diff options
Diffstat (limited to 'WebCore/platform/graphics/qt/ContextShadow.cpp')
-rw-r--r-- | WebCore/platform/graphics/qt/ContextShadow.cpp | 162 |
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 ℑ + } + + // 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 ℑ +} + +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(); } - } |