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)
55QT_BEGIN_NAMESPACE
56Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP, int hbitmapFormat = 0);
57QT_END_NAMESPACE
58#endif
59
60typedef QHash<QByteArray, QPixmap> WebGraphicHash;
61Q_GLOBAL_STATIC(WebGraphicHash, _graphics)
62
63static void earlyClearGraphics()
64{
65    _graphics()->clear();
66}
67
68static 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
95static QPixmap loadResourcePixmap(const char *name)
96{
97    return graphics()->value(name);
98}
99
100namespace WebCore {
101
102bool 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
120PassRefPtr<Image> Image::loadPlatformResource(const char* name)
121{
122    return StillImage::create(loadResourcePixmap(name));
123}
124
125void 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
134void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform,
135    const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode)
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
199BitmapImage::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
229void BitmapImage::invalidatePlatformData()
230{
231}
232
233// Drawing Routines
234void 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->state());
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
279void 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)
296PassRefPtr<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
308