Extend platform layer so it can pass blend modes to the compositing calls
[WebKit-https.git] / Source / WebCore / platform / graphics / qt / ImageQt.cpp
1 /*
2  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
3  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
4  * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
5  * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
6  * Copyright (C) 2010 Sencha, Inc.
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "Image.h"
34
35 #include "AffineTransform.h"
36 #include "BitmapImage.h"
37 #include "FloatRect.h"
38 #include "GraphicsContext.h"
39 #include "ImageObserver.h"
40 #include "ShadowBlur.h"
41 #include "StillImageQt.h"
42 #include <wtf/text/WTFString.h>
43
44 #include <QCoreApplication>
45 #include <QDebug>
46 #include <QImage>
47 #include <QImageReader>
48 #include <QPainter>
49 #include <QPixmap>
50 #include <QTransform>
51
52 #include <math.h>
53
54 #if OS(WINDOWS)
55 QT_BEGIN_NAMESPACE
56 Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP, int hbitmapFormat = 0);
57 QT_END_NAMESPACE
58 #endif
59
60 typedef QHash<QByteArray, QPixmap> WebGraphicHash;
61 Q_GLOBAL_STATIC(WebGraphicHash, _graphics)
62
63 static void earlyClearGraphics()
64 {
65     _graphics()->clear();
66 }
67
68 static WebGraphicHash* graphics()
69 {
70     WebGraphicHash* hash = _graphics();
71
72     if (hash->isEmpty()) {
73
74         // prevent ~QPixmap running after ~QApplication (leaks native pixmaps)
75         qAddPostRoutine(earlyClearGraphics);
76
77         // QWebSettings::MissingImageGraphic
78         hash->insert("missingImage", QPixmap(QLatin1String(":webkit/resources/missingImage.png")));
79         // QWebSettings::MissingPluginGraphic
80         hash->insert("nullPlugin", QPixmap(QLatin1String(":webkit/resources/nullPlugin.png")));
81         // QWebSettings::DefaultFrameIconGraphic
82         hash->insert("urlIcon", QPixmap(QLatin1String(":webkit/resources/urlIcon.png")));
83         // QWebSettings::TextAreaSizeGripCornerGraphic
84         hash->insert("textAreaResizeCorner", QPixmap(QLatin1String(":webkit/resources/textAreaResizeCorner.png")));
85         // QWebSettings::DeleteButtonGraphic
86         hash->insert("deleteButton", QPixmap(QLatin1String(":webkit/resources/deleteButton.png")));
87         // QWebSettings::InputSpeechButtonGraphic
88         hash->insert("inputSpeech", QPixmap(QLatin1String(":webkit/resources/inputSpeech.png")));
89     }
90
91     return hash;
92 }
93
94 // This function loads resources into WebKit
95 static QPixmap loadResourcePixmap(const char *name)
96 {
97     return graphics()->value(name);
98 }
99
100 namespace WebCore {
101
102 bool FrameData::clear(bool clearMetadata)
103 {
104     if (clearMetadata)
105         m_haveMetadata = false;
106
107     if (m_frame) {
108         delete m_frame;
109         m_frame = 0;
110         return true;
111     }
112     return false;
113 }
114
115
116 // ================================================
117 // Image Class
118 // ================================================
119
120 PassRefPtr<Image> Image::loadPlatformResource(const char* name)
121 {
122     return StillImage::create(loadResourcePixmap(name));
123 }
124
125 void Image::setPlatformResource(const char* name, const QPixmap& pixmap)
126 {
127     WebGraphicHash* h = graphics();
128     if (pixmap.isNull())
129         h->remove(name);
130     else
131         h->insert(name, pixmap);
132 }
133
134 void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform,
135                         const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect)
136 {
137     QPixmap* framePixmap = nativeImageForCurrentFrame();
138     if (!framePixmap) // If it's too early we won't have an image yet.
139         return;
140
141 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
142     FloatRect tileRectAdjusted = adjustSourceRectForDownSampling(tileRect, framePixmap->size());
143 #else
144     FloatRect tileRectAdjusted = tileRect;
145 #endif
146
147     // Qt interprets 0 width/height as full width/height so just short circuit.
148     QRectF dr = QRectF(destRect).normalized();
149     QRect tr = QRectF(tileRectAdjusted).toRect().normalized();
150     if (!dr.width() || !dr.height() || !tr.width() || !tr.height())
151         return;
152
153     QPixmap pixmap = *framePixmap;
154     if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height())
155         pixmap = pixmap.copy(tr);
156
157     CompositeOperator previousOperator = ctxt->compositeOperation();
158
159     ctxt->setCompositeOperation(!pixmap.hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op);
160
161     QPainter* p = ctxt->platformContext();
162     QTransform transform(patternTransform);
163
164     // If this would draw more than one scaled tile, we scale the pixmap first and then use the result to draw.
165     if (transform.type() == QTransform::TxScale) {
166         QRectF tileRectInTargetCoords = (transform * QTransform().translate(phase.x(), phase.y())).mapRect(tr);
167
168         bool tileWillBePaintedOnlyOnce = tileRectInTargetCoords.contains(dr);
169         if (!tileWillBePaintedOnlyOnce) {
170             QSizeF scaledSize(float(pixmap.width()) * transform.m11(), float(pixmap.height()) * transform.m22());
171             QPixmap scaledPixmap(scaledSize.toSize());
172             if (pixmap.hasAlpha())
173                 scaledPixmap.fill(Qt::transparent);
174             {
175                 QPainter painter(&scaledPixmap);
176                 painter.setCompositionMode(QPainter::CompositionMode_Source);
177                 painter.setRenderHints(p->renderHints());
178                 painter.drawPixmap(QRect(0, 0, scaledPixmap.width(), scaledPixmap.height()), pixmap);
179             }
180             pixmap = scaledPixmap;
181             transform = QTransform::fromTranslate(transform.dx(), transform.dy());
182         }
183     }
184
185     /* Translate the coordinates as phase is not in world matrix coordinate space but the tile rect origin is. */
186     transform *= QTransform().translate(phase.x(), phase.y());
187     transform.translate(tr.x(), tr.y());
188
189     QBrush b(pixmap);
190     b.setTransform(transform);
191     p->fillRect(dr, b);
192
193     ctxt->setCompositeOperation(previousOperator);
194
195     if (imageObserver())
196         imageObserver()->didDraw(this);
197 }
198
199 BitmapImage::BitmapImage(QPixmap* pixmap, ImageObserver* observer)
200     : Image(observer)
201     , m_currentFrame(0)
202     , m_frames(0)
203     , m_frameTimer(0)
204     , m_repetitionCount(cAnimationNone)
205     , m_repetitionCountStatus(Unknown)
206     , m_repetitionsComplete(0)
207     , m_decodedSize(0)
208     , m_frameCount(1)
209     , m_isSolidColor(false)
210     , m_checkedForSolidColor(false)
211     , m_animationFinished(true)
212     , m_allDataReceived(true)
213     , m_haveSize(true)
214     , m_sizeAvailable(true)
215     , m_haveFrameCount(true)
216 {
217     int width = pixmap->width();
218     int height = pixmap->height();
219     m_decodedSize = width * height * 4;
220     m_size = IntSize(width, height);
221
222     m_frames.grow(1);
223     m_frames[0].m_frame = pixmap;
224     m_frames[0].m_hasAlpha = pixmap->hasAlpha();
225     m_frames[0].m_haveMetadata = true;
226     checkForSolidColor();
227 }
228
229 void BitmapImage::invalidatePlatformData()
230 {
231 }
232
233 // Drawing Routines
234 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst,
235     const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op, BlendMode)
236 {
237     QRectF normalizedDst = dst.normalized();
238     QRectF normalizedSrc = src.normalized();
239
240     startAnimation();
241
242     if (normalizedSrc.isEmpty() || normalizedDst.isEmpty())
243         return;
244
245     QPixmap* image = nativeImageForCurrentFrame();
246     if (!image)
247         return;
248
249     if (mayFillWithSolidColor()) {
250         fillWithSolidColor(ctxt, normalizedDst, solidColor(), styleColorSpace, op);
251         return;
252     }
253
254 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
255     normalizedSrc = adjustSourceRectForDownSampling(normalizedSrc, image->size());
256 #endif
257
258     CompositeOperator previousOperator = ctxt->compositeOperation();
259     ctxt->setCompositeOperation(!image->hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op);
260
261     if (ctxt->hasShadow()) {
262         ShadowBlur* shadow = ctxt->shadowBlur();
263         GraphicsContext* shadowContext = shadow->beginShadowLayer(ctxt, normalizedDst);
264         if (shadowContext) {
265             QPainter* shadowPainter = shadowContext->platformContext();
266             shadowPainter->drawPixmap(normalizedDst, *image, normalizedSrc);
267             shadow->endShadowLayer(ctxt);
268         }
269     }
270
271     ctxt->platformContext()->drawPixmap(normalizedDst, *image, normalizedSrc);
272
273     ctxt->setCompositeOperation(previousOperator);
274
275     if (imageObserver())
276         imageObserver()->didDraw(this);
277 }
278
279 void BitmapImage::checkForSolidColor()
280 {
281     m_isSolidColor = false;
282     m_checkedForSolidColor = true;
283
284     if (frameCount() > 1)
285         return;
286
287     QPixmap* framePixmap = frameAtIndex(0);
288     if (!framePixmap || framePixmap->width() != 1 || framePixmap->height() != 1)
289         return;
290
291     m_isSolidColor = true;
292     m_solidColor = QColor::fromRgba(framePixmap->toImage().pixel(0, 0));
293 }
294
295 #if OS(WINDOWS)
296 PassRefPtr<BitmapImage> BitmapImage::create(HBITMAP hBitmap)
297 {
298     QPixmap* qPixmap = new QPixmap(qt_pixmapFromWinHBITMAP(hBitmap));
299
300     return BitmapImage::create(qPixmap);
301 }
302 #endif
303
304 }
305
306
307 // vim: ts=4 sw=4 et