10fe1feb6e87f4f6a682c2eec4ee07c7ebafd03a
[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 "NativeImageQt.h"
41 #include "PlatformString.h"
42 #include "ShadowBlur.h"
43 #include "StillImageQt.h"
44
45 #include <QCoreApplication>
46 #include <QDebug>
47 #include <QImage>
48 #include <QImageReader>
49 #include <QPainter>
50 #include <QPixmap>
51 #include <QTransform>
52
53 #include <math.h>
54
55 #if OS(WINDOWS) && HAVE(QT5)
56 Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP, int hbitmapFormat = 0);
57 #endif
58
59 typedef QHash<QByteArray, QImage> WebGraphicHash;
60 Q_GLOBAL_STATIC(WebGraphicHash, _graphics)
61
62 static void earlyClearGraphics()
63 {
64     _graphics()->clear();
65 }
66
67 static WebGraphicHash* graphics()
68 {
69     WebGraphicHash* hash = _graphics();
70
71     if (hash->isEmpty()) {
72
73         // prevent ~QImage running after ~QApplication (leaks native images)
74         qAddPostRoutine(earlyClearGraphics);
75
76         // QWebSettings::MissingImageGraphic
77         hash->insert("missingImage", QImage(QLatin1String(":webkit/resources/missingImage.png")));
78         // QWebSettings::MissingPluginGraphic
79         hash->insert("nullPlugin", QImage(QLatin1String(":webkit/resources/nullPlugin.png")));
80         // QWebSettings::DefaultFrameIconGraphic
81         hash->insert("urlIcon", QImage(QLatin1String(":webkit/resources/urlIcon.png")));
82         // QWebSettings::TextAreaSizeGripCornerGraphic
83         hash->insert("textAreaResizeCorner", QImage(QLatin1String(":webkit/resources/textAreaResizeCorner.png")));
84         // QWebSettings::DeleteButtonGraphic
85         hash->insert("deleteButton", QImage(QLatin1String(":webkit/resources/deleteButton.png")));
86         // QWebSettings::InputSpeechButtonGraphic
87         hash->insert("inputSpeech", QImage(QLatin1String(":webkit/resources/inputSpeech.png")));
88     }
89
90     return hash;
91 }
92
93 // This function loads resources into WebKit
94 static QImage loadResourceImage(const char *name)
95 {
96     return graphics()->value(name);
97 }
98
99 namespace WebCore {
100
101 bool FrameData::clear(bool clearMetadata)
102 {
103     if (clearMetadata)
104         m_haveMetadata = false;
105
106     if (m_frame) {
107         delete m_frame;
108         m_frame = 0;
109         return true;
110     }
111     return false;
112 }
113
114
115 // ================================================
116 // Image Class
117 // ================================================
118
119 PassRefPtr<Image> Image::loadPlatformResource(const char* name)
120 {
121     return StillImage::create(loadResourceImage(name));
122 }
123
124 void Image::setPlatformResource(const char* name, const QImage& image)
125 {
126     WebGraphicHash* h = graphics();
127     if (image.isNull())
128         h->remove(name);
129     else
130         h->insert(name, image);
131 }
132
133 void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform,
134                         const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect)
135 {
136     QImage* frameImage = nativeImageForCurrentFrame();
137     if (!frameImage) // If it's too early we won't have an image yet.
138         return;
139
140 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
141     FloatRect tileRectAdjusted = adjustSourceRectForDownSampling(tileRect, framePixmap->size());
142 #else
143     FloatRect tileRectAdjusted = tileRect;
144 #endif
145
146     // Qt interprets 0 width/height as full width/height so just short circuit.
147     QRectF dr = QRectF(destRect).normalized();
148     QRect tr = QRectF(tileRectAdjusted).toRect().normalized();
149     if (!dr.width() || !dr.height() || !tr.width() || !tr.height())
150         return;
151
152     QImage image = *frameImage;
153     if (tr.x() || tr.y() || tr.width() != image.width() || tr.height() != image.height())
154         image = image.copy(tr);
155
156     CompositeOperator previousOperator = ctxt->compositeOperation();
157
158     ctxt->setCompositeOperation(!image.hasAlphaChannel() && op == CompositeSourceOver ? CompositeCopy : op);
159
160     QPainter* p = ctxt->platformContext();
161     QTransform transform(patternTransform);
162
163     // If this would draw more than one scaled tile, we scale the image first and then use the result to draw.
164     if (transform.type() == QTransform::TxScale) {
165         QRectF tileRectInTargetCoords = (transform * QTransform().translate(phase.x(), phase.y())).mapRect(tr);
166
167         bool tileWillBePaintedOnlyOnce = tileRectInTargetCoords.contains(dr);
168         if (!tileWillBePaintedOnlyOnce) {
169             QSizeF scaledSize(float(image.width()) * transform.m11(), float(image.height()) * transform.m22());
170             QImage scaledImage;
171             if (image.hasAlphaChannel()) {
172                 scaledImage = QImage(scaledSize.toSize(), NativeImageQt::defaultFormatForAlphaEnabledImages());
173                 scaledImage.fill(Qt::transparent);
174             } else
175                 scaledImage = QImage(scaledSize.toSize(), NativeImageQt::defaultFormatForOpaqueImages());
176
177             {
178                 QPainter painter(&scaledImage);
179                 painter.setCompositionMode(QPainter::CompositionMode_Source);
180                 painter.setRenderHints(p->renderHints());
181                 painter.drawImage(QRect(0, 0, scaledImage.width(), scaledImage.height()), image);
182             }
183             image = scaledImage;
184             transform = QTransform::fromTranslate(transform.dx(), transform.dy());
185         }
186     }
187
188     /* Translate the coordinates as phase is not in world matrix coordinate space but the tile rect origin is. */
189     transform *= QTransform().translate(phase.x(), phase.y());
190     transform.translate(tr.x(), tr.y());
191
192     QBrush b(image);
193     b.setTransform(transform);
194     p->fillRect(dr, b);
195
196     ctxt->setCompositeOperation(previousOperator);
197
198     if (imageObserver())
199         imageObserver()->didDraw(this);
200 }
201
202 BitmapImage::BitmapImage(QImage* image, ImageObserver* observer)
203     : Image(observer)
204     , m_currentFrame(0)
205     , m_frames(0)
206     , m_frameTimer(0)
207     , m_repetitionCount(cAnimationNone)
208     , m_repetitionCountStatus(Unknown)
209     , m_repetitionsComplete(0)
210     , m_decodedSize(0)
211     , m_frameCount(1)
212     , m_isSolidColor(false)
213     , m_checkedForSolidColor(false)
214     , m_animationFinished(true)
215     , m_allDataReceived(true)
216     , m_haveSize(true)
217     , m_sizeAvailable(true)
218     , m_haveFrameCount(true)
219 {
220     int width = image->width();
221     int height = image->height();
222     m_decodedSize = width * height * 4;
223     m_size = IntSize(width, height);
224
225     m_frames.grow(1);
226     m_frames[0].m_frame = image;
227     m_frames[0].m_hasAlpha = image->hasAlphaChannel();
228     m_frames[0].m_haveMetadata = true;
229     checkForSolidColor();
230 }
231
232 void BitmapImage::invalidatePlatformData()
233 {
234 }
235
236 // Drawing Routines
237 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst,
238                        const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op)
239 {
240     QRectF normalizedDst = dst.normalized();
241     QRectF normalizedSrc = src.normalized();
242
243     startAnimation();
244
245     if (normalizedSrc.isEmpty() || normalizedDst.isEmpty())
246         return;
247
248     QImage* image = nativeImageForCurrentFrame();
249
250     if (!image)
251         return;
252
253     if (mayFillWithSolidColor()) {
254         fillWithSolidColor(ctxt, normalizedDst, solidColor(), styleColorSpace, op);
255         return;
256     }
257
258 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
259     normalizedSrc = adjustSourceRectForDownSampling(normalizedSrc, image->size());
260 #endif
261
262     CompositeOperator previousOperator = ctxt->compositeOperation();
263     ctxt->setCompositeOperation(!image->hasAlphaChannel() && op == CompositeSourceOver ? CompositeCopy : op);
264
265     if (ctxt->hasShadow()) {
266         ShadowBlur* shadow = ctxt->shadowBlur();
267         GraphicsContext* shadowContext = shadow->beginShadowLayer(ctxt, normalizedDst);
268         if (shadowContext) {
269             QPainter* shadowPainter = shadowContext->platformContext();
270             shadowPainter->drawImage(normalizedDst, *image, normalizedSrc);
271             shadow->endShadowLayer(ctxt);
272         }
273     }
274
275     ctxt->platformContext()->drawImage(normalizedDst, *image, normalizedSrc);
276
277     ctxt->setCompositeOperation(previousOperator);
278
279     if (imageObserver())
280         imageObserver()->didDraw(this);
281 }
282
283 void BitmapImage::checkForSolidColor()
284 {
285     m_isSolidColor = false;
286     m_checkedForSolidColor = true;
287
288     if (frameCount() > 1)
289         return;
290
291     QImage* frameImage = frameAtIndex(0);
292     if (!frameImage || frameImage->width() != 1 || frameImage->height() != 1)
293         return;
294
295     m_isSolidColor = true;
296     m_solidColor = QColor::fromRgba(frameImage->pixel(0, 0));
297 }
298
299 #if OS(WINDOWS)
300 PassRefPtr<BitmapImage> BitmapImage::create(HBITMAP hBitmap)
301 {
302 #if HAVE(QT5)
303     QImage* nativeImage = new QImage(qt_pixmapFromWinHBITMAP(hBitmap).toImage());
304 #else
305     QImage* nativeImage = new QImage(QPixmap::fromWinHBITMAP(hBitmap).toImage());
306 #endif
307
308     return BitmapImage::create(nativeImage);
309 }
310 #endif
311
312 }
313
314
315 // vim: ts=4 sw=4 et