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