2010-11-23 Helder Correia <helder@sencha.com>
[WebKit-https.git] / WebCore / platform / graphics / qt / ContextShadowQt.cpp
1 /*
2  * Copyright (C) 2010 Sencha, Inc.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "ContextShadow.h"
30
31 #include <QPainter>
32 #include <QTimerEvent>
33
34 namespace WebCore {
35
36 // ContextShadow needs a scratch image as the buffer for the blur filter.
37 // Instead of creating and destroying the buffer for every operation,
38 // we create a buffer which will be automatically purged via a timer.
39
40 class ShadowBuffer: public QObject {
41 public:
42     ShadowBuffer(QObject* parent = 0);
43
44     QImage* scratchImage(const QSize& size);
45
46     void schedulePurge();
47
48 protected:
49     void timerEvent(QTimerEvent* event);
50
51 private:
52     QImage image;
53     int timerId;
54 };
55
56 ShadowBuffer::ShadowBuffer(QObject* parent)
57     : QObject(parent)
58     , timerId(0)
59 {
60 }
61
62 QImage* ShadowBuffer::scratchImage(const QSize& size)
63 {
64     int width = size.width();
65     int height = size.height();
66
67     // We do not need to recreate the buffer if the buffer is reasonably
68     // larger than the requested size. However, if the requested size is
69     // much smaller than our buffer, reduce our buffer so that we will not
70     // keep too many allocated pixels for too long.
71     if (!image.isNull() && (image.width() > width) && (image.height() > height))
72         if (((2 * width) > image.width()) && ((2 * height) > image.height())) {
73             image.fill(Qt::transparent);
74             return &image;
75         }
76
77     // Round to the nearest 32 pixels so we do not grow the buffer everytime
78     // there is larger request by 1 pixel.
79     width = (1 + (width >> 5)) << 5;
80     height = (1 + (height >> 5)) << 5;
81
82     image = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
83     image.fill(Qt::transparent);
84     return &image;
85 }
86
87 void ShadowBuffer::schedulePurge()
88 {
89     static const double BufferPurgeDelay = 2; // seconds
90     killTimer(timerId);
91     timerId = startTimer(BufferPurgeDelay * 1000);
92 }
93
94 void ShadowBuffer::timerEvent(QTimerEvent* event)
95 {
96     if (event->timerId() == timerId) {
97         killTimer(timerId);
98         image = QImage();
99     }
100     QObject::timerEvent(event);
101 }
102
103 Q_GLOBAL_STATIC(ShadowBuffer, scratchShadowBuffer)
104
105 PlatformContext ContextShadow::beginShadowLayer(PlatformContext p, const FloatRect& layerArea)
106 {
107     QRect clipRect;
108     if (p->hasClipping())
109 #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)
110         clipRect = p->clipBoundingRect().toAlignedRect();
111 #else
112         clipRect = p->clipRegion().boundingRect();
113 #endif
114     else
115         clipRect = p->transform().inverted().mapRect(p->window());
116
117     calculateLayerBoundingRect(layerArea, IntRect(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()));
118
119     // Don't paint if we are totally outside the clip region.
120     if (m_layerRect.isEmpty())
121         return 0;
122
123     ShadowBuffer* shadowBuffer = scratchShadowBuffer();
124     QImage* shadowImage = shadowBuffer->scratchImage(m_layerRect.size());
125     m_layerImage = QImage(*shadowImage);
126
127     m_layerContext = new QPainter;
128     m_layerContext->begin(&m_layerImage);
129     m_layerContext->setFont(p->font());
130     m_layerContext->translate(m_offset.width(), m_offset.height());
131
132     // The origin is now the top left corner of the scratch image.
133     m_layerContext->translate(-m_layerRect.x(), -m_layerRect.y());
134
135     return m_layerContext;
136 }
137
138 void ContextShadow::endShadowLayer(PlatformContext p)
139 {
140     m_layerContext->end();
141     delete m_layerContext;
142     m_layerContext = 0;
143
144     if (m_type == BlurShadow) {
145         blurLayerImage(m_layerImage.bits(), IntSize(m_layerImage.width(), m_layerImage.height()),
146                        m_layerImage.bytesPerLine());
147     }
148
149     if (m_type != NoShadow) {
150         // "Colorize" with the right shadow color.
151         QPainter p(&m_layerImage);
152         p.setCompositionMode(QPainter::CompositionMode_SourceIn);
153         p.fillRect(m_layerImage.rect(), m_color.rgb());
154         p.end();
155     }
156
157     p->drawImage(m_layerRect.topLeft(), m_layerImage);
158     scratchShadowBuffer()->schedulePurge();
159 }
160
161 }