summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/qt/ContextShadow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/qt/ContextShadow.cpp')
-rw-r--r--WebCore/platform/graphics/qt/ContextShadow.cpp256
1 files changed, 256 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/qt/ContextShadow.cpp b/WebCore/platform/graphics/qt/ContextShadow.cpp
new file mode 100644
index 0000000..0511218
--- /dev/null
+++ b/WebCore/platform/graphics/qt/ContextShadow.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2010 Sencha, Inc.
+ *
+ * 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 COMPUTER, 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 "ContextShadow.h"
+
+namespace WebCore {
+
+ContextShadow::ContextShadow()
+ : type(NoShadow)
+ , blurRadius(0)
+{
+}
+
+ContextShadow::ContextShadow(const QColor& c, float r, qreal dx, qreal dy)
+ : color(c)
+ , blurRadius(qRound(r))
+ , offset(dx, dy)
+{
+ // The type of shadow is decided by the blur radius, shadow offset, and shadow color.
+ if (!color.isValid() || !color.alpha()) {
+ // Can't paint the shadow with invalid or invisible color.
+ type = NoShadow;
+ } else if (r > 0) {
+ // Shadow is always blurred, even the offset is zero.
+ type = BlurShadow;
+ } else if (offset.isNull()) {
+ // Without blur and zero offset means the shadow is fully hidden.
+ type = NoShadow;
+ } else {
+ if (color.alpha() > 0)
+ type = AlphaSolidShadow;
+ else
+ type = OpaqueSolidShadow;
+ }
+}
+
+void ContextShadow::clear()
+{
+ type = NoShadow;
+ color = QColor();
+ blurRadius = 0;
+ offset = QPointF(0, 0);
+}
+
+// Instead of integer division, we use 18.14 for fixed-point division.
+static const int BlurSumShift = 14;
+
+// Note: image must be RGB32 format
+static void blurHorizontal(QImage& image, int radius, bool swap = false)
+{
+ Q_ASSERT(image.format() == QImage::Format_ARGB32_Premultiplied);
+
+ // See comments in http://webkit.org/b/40793, it seems sensible
+ // to follow Skia's limit of 128 pixels of blur radius
+ radius = qMin(128, radius);
+
+ int imgWidth = image.width();
+ int imgHeight = image.height();
+
+ // Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlur
+ // for the approaches when the box-blur radius is even vs odd.
+ int dmax = radius >> 1;
+ int dmin = qMax(0, dmax - 1 + (radius & 1));
+
+ for (int y = 0; y < imgHeight; ++y) {
+
+ unsigned char* pixels = image.scanLine(y);
+
+ int left;
+ int right;
+ int pixelCount;
+ int prev;
+ int next;
+ int firstAlpha;
+ int lastAlpha;
+ int totalAlpha;
+ unsigned char* target;
+ unsigned char* prevPtr;
+ unsigned char* nextPtr;
+
+ int invCount;
+
+ static const int alphaChannel = 3;
+ static const int blueChannel = 0;
+ static const int greenChannel = 1;
+
+ // For each step, we use sliding window algorithm. This is much more
+ // efficient than computing the sum of each pixels covered by the box
+ // kernel size for each x.
+
+ // As noted in the SVG filter specification, running box blur 3x
+ // approximates a real gaussian blur nicely.
+
+ // Step 1: blur alpha channel and store the result in the blue channel.
+ left = swap ? dmax : dmin;
+ right = swap ? dmin : dmax;
+ pixelCount = left + 1 + right;
+ invCount = (1 << BlurSumShift) / pixelCount;
+ prev = -left;
+ next = 1 + right;
+ firstAlpha = pixels[alphaChannel];
+ lastAlpha = pixels[(imgWidth - 1) * 4 + alphaChannel];
+ totalAlpha = 0;
+ for (int i = 0; i < pixelCount; ++i)
+ totalAlpha += pixels[qBound(0, i - left, imgWidth - 1) * 4 + alphaChannel];
+ target = pixels + blueChannel;
+ prevPtr = pixels + prev * 4 + alphaChannel;
+ nextPtr = pixels + next * 4 + alphaChannel;
+ for (int x = 0; x < imgWidth; ++x, ++prev, ++next, target += 4, prevPtr += 4, nextPtr += 4) {
+ *target = (totalAlpha * invCount) >> BlurSumShift;
+ int delta = ((next < imgWidth) ? *nextPtr : lastAlpha) -
+ ((prev > 0) ? *prevPtr : firstAlpha);
+ totalAlpha += delta;
+ }
+
+ // Step 2: blur blue channel and store the result in the green channel.
+ left = swap ? dmin : dmax;
+ right = swap ? dmax : dmin;
+ pixelCount = left + 1 + right;
+ invCount = (1 << BlurSumShift) / pixelCount;
+ prev = -left;
+ next = 1 + right;
+ firstAlpha = pixels[blueChannel];
+ lastAlpha = pixels[(imgWidth - 1) * 4 + blueChannel];
+ totalAlpha = 0;
+ for (int i = 0; i < pixelCount; ++i)
+ totalAlpha += pixels[qBound(0, i - left, imgWidth - 1) * 4 + blueChannel];
+ target = pixels + greenChannel;
+ prevPtr = pixels + prev * 4 + blueChannel;
+ nextPtr = pixels + next * 4 + blueChannel;
+ for (int x = 0; x < imgWidth; ++x, ++prev, ++next, target += 4, prevPtr += 4, nextPtr += 4) {
+ *target = (totalAlpha * invCount) >> BlurSumShift;
+ int delta = ((next < imgWidth) ? *nextPtr : lastAlpha) -
+ ((prev > 0) ? *prevPtr : firstAlpha);
+ totalAlpha += delta;
+ }
+
+ // Step 3: blur green channel and store the result in the alpha channel.
+ left = dmax;
+ right = dmax;
+ pixelCount = left + 1 + right;
+ invCount = (1 << BlurSumShift) / pixelCount;
+ prev = -left;
+ next = 1 + right;
+ firstAlpha = pixels[greenChannel];
+ lastAlpha = pixels[(imgWidth - 1) * 4 + greenChannel];
+ totalAlpha = 0;
+ for (int i = 0; i < pixelCount; ++i)
+ totalAlpha += pixels[qBound(0, i - left, imgWidth - 1) * 4 + greenChannel];
+ target = pixels + alphaChannel;
+ prevPtr = pixels + prev * 4 + greenChannel;
+ nextPtr = pixels + next * 4 + greenChannel;
+ for (int x = 0; x < imgWidth; ++x, ++prev, ++next, target += 4, prevPtr += 4, nextPtr += 4) {
+ *target = (totalAlpha * invCount) >> BlurSumShift;
+ int delta = ((next < imgWidth) ? *nextPtr : lastAlpha) -
+ ((prev > 0) ? *prevPtr : firstAlpha);
+ totalAlpha += delta;
+ }
+ }
+}
+
+static void shadowBlur(QImage& image, int radius, const QColor& shadowColor)
+{
+ blurHorizontal(image, radius);
+
+ QTransform transform;
+ transform.rotate(90);
+ image = image.transformed(transform);
+ blurHorizontal(image, radius, true);
+ transform.reset();
+ transform.rotate(270);
+ image = image.transformed(transform);
+
+ // "Colorize" with the right shadow color.
+ QPainter p(&image);
+ p.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ p.fillRect(image.rect(), shadowColor.rgb());
+ p.end();
+}
+
+void ContextShadow::drawShadowRect(QPainter* p, const QRectF& rect)
+{
+ if (type == NoShadow)
+ return;
+
+ if (type == BlurShadow) {
+ QRectF shadowRect = rect.translated(offset);
+
+ // We expand the area by the blur radius * 2 to give extra space
+ // for the blur transition.
+ int extra = blurRadius * 2;
+ QRectF bufferRect = shadowRect.adjusted(-extra, -extra, extra, extra);
+ QRect alignedBufferRect = bufferRect.toAlignedRect();
+
+ QRect clipRect;
+ if (p->hasClipping())
+ clipRect = p->clipRegion().boundingRect();
+ else
+ clipRect = p->transform().inverted().mapRect(p->window());
+
+ if (!clipRect.contains(alignedBufferRect)) {
+
+ // No need to have the buffer larger that the clip.
+ alignedBufferRect = alignedBufferRect.intersected(clipRect);
+ if (alignedBufferRect.isEmpty())
+ return;
+
+ // We adjust again because the pixels at the borders are still
+ // potentially affected by the pixels outside the buffer.
+ alignedBufferRect.adjust(-extra, -extra, extra, extra);
+ }
+
+ QImage shadowImage(alignedBufferRect.size(), QImage::Format_ARGB32_Premultiplied);
+ shadowImage.fill(Qt::transparent);
+ QPainter shadowPainter(&shadowImage);
+
+ shadowPainter.fillRect(shadowRect.translated(-alignedBufferRect.topLeft()), color);
+ shadowPainter.end();
+
+ shadowBlur(shadowImage, blurRadius, color);
+
+ p->drawImage(alignedBufferRect.topLeft(), shadowImage);
+
+ return;
+ }
+
+ p->fillRect(rect.translated(offset), color);
+}
+
+
+}