[SCM] WebKit Debian packaging branch, webkit-1.3, updated. upstream/1.3.7-4207-g178b198
simon.fraser at apple.com
simon.fraser at apple.com
Mon Feb 21 00:24:15 UTC 2011
The following commit has been merged in the webkit-1.3 branch:
commit 6651a5174f115d158ebe1a5b6ee4d05aa4197002
Author: simon.fraser at apple.com <simon.fraser at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date: Mon Jan 31 00:44:18 2011 +0000
2011-01-30 Simon Fraser <simon.fraser at apple.com>
Reviewed by Sam Weinig.
Make ContextShadow code cross-platform
https://bugs.webkit.org/show_bug.cgi?id=51312
Add a new class, ShadowBlur, that contains most of the
code from ContextShadow, but is fully cross-platform.
It depends on one new method, GraphicsContext::clipBounds(),
which platforms will have to implement.
Add ShadowBlur to the Mac Xcode project, but don't use it
anywhere yet.
* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/GraphicsContext.cpp:
(WebCore::GraphicsContext::clipBounds):
* platform/graphics/GraphicsContext.h:
* platform/graphics/ShadowBlur.cpp: Added.
(WebCore::roundUpToMultipleOf32):
(WebCore::ScratchBuffer::ScratchBuffer):
(WebCore::ScratchBuffer::getScratchBuffer):
(WebCore::ScratchBuffer::scheduleScratchBufferPurge):
(WebCore::ScratchBuffer::timerFired):
(WebCore::ScratchBuffer::clearScratchBuffer):
(WebCore::ScratchBuffer::shared):
(WebCore::ShadowBlur::ShadowBlur):
(WebCore::ShadowBlur::blurLayerImage):
(WebCore::ShadowBlur::adjustBlurDistance):
(WebCore::ShadowBlur::calculateLayerBoundingRect):
(WebCore::ShadowBlur::beginShadowLayer):
(WebCore::ShadowBlur::endShadowLayer):
(WebCore::ShadowBlur::drawRectShadow):
(WebCore::ShadowBlur::drawRectShadowWithoutTiling):
(WebCore::ShadowBlur::drawRectShadowWithTiling):
(WebCore::ShadowBlur::clipBounds):
* platform/graphics/ShadowBlur.h: Added.
(WebCore::ShadowBlur::setShadowsIgnoreTransforms):
(WebCore::ShadowBlur::shadowsIgnoreTransforms):
* platform/graphics/cg/GraphicsContextCG.cpp:
(WebCore::GraphicsContext::clipBounds):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@77097 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 77fc2a0..42af1f3 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,46 @@
+2011-01-30 Simon Fraser <simon.fraser at apple.com>
+
+ Reviewed by Sam Weinig.
+
+ Make ContextShadow code cross-platform
+ https://bugs.webkit.org/show_bug.cgi?id=51312
+
+ Add a new class, ShadowBlur, that contains most of the
+ code from ContextShadow, but is fully cross-platform.
+ It depends on one new method, GraphicsContext::clipBounds(),
+ which platforms will have to implement.
+
+ Add ShadowBlur to the Mac Xcode project, but don't use it
+ anywhere yet.
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * platform/graphics/GraphicsContext.cpp:
+ (WebCore::GraphicsContext::clipBounds):
+ * platform/graphics/GraphicsContext.h:
+ * platform/graphics/ShadowBlur.cpp: Added.
+ (WebCore::roundUpToMultipleOf32):
+ (WebCore::ScratchBuffer::ScratchBuffer):
+ (WebCore::ScratchBuffer::getScratchBuffer):
+ (WebCore::ScratchBuffer::scheduleScratchBufferPurge):
+ (WebCore::ScratchBuffer::timerFired):
+ (WebCore::ScratchBuffer::clearScratchBuffer):
+ (WebCore::ScratchBuffer::shared):
+ (WebCore::ShadowBlur::ShadowBlur):
+ (WebCore::ShadowBlur::blurLayerImage):
+ (WebCore::ShadowBlur::adjustBlurDistance):
+ (WebCore::ShadowBlur::calculateLayerBoundingRect):
+ (WebCore::ShadowBlur::beginShadowLayer):
+ (WebCore::ShadowBlur::endShadowLayer):
+ (WebCore::ShadowBlur::drawRectShadow):
+ (WebCore::ShadowBlur::drawRectShadowWithoutTiling):
+ (WebCore::ShadowBlur::drawRectShadowWithTiling):
+ (WebCore::ShadowBlur::clipBounds):
+ * platform/graphics/ShadowBlur.h: Added.
+ (WebCore::ShadowBlur::setShadowsIgnoreTransforms):
+ (WebCore::ShadowBlur::shadowsIgnoreTransforms):
+ * platform/graphics/cg/GraphicsContextCG.cpp:
+ (WebCore::GraphicsContext::clipBounds):
+
2011-01-29 Simon Fraser <simon.fraser at apple.com>
Reviewed by Dan Bernstein.
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 2df193a..98aa4c7 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -254,6 +254,8 @@
0C45342810CDBBFA00869157 /* JSWebGLUniformLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C45342610CDBBFA00869157 /* JSWebGLUniformLocation.h */; };
0F11A54F0F39233100C37884 /* RenderSelectionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F11A54E0F39233100C37884 /* RenderSelectionInfo.h */; };
0F15DA8A0F3AAEE70000CE47 /* AnimationControllerPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F15DA890F3AAEE70000CE47 /* AnimationControllerPrivate.h */; };
+ 0F3DD44F12F5EA1B000D9190 /* ShadowBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */; };
+ 0F3DD45012F5EA1B000D9190 /* ShadowBlur.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */; };
0F500AAF0F54DB1B00EEF928 /* TransformState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F500AAE0F54DB1B00EEF928 /* TransformState.h */; };
0F500AB10F54DB3100EEF928 /* TransformState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F500AB00F54DB3100EEF928 /* TransformState.cpp */; };
0F56028F0E4B76580065B038 /* RenderMarquee.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F56028D0E4B76580065B038 /* RenderMarquee.h */; };
@@ -6568,6 +6570,8 @@
0C45342610CDBBFA00869157 /* JSWebGLUniformLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWebGLUniformLocation.h; sourceTree = "<group>"; };
0F11A54E0F39233100C37884 /* RenderSelectionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderSelectionInfo.h; sourceTree = "<group>"; };
0F15DA890F3AAEE70000CE47 /* AnimationControllerPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationControllerPrivate.h; path = animation/AnimationControllerPrivate.h; sourceTree = "<group>"; };
+ 0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowBlur.cpp; sourceTree = "<group>"; };
+ 0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowBlur.h; sourceTree = "<group>"; };
0F500AAE0F54DB1B00EEF928 /* TransformState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformState.h; sourceTree = "<group>"; };
0F500AB00F54DB3100EEF928 /* TransformState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransformState.cpp; sourceTree = "<group>"; };
0F56028D0E4B76580065B038 /* RenderMarquee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMarquee.h; sourceTree = "<group>"; };
@@ -17537,6 +17541,8 @@
A73F95FD12C97BFE0031AAF9 /* RoundedIntRect.h */,
371F4FFB0D25E7F300ECE0D5 /* SegmentedFontData.cpp */,
371F4FFA0D25E7F300ECE0D5 /* SegmentedFontData.h */,
+ 0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */,
+ 0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */,
B2C3DA530D006CD600EF6F26 /* SimpleFontData.cpp */,
B2C3DA540D006CD600EF6F26 /* SimpleFontData.h */,
B23540F00D00782E002382FA /* StringTruncator.cpp */,
@@ -22451,6 +22457,7 @@
E134F5AB12EE343F004EC58D /* IntRectHash.h in Headers */,
977E2DCE12F0E28300C13379 /* HTMLSourceTracker.h in Headers */,
977E2E0F12F0FC9C00C13379 /* XSSFilter.h in Headers */,
+ 0F3DD45012F5EA1B000D9190 /* ShadowBlur.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -25148,6 +25155,7 @@
4F2D205512EAE7B3005C2874 /* InspectorAgent.cpp in Sources */,
977E2DCD12F0E28300C13379 /* HTMLSourceTracker.cpp in Sources */,
977E2E0E12F0FC9C00C13379 /* XSSFilter.cpp in Sources */,
+ 0F3DD44F12F5EA1B000D9190 /* ShadowBlur.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/Source/WebCore/platform/graphics/ContextShadow.h b/Source/WebCore/platform/graphics/ContextShadow.h
index a1fba5c..c0571f0 100644
--- a/Source/WebCore/platform/graphics/ContextShadow.h
+++ b/Source/WebCore/platform/graphics/ContextShadow.h
@@ -68,6 +68,8 @@ typedef void* PlatformContext;
// This class should be copyable since GraphicsContextQt keeps a stack of
// the shadow state for savePlatformState and restorePlatformState.
+// This class is Deprecated. Platforms should migrate to ShadowBlur.
+
class ContextShadow {
public:
enum {
diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp
index e05a578..5b00e25 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp
@@ -565,6 +565,14 @@ void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& re
buffer->clip(this, rect);
}
+#if !PLATFORM(CG)
+IntRect GraphicsContext::clipBounds() const
+{
+ ASSERT_NOT_REACHED();
+ return IntRect();
+}
+#endif
+
TextDrawingModeFlags GraphicsContext::textDrawingMode() const
{
return m_state.textDrawingMode;
diff --git a/Source/WebCore/platform/graphics/GraphicsContext.h b/Source/WebCore/platform/graphics/GraphicsContext.h
index 6c9a2f6..c018d67 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext.h
+++ b/Source/WebCore/platform/graphics/GraphicsContext.h
@@ -320,6 +320,8 @@ namespace WebCore {
void clipPath(const Path&, WindRule);
void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true);
void clipToImageBuffer(ImageBuffer*, const FloatRect&);
+
+ IntRect clipBounds() const;
TextDrawingModeFlags textDrawingMode() const;
void setTextDrawingMode(TextDrawingModeFlags);
@@ -373,7 +375,7 @@ namespace WebCore {
// This clip function is used only by <canvas> code. It allows
// implementations to handle clipping on the canvas differently since
- // the disipline is different.
+ // the discipline is different.
void canvasClip(const Path&);
void clipOut(const Path&);
diff --git a/Source/WebCore/platform/graphics/ShadowBlur.cpp b/Source/WebCore/platform/graphics/ShadowBlur.cpp
new file mode 100644
index 0000000..238d774
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ShadowBlur.cpp
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2011 Apple Inc.
+ * Copyright (C) 2010 Sencha, Inc.
+ * Copyright (C) 2010 Igalia S.L.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * 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.
+ */
+
+#include "config.h"
+#include "ShadowBlur.h"
+
+#include "AffineTransform.h"
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "Timer.h"
+#include <wtf/MathExtras.h>
+#include <wtf/Noncopyable.h>
+
+using namespace std;
+
+namespace WebCore {
+
+static inline int roundUpToMultipleOf32(int d)
+{
+ return (1 + (d >> 5)) << 5;
+}
+
+// ShadowBlur needs a scratch image as the buffer for the blur filter.
+// Instead of creating and destroying the buffer for every operation,
+// we create a buffer which will be automatically purged via a timer.
+class ScratchBuffer {
+public:
+ ScratchBuffer()
+ : m_purgeTimer(this, &ScratchBuffer::timerFired)
+ {
+ }
+
+ ImageBuffer* getScratchBuffer(const IntSize& size)
+ {
+ // We do not need to recreate the buffer if the current buffer is large enough.
+ if (m_imageBuffer && m_imageBuffer->width() >= size.width() && m_imageBuffer->height() >= size.height())
+ return m_imageBuffer.get();
+
+ // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
+ IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height()));
+
+ m_imageBuffer = ImageBuffer::create(roundedSize);
+ return m_imageBuffer.get();
+ }
+
+ void scheduleScratchBufferPurge()
+ {
+ if (m_purgeTimer.isActive())
+ m_purgeTimer.stop();
+
+ const double scratchBufferPurgeInterval = 2;
+ m_purgeTimer.startOneShot(scratchBufferPurgeInterval);
+ }
+
+ static ScratchBuffer& shared();
+
+private:
+ void timerFired(Timer<ScratchBuffer>*)
+ {
+ clearScratchBuffer();
+ }
+
+ void clearScratchBuffer()
+ {
+ m_imageBuffer = 0;
+ }
+
+ OwnPtr<ImageBuffer> m_imageBuffer;
+ Timer<ScratchBuffer> m_purgeTimer;
+};
+
+ScratchBuffer& ScratchBuffer::shared()
+{
+ DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ());
+ return scratchBuffer;
+}
+
+ShadowBlur::ShadowBlur(float radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace)
+ : m_color(color)
+ , m_colorSpace(colorSpace)
+ , m_blurRadius(radius)
+ , m_offset(offset)
+ , m_shadowsIgnoreTransforms(false)
+{
+ // Limit blur radius to 128 to avoid lots of very expensive blurring.
+ m_blurRadius = min<float>(m_blurRadius, 128);
+
+ // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
+ if (!m_color.isValid() || !color.alpha()) {
+ // Can't paint the shadow with invalid or invisible color.
+ m_type = NoShadow;
+ } else if (radius > 0) {
+ // Shadow is always blurred, even the offset is zero.
+ m_type = BlurShadow;
+ } else if (!m_offset.width() && !m_offset.height()) {
+ // Without blur and zero offset means the shadow is fully hidden.
+ m_type = NoShadow;
+ } else
+ m_type = SolidShadow;
+}
+
+// Instead of integer division, we use 17.15 for fixed-point division.
+static const int blurSumShift = 15;
+static const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
+
+// Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlur.
+// As noted in the SVG filter specification, running box blur 3x
+// approximates a real gaussian blur nicely.
+
+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
+
+ int diameter;
+ if (m_shadowsIgnoreTransforms)
+ diameter = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up.
+ else
+ diameter = max(2, static_cast<int>(floorf(m_blurRadius / 2 * gaussianKernelFactor + 0.5f))); // CSS
+
+ int dMax = diameter >> 1;
+ int dMin = dMax - 1 + (diameter & 1);
+ if (dMin < 0)
+ dMin = 0;
+
+ // First pass is horizontal.
+ int stride = 4;
+ int delta = rowStride;
+ int final = size.height();
+ int dim = size.width();
+
+ // Two stages: horizontal and vertical
+ for (int pass = 0; pass < 2; ++pass) {
+ unsigned char* pixels = imageData;
+
+ for (int j = 0; j < final; ++j, pixels += delta) {
+ // For each step, we blur the alpha in a channel and store the result
+ // in another channel for the subsequent step.
+ // We use sliding window algorithm to accumulate the alpha values.
+ // 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 = (!step) ? dMin : dMax;
+ int side2 = (step == 1) ? dMin : dMax;
+ int pixelCount = side1 + 1 + side2;
+ int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount;
+ int ofs = 1 + side2;
+ int alpha1 = pixels[channels[step]];
+ int alpha2 = pixels[(dim - 1) * stride + channels[step]];
+
+ unsigned char* ptr = pixels + channels[step + 1];
+ unsigned char* prev = pixels + stride + channels[step];
+ unsigned char* next = pixels + ofs * stride + channels[step];
+
+ int i;
+ int sum = side1 * alpha1 + alpha1;
+ int limit = (dim < side2 + 1) ? dim : side2 + 1;
+
+ for (i = 1; i < limit; ++i, prev += stride)
+ sum += *prev;
+
+ if (limit <= side2)
+ sum += (side2 - limit + 1) * alpha2;
+
+ limit = (side1 < dim) ? side1 : dim;
+ for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) {
+ *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);
+ }
+ }
+ }
+
+ // Last pass is vertical.
+ stride = rowStride;
+ delta = 4;
+ final = size.width();
+ dim = size.height();
+ }
+}
+
+void ShadowBlur::adjustBlurDistance(GraphicsContext* context)
+{
+ if (!m_shadowsIgnoreTransforms)
+ return;
+
+ const AffineTransform transform = context->getCTM();
+
+ // Adjust blur if we're scaling, since the radius must not be affected by transformations.
+ // FIXME: use AffineTransform::isIdentityOrTranslationOrFlipped()?
+ if (transform.isIdentity())
+ return;
+
+ // Calculate transformed unit vectors.
+ const FloatQuad unitQuad(FloatPoint(0, 0), FloatPoint(1, 0),
+ FloatPoint(0, 1), FloatPoint(1, 1));
+ const FloatQuad transformedUnitQuad = transform.mapQuad(unitQuad);
+
+ // Calculate X axis scale factor.
+ const FloatSize xUnitChange = transformedUnitQuad.p2() - transformedUnitQuad.p1();
+ const float xAxisScale = sqrtf(xUnitChange.width() * xUnitChange.width()
+ + xUnitChange.height() * xUnitChange.height());
+
+ // Calculate Y axis scale factor.
+ const FloatSize yUnitChange = transformedUnitQuad.p3() - transformedUnitQuad.p1();
+ const float yAxisScale = sqrtf(yUnitChange.width() * yUnitChange.width()
+ + yUnitChange.height() * yUnitChange.height());
+
+ // blurLayerImage() does not support per-axis blurring, so calculate a balanced scaling.
+ // FIXME: does AffineTransform.xScale()/yScale() help?
+ const float scale = sqrtf(xAxisScale * yAxisScale);
+ m_blurRadius = roundf(m_blurRadius / scale);
+}
+
+IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect)
+{
+ // Calculate the destination of the blurred and/or transformed layer.
+ FloatRect layerFloatRect;
+ float inflation = 0;
+
+ const AffineTransform transform = context->getCTM();
+ if (m_shadowsIgnoreTransforms && !transform.isIdentity()) {
+ FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect));
+ transformedPolygon.move(m_offset);
+ layerFloatRect = transform.inverse().mapQuad(transformedPolygon).boundingBox();
+ } else {
+ layerFloatRect = shadowedRect;
+ layerFloatRect.move(m_offset);
+ }
+
+ // We expand the area by the blur radius to give extra space for the blur transition.
+ if (m_type == BlurShadow) {
+ layerFloatRect.inflate(m_blurRadius);
+ inflation += m_blurRadius;
+ }
+
+ FloatRect unclippedLayerRect = layerFloatRect;
+
+ if (!clipRect.contains(enclosingIntRect(layerFloatRect))) {
+ // If we are totally outside the clip region, we aren't painting at all.
+ if (intersection(layerFloatRect, clipRect).isEmpty())
+ return IntRect();
+
+ IntRect inflatedClip = clipRect;
+ // Pixels at the edges can be affected by pixels outside the buffer,
+ // so intersect with the clip inflated by the blur.
+ if (m_type == BlurShadow)
+ inflatedClip.inflate(m_blurRadius);
+
+ layerFloatRect.intersect(inflatedClip);
+ }
+
+ const int frameSize = inflation * 2;
+ m_sourceRect = IntRect(0, 0, shadowedRect.width() + frameSize, shadowedRect.height() + frameSize);
+ m_layerOrigin = FloatPoint(layerFloatRect.x(), layerFloatRect.y());
+ m_layerSize = layerFloatRect.size();
+
+ const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y());
+ const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin;
+
+ // Set the origin as the top left corner of the scratch image, or, in case there's a clipped
+ // out region, set the origin accordingly to the full bounding rect's top-left corner.
+ float translationX = -shadowedRect.x() + inflation - fabsf(clippedOut.width());
+ float translationY = -shadowedRect.y() + inflation - fabsf(clippedOut.height());
+ m_layerContextTranslation = FloatSize(translationX, translationY);
+
+ return enclosingIntRect(layerFloatRect);
+}
+
+GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext* graphicsContext, const FloatRect& shadowedRect)
+{
+ adjustBlurDistance(graphicsContext);
+
+ IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
+ // 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)
+{
+ 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();
+
+ graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, m_layerImage->size()));
+ graphicsContext->setFillColor(m_color, m_colorSpace);
+
+ graphicsContext->clearShadow();
+ 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();
+}
+
+void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii)
+{
+ // drawShadowedRect 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, 1);
+ return;
+ }
+
+ int templateSideLength = 1;
+ float twiceRadius = m_blurRadius * 2;
+
+ // 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);
+
+ if (shadowTemplateSize.width() > shadowedRect.width() || shadowTemplateSize.height() > shadowedRect.height()) {
+ drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, 1);
+ return;
+ }
+
+ // Determine dimensions of shadow rect.
+ FloatRect shadowRect = shadowedRect;
+ shadowRect.inflate(m_blurRadius); // FIXME: duplicating code with calculateLayerBoundingRect.
+
+ // Reduce the size of what we have to draw with the clip area.
+ calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds());
+
+ // If the template area ends up being larger than the area to be blurred, use the simple case.
+ // FIXME: when does this happen?
+ if ((shadowTemplateSize.width() * shadowTemplateSize.height() > m_sourceRect.width() * m_sourceRect.height())) {
+ drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, 1);
+ return;
+ }
+
+ drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, 1, shadowTemplateSize);
+}
+
+void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, float alpha)
+{
+ GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, shadowedRect);
+ if (!shadowContext)
+ return;
+
+ Path path;
+ path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+
+ shadowContext->setFillColor(Color(.0f, .0f, .0f, alpha), ColorSpaceDeviceRGB);
+ shadowContext->fillPath(path);
+
+ endShadowLayer(graphicsContext);
+}
+
+/*
+ This function uses tiling to improve the performance of the shadow
+ drawing of rounded rectangles. The code basically does the following
+ steps:
+
+ 1. Calculate the size of the shadow template, a rectangle that
+ contains all the necessary tiles to draw the complete shadow.
+
+ 2. If that size is smaller than the real rectangle render the new
+ template rectangle and its shadow in a new surface, in other case
+ render the shadow of the real rectangle in the destination
+ surface.
+
+ 3. Calculate the sizes and positions of the tiles and their
+ destinations and use drawPattern to render the final shadow. The
+ code divides the rendering in 8 tiles:
+
+ 1 | 2 | 3
+ -----------
+ 4 | | 5
+ -----------
+ 6 | 7 | 8
+
+ 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.
+ */
+
+void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, float alpha, const IntSize& shadowTemplateSize)
+{
+ float twiceRadius = m_blurRadius * 2;
+
+ // Size of the tiling side.
+ int templateSideLength = 1;
+
+ FloatRect shadowRect = shadowedRect;
+ shadowRect.inflate(m_blurRadius); // FIXME: duplicating code with calculateLayerBoundingRect.
+
+ shadowRect.move(m_offset.width(), m_offset.height());
+
+ m_layerImage = ScratchBuffer::shared().getScratchBuffer(shadowTemplateSize);
+
+ // Draw shadow into a new ImageBuffer.
+ GraphicsContext* shadowContext = m_layerImage->context();
+ shadowContext->save();
+
+ shadowContext->clearRect(FloatRect(0, 0, shadowTemplateSize.width(), shadowTemplateSize.height()));
+
+ // Draw the rectangle.
+ IntRect templateRect = IntRect(m_blurRadius, m_blurRadius, shadowTemplateSize.width() - twiceRadius, shadowTemplateSize.height() - twiceRadius);
+ Path path;
+ path.addRoundedRect(templateRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+
+ shadowContext->setFillColor(Color(.0f, .0f, .0f, alpha), 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()));
+
+ shadowContext->restore();
+
+ // Fill the internal part of the shadow.
+ shadowRect.inflate(-twiceRadius);
+ if (!shadowRect.isEmpty()) {
+ graphicsContext->save();
+
+ path.clear();
+ path.addRoundedRect(shadowRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
+
+ graphicsContext->setFillColor(m_color, m_colorSpace);
+ graphicsContext->fillPath(path);
+
+ graphicsContext->restore();
+ }
+ shadowRect.inflate(twiceRadius);
+
+ // 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.
+
+ // Draw top side.
+ FloatRect tileRect = FloatRect(twiceRadius + radii.topLeft().width(), 0, templateSideLength, twiceRadius);
+ FloatRect destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y());
+ destRect.setWidth(shadowRect.width() - radii.topLeft().width() - radii.topRight().width() - m_blurRadius * 4);
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the bottom side.
+ tileRect = FloatRect(twiceRadius + radii.bottomLeft().width(), shadowTemplateSize.height() - twiceRadius, templateSideLength, twiceRadius);
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y() + twiceRadius + shadowedRect.height() - shadowTemplateSize.height());
+ destRect.setWidth(shadowRect.width() - radii.bottomLeft().width() - radii.bottomRight().width() - m_blurRadius * 4);
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the right side.
+ tileRect = FloatRect(shadowTemplateSize.width() - twiceRadius, twiceRadius + radii.topRight().height(), twiceRadius, templateSideLength);
+ destRect = tileRect;
+ destRect.move(shadowRect.x() + twiceRadius + shadowedRect.width() - shadowTemplateSize.width(), shadowRect.y());
+ destRect.setHeight(shadowRect.height() - radii.topRight().height() - radii.bottomRight().height() - m_blurRadius * 4);
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the left side.
+ tileRect = FloatRect(0, twiceRadius + radii.topLeft().height(), twiceRadius, templateSideLength);
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y());
+ destRect.setHeight(shadowRect.height() - radii.topLeft().height() - radii.bottomLeft().height() - m_blurRadius * 4);
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the top left corner.
+ tileRect = FloatRect(0, 0, twiceRadius + radii.topLeft().width(), twiceRadius + radii.topLeft().height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y());
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the top right corner.
+ tileRect = FloatRect(shadowTemplateSize.width() - twiceRadius - radii.topRight().width(), 0, twiceRadius + radii.topRight().width(),
+ twiceRadius + radii.topRight().height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x() + shadowedRect.width() - shadowTemplateSize.width() + twiceRadius, shadowRect.y());
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the bottom right corner.
+ tileRect = FloatRect(shadowTemplateSize.width() - twiceRadius - radii.bottomRight().width(),
+ shadowTemplateSize.height() - twiceRadius - radii.bottomRight().height(),
+ twiceRadius + radii.bottomRight().width(), twiceRadius + radii.bottomRight().height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x() + shadowedRect.width() - shadowTemplateSize.width() + twiceRadius,
+ shadowRect.y() + shadowedRect.height() - shadowTemplateSize.height() + twiceRadius);
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ // Draw the bottom left corner.
+ tileRect = FloatRect(0, shadowTemplateSize.height() - twiceRadius - radii.bottomLeft().height(),
+ twiceRadius + radii.bottomLeft().width(), twiceRadius + radii.bottomLeft().height());
+ destRect = tileRect;
+ destRect.move(shadowRect.x(), shadowRect.y() + shadowedRect.height() - shadowTemplateSize.height() + twiceRadius);
+ graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect);
+
+ m_layerImage = 0;
+ // Schedule a purge of the scratch buffer.
+ ScratchBuffer::shared().scheduleScratchBufferPurge();
+}
+
+#if !PLATFORM(CG)
+IntRect ShadowBlur::clipBounds(GraphicsContext*)
+{
+ // FIXME: add clipBounds() to GraphicsContext.
+ ASSERT_NOT_REACHED();
+ return IntRect();
+}
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/graphics/ShadowBlur.h b/Source/WebCore/platform/graphics/ShadowBlur.h
new file mode 100644
index 0000000..a93eb4d
--- /dev/null
+++ b/Source/WebCore/platform/graphics/ShadowBlur.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 Apple Inc.
+ * Copyright (C) 2010 Sencha, Inc.
+ * Copyright (C) 2010 Igalia S.L.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * 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.
+ */
+
+#ifndef ShadowBlur_h
+#define ShadowBlur_h
+
+#include "Color.h"
+#include "FloatRect.h"
+#include "RoundedIntRect.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class AffineTransform;
+class GraphicsContext;
+class ImageBuffer;
+
+class ShadowBlur {
+ WTF_MAKE_NONCOPYABLE(ShadowBlur);
+public:
+ ShadowBlur(float radius, const FloatSize& offset, const Color&, ColorSpace);
+
+ void setShadowsIgnoreTransforms(bool ignoreTransforms) { m_shadowsIgnoreTransforms = ignoreTransforms; }
+ bool shadowsIgnoreTransforms() const { return m_shadowsIgnoreTransforms; }
+
+ void drawRectShadow(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&);
+
+private:
+ GraphicsContext* beginShadowLayer(GraphicsContext*, const FloatRect& layerArea);
+ void endShadowLayer(GraphicsContext*);
+
+ void adjustBlurDistance(GraphicsContext*);
+ void blurLayerImage(unsigned char*, const IntSize&, int stride);
+ IntRect calculateLayerBoundingRect(GraphicsContext*, const FloatRect& layerArea, const IntRect& clipRect);
+
+ void drawRectShadowWithoutTiling(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&, float alpha);
+ void drawRectShadowWithTiling(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&, float alpha, const IntSize& shadowTemplateSize);
+
+ enum ShadowType {
+ NoShadow,
+ SolidShadow,
+ BlurShadow
+ };
+
+ ShadowType m_type;
+
+ Color m_color;
+ ColorSpace m_colorSpace;
+ float m_blurRadius;
+ FloatSize m_offset;
+
+ ImageBuffer* m_layerImage; // Buffer to where the temporary shadow will be drawn to.
+
+ FloatRect m_sourceRect; // Sub-rect of m_layerImage that contains the shadow pixels.
+ 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.
+
+ bool m_shadowsIgnoreTransforms;
+};
+
+} // namespace WebCore
+
+#endif // ShadowBlur_h
diff --git a/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
index 6142569..60356b6 100644
--- a/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
+++ b/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
@@ -727,6 +727,11 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
CGContextClip(context);
}
+IntRect GraphicsContext::clipBounds() const
+{
+ return enclosingIntRect(CGContextGetClipBoundingBox(platformContext()));
+}
+
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
{
if (paintingDisabled())
--
WebKit Debian packaging
More information about the Pkg-webkit-commits
mailing list