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