/* * 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" #include "AffineTransform.h" #include "GraphicsContext.h" #include #include 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(-1) { } 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(0); 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(0); return ℑ } void ShadowBuffer::schedulePurge() { static const double BufferPurgeDelay = 2; // seconds if (timerId >= 0) 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) PlatformContext ContextShadow::beginShadowLayer(GraphicsContext* context, const FloatRect& layerArea) { // Set m_blurDistance. adjustBlurDistance(context); PlatformContext p = context->platformContext(); QRect clipRect; if (p->hasClipping()) #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) clipRect = p->clipBoundingRect().toAlignedRect(); #else clipRect = p->clipRegion().boundingRect(); #endif else clipRect = p->transform().inverted().mapRect(p->window()); // Set m_layerOrigin, m_layerContextTranslation, m_sourceRect. IntRect clip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); IntRect layerRect = calculateLayerBoundingRect(context, layerArea, clip); // Don't paint if we are totally outside the clip region. if (layerRect.isEmpty()) return 0; ShadowBuffer* shadowBuffer = scratchShadowBuffer(); QImage* shadowImage = shadowBuffer->scratchImage(layerRect.size()); m_layerImage = QImage(*shadowImage); m_layerContext = new QPainter; m_layerContext->begin(&m_layerImage); m_layerContext->setFont(p->font()); m_layerContext->translate(m_layerContextTranslation); return m_layerContext; } void ContextShadow::endShadowLayer(GraphicsContext* context) { m_layerContext->end(); delete m_layerContext; m_layerContext = 0; if (m_type == BlurShadow) { blurLayerImage(m_layerImage.bits(), IntSize(m_layerImage.width(), m_layerImage.height()), m_layerImage.bytesPerLine()); } if (m_type != NoShadow) { // "Colorize" with the right shadow color. QPainter p(&m_layerImage); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(m_layerImage.rect(), m_color.rgb()); p.end(); } context->platformContext()->drawImage(m_layerOrigin, m_layerImage, m_sourceRect); scratchShadowBuffer()->schedulePurge(); } }