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.cpp162
1 files changed, 123 insertions, 39 deletions
diff --git a/WebCore/platform/graphics/qt/ContextShadow.cpp b/WebCore/platform/graphics/qt/ContextShadow.cpp
index 0511218..829ca82 100644
--- a/WebCore/platform/graphics/qt/ContextShadow.cpp
+++ b/WebCore/platform/graphics/qt/ContextShadow.cpp
@@ -28,8 +28,80 @@
#include "config.h"
#include "ContextShadow.h"
+#include <QTimerEvent>
+#include <wtf/Noncopyable.h>
+
namespace WebCore {
+// ContextShadow 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 ShadowBuffer: public QObject {
+public:
+ ShadowBuffer(QObject* parent = 0);
+
+ QImage* scratchImage(const QSize& size);
+
+ void schedulePurge();
+
+protected:
+ void timerEvent(QTimerEvent* event);
+
+private:
+ QImage image;
+ int timerId;
+};
+
+ShadowBuffer::ShadowBuffer(QObject* parent)
+ : QObject(parent)
+ , timerId(0)
+{
+}
+
+QImage* ShadowBuffer::scratchImage(const QSize& size)
+{
+ int width = size.width();
+ int height = size.height();
+
+ // We do not need to recreate the buffer if the buffer is reasonably
+ // larger than the requested size. However, if the requested size is
+ // much smaller than our buffer, reduce our buffer so that we will not
+ // keep too many allocated pixels for too long.
+ if (!image.isNull() && (image.width() > width) && (image.height() > height))
+ if (((2 * width) > image.width()) && ((2 * height) > image.height())) {
+ image.fill(Qt::transparent);
+ return &image;
+ }
+
+ // Round to the nearest 32 pixels so we do not grow the buffer everytime
+ // there is larger request by 1 pixel.
+ width = (1 + (width >> 5)) << 5;
+ height = (1 + (height >> 5)) << 5;
+
+ image = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
+ image.fill(Qt::transparent);
+ return &image;
+}
+
+void ShadowBuffer::schedulePurge()
+{
+ static const double BufferPurgeDelay = 2; // seconds
+ killTimer(timerId);
+ timerId = startTimer(BufferPurgeDelay * 1000);
+}
+
+void ShadowBuffer::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == timerId) {
+ killTimer(timerId);
+ image = QImage();
+ }
+ QObject::timerEvent(event);
+}
+
+Q_GLOBAL_STATIC(ShadowBuffer, scratchShadowBuffer)
+
ContextShadow::ContextShadow()
: type(NoShadow)
, blurRadius(0)
@@ -203,54 +275,66 @@ static void shadowBlur(QImage& image, int radius, const QColor& shadowColor)
p.end();
}
-void ContextShadow::drawShadowRect(QPainter* p, const QRectF& rect)
+QPainter* ContextShadow::beginShadowLayer(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)) {
+ // We expand the area by the blur radius * 2 to give extra space
+ // for the blur transition.
+ int extra = (type == BlurShadow) ? blurRadius * 2 : 0;
+
+ QRectF shadowRect = rect.translated(offset);
+ QRectF bufferRect = shadowRect.adjusted(-extra, -extra, extra, extra);
+ m_layerRect = bufferRect.toAlignedRect();
+
+ QRect clipRect;
+ if (p->hasClipping())
+#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
+ clipRect = p->clipBoundingRect();
+#else
+ clipRect = p->clipRegion().boundingRect();
+#endif
+ else
+ clipRect = p->transform().inverted().mapRect(p->window());
+
+ if (!clipRect.contains(m_layerRect)) {
+
+ // No need to have the buffer larger than the clip.
+ m_layerRect = m_layerRect.intersected(clipRect);
+ if (m_layerRect.isEmpty())
+ return 0;
+
+ // We adjust again because the pixels at the borders are still
+ // potentially affected by the pixels outside the buffer.
+ if (type == BlurShadow)
+ m_layerRect.adjust(-extra, -extra, extra, extra);
+ }
- // No need to have the buffer larger that the clip.
- alignedBufferRect = alignedBufferRect.intersected(clipRect);
- if (alignedBufferRect.isEmpty())
- return;
+ ShadowBuffer* shadowBuffer = scratchShadowBuffer();
+ QImage* shadowImage = shadowBuffer->scratchImage(m_layerRect.size());
+ m_layerImage = QImage(*shadowImage);
- // 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);
- }
+ m_layerPainter = new QPainter;
+ m_layerPainter->begin(&m_layerImage);
+ m_layerPainter->setFont(p->font());
+ m_layerPainter->translate(offset);
- QImage shadowImage(alignedBufferRect.size(), QImage::Format_ARGB32_Premultiplied);
- shadowImage.fill(Qt::transparent);
- QPainter shadowPainter(&shadowImage);
+ // The origin is now the top left corner of the scratch image.
+ m_layerPainter->translate(-m_layerRect.topLeft());
- shadowPainter.fillRect(shadowRect.translated(-alignedBufferRect.topLeft()), color);
- shadowPainter.end();
+ return m_layerPainter;
+}
- shadowBlur(shadowImage, blurRadius, color);
+void ContextShadow::endShadowLayer(QPainter* p)
+{
+ m_layerPainter->end();
+ delete m_layerPainter;
+ m_layerPainter = 0;
- p->drawImage(alignedBufferRect.topLeft(), shadowImage);
+ if (type == BlurShadow)
+ shadowBlur(m_layerImage, blurRadius, color);
- return;
- }
+ p->drawImage(m_layerRect.topLeft(), m_layerImage);
- p->fillRect(rect.translated(offset), color);
+ scratchShadowBuffer()->schedulePurge();
}
-
}