Remove static linkage of QtWebKit against the ICO image format plugin.
[WebKit-https.git] / WebCore / platform / graphics / qt / ImageDecoderQt.cpp
1 /*
2  * Copyright (C) 2006 Friedemann Kleint <fkleint@trolltech.com>
3  * Copyright (C) 2006 Trolltech ASA
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "ImageDecoderQt.h"
31
32 #include <QtCore/QByteArray>
33 #include <QtCore/QBuffer>
34
35 #include <QtGui/QImageReader>
36 #include <qdebug.h>
37
38 namespace {
39     const  QImage::Format DesiredFormat = QImage::Format_ARGB32;
40     const  bool debugImageDecoderQt = false;
41 }
42
43 namespace WebCore {
44 ImageDecoderQt::ImageData::ImageData(const QImage& image, ImageState imageState, int duration) :
45     m_image(image), m_imageState(imageState), m_duration(duration)
46 {
47 }
48
49 // Context, maintains IODevice on a data buffer.
50 class ImageDecoderQt::ReadContext {
51 public:
52
53     enum LoadMode {
54         // Load images incrementally. This is still experimental and
55         // will cause the image plugins to report errors.
56         // Also note that as of Qt 4.2.2, the JPEG loader does not return error codes
57         // on "preliminary end of data".
58         LoadIncrementally,
59             // Load images only if  all data have been received
60             LoadComplete };
61
62     ReadContext(const IncomingData & data, LoadMode loadMode, ImageList &target);
63
64     enum ReadResult { ReadEOF, ReadFailed, ReadPartial, ReadComplete };
65
66     // Append data and read out all images. Returns the result
67     // of the last read operation, so, even if  ReadPartial is returned,
68     // a few images might have been read.
69     ReadResult read(bool allDataReceived);
70
71     QImageReader *reader() { return &m_reader; }
72
73 private:
74     enum IncrementalReadResult { IncrementalReadFailed, IncrementalReadPartial, IncrementalReadComplete };
75     // Incrementally read an image
76     IncrementalReadResult readImageLines(ImageData &);
77
78     const LoadMode m_loadMode;
79
80     QByteArray m_data;
81     QBuffer m_buffer;
82     QImageReader m_reader;
83
84     ImageList &m_target;
85
86     // Detected data format of the stream
87     enum QImage::Format m_dataFormat;
88     QSize m_size;
89
90 };
91
92 ImageDecoderQt::ReadContext::ReadContext(const IncomingData & data, LoadMode loadMode, ImageList &target) :
93     m_loadMode(loadMode),
94     m_data(data.data(), data.size()),
95     m_buffer(&m_data),
96     m_reader(&m_buffer),
97     m_target(target),
98     m_dataFormat(QImage::Format_Invalid)
99 {
100     m_buffer.open(QIODevice::ReadOnly);
101 }
102
103
104 ImageDecoderQt::ReadContext::ReadResult
105         ImageDecoderQt::ReadContext::read(bool allDataReceived)
106 {
107     // Complete mode: Read only all all data received
108     if (m_loadMode == LoadComplete && !allDataReceived)
109         return ReadPartial;
110
111     // Attempt to read out all images
112     while (true) {
113         if (m_target.empty() || m_target.back().m_imageState == ImageComplete) {
114             // Start a new image.
115             if (!m_reader.canRead())
116                 return ReadEOF;
117
118             // Attempt to construct an empty image of the matching size and format
119             // for efficient reading
120             QImage newImage = m_dataFormat != QImage::Format_Invalid  ?
121                           QImage(m_size,m_dataFormat) : QImage();
122             m_target.push_back(ImageData(newImage));
123         }
124
125         // read chunks
126         switch (readImageLines(m_target.back())) {
127         case IncrementalReadFailed:
128             m_target.pop_back();
129             return ReadFailed;
130         case IncrementalReadPartial:
131             return ReadPartial;
132         case IncrementalReadComplete:
133             m_target.back().m_imageState = ImageComplete;
134             //store for next
135             m_dataFormat = m_target.back().m_image.format();
136             m_size = m_target.back().m_image.size();
137             const bool supportsAnimation = m_reader.supportsAnimation();
138
139             if (debugImageDecoderQt)
140                 qDebug() << "readImage(): #" << m_target.size() << " complete, " << m_size << " format " << m_dataFormat
141                 <<  " supportsAnimation=" <<  supportsAnimation ;
142             // No point in readinfg further
143             if (!supportsAnimation)
144                 return ReadComplete;
145
146             break;
147         }
148     }
149     return ReadComplete;
150 }
151
152
153
154 ImageDecoderQt::ReadContext::IncrementalReadResult
155         ImageDecoderQt::ReadContext::readImageLines(ImageData &imageData)
156 {
157     // TODO: Implement incremental reading here,
158     // set state to reflect complete header, etc.
159     // For now, we read the whole image.
160
161     const qint64 startPos = m_buffer.pos ();
162     // Oops, failed. Rewind.
163     if (!m_reader.read(&imageData.m_image)) {
164         m_buffer.seek(startPos);
165         const bool gotHeader = imageData.m_image.size().width();
166
167         if (debugImageDecoderQt)
168             qDebug() << "readImageLines(): read() failed: " << m_reader.errorString()
169                 << " got header=" << gotHeader;
170         // [Experimental] Did we manage to read the header?
171         if (gotHeader) {
172             imageData.m_imageState = ImageHeaderValid;
173             return IncrementalReadPartial;
174         }
175         return IncrementalReadFailed;
176     }
177     imageData.m_duration = m_reader.nextImageDelay();
178     return IncrementalReadComplete;
179 }
180
181
182 // ImageDecoderQt
183 ImageDecoderQt::ImageDecoderQt( )
184 {
185 }
186
187 ImageDecoderQt::~ImageDecoderQt()
188 {
189 }
190
191 bool ImageDecoderQt::hasFirstImageHeader() const
192 {
193     return  !m_imageList.empty() && m_imageList[0].m_imageState >= ImageHeaderValid;
194 }
195
196 void ImageDecoderQt::reset()
197 {
198     m_failed = false;
199     m_imageList.clear();
200     m_pixmapCache.clear();
201     m_sizeAvailable = false;
202     m_loopCount = cAnimationNone;
203     m_size = IntSize(-1, -1);
204 }
205
206 void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived)
207 {
208     reset();
209     ReadContext readContext(data, ReadContext::LoadComplete, m_imageList);
210
211     if (debugImageDecoderQt)
212         qDebug() << " setData " << data.size() << " image bytes, complete=" << allDataReceived;
213
214     const  ReadContext::ReadResult readResult =  readContext.read(allDataReceived);
215
216     if (debugImageDecoderQt)
217         qDebug()  << " read returns " << readResult;
218
219     switch ( readResult)    {
220     case ReadContext::ReadFailed:
221         m_failed = true;
222         break;
223     case ReadContext::ReadEOF:
224     case ReadContext::ReadPartial:
225     case ReadContext::ReadComplete:
226         // Did we read anything - try to set the size.
227         if (hasFirstImageHeader()) {
228             m_sizeAvailable = true;
229             m_size = m_imageList[0].m_image.size();
230
231             if (readContext.reader()->supportsAnimation()) {
232                 if (readContext.reader()->loopCount() != -1)
233                     m_loopCount = readContext.reader()->loopCount();
234                 else
235                     m_loopCount = 0; //loop forever
236             }
237         }
238         break;
239     }
240 }
241
242
243 bool ImageDecoderQt::isSizeAvailable() const
244 {
245     if (debugImageDecoderQt)
246         qDebug() << " ImageDecoderQt::isSizeAvailable() returns" << m_sizeAvailable;
247     return m_sizeAvailable;
248 }
249
250 int ImageDecoderQt::frameCount() const
251 {
252     if (debugImageDecoderQt)
253         qDebug() << " ImageDecoderQt::frameCount() returns" << m_imageList.size();
254     return m_imageList.size();
255 }
256
257
258 int ImageDecoderQt::repetitionCount() const
259 {
260     if (debugImageDecoderQt)
261         qDebug() << " ImageDecoderQt::repetitionCount() returns" << m_loopCount;
262     return m_loopCount;
263 }
264
265
266 bool ImageDecoderQt::supportsAlpha() const
267 {
268     return hasFirstImageHeader() && m_imageList[0].m_image.hasAlphaChannel();
269 }
270
271 int ImageDecoderQt::duration(size_t index) const
272 {
273     if (index >= m_imageList.size())
274         return 0;
275     return  m_imageList[index].m_duration;
276 }
277
278 RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index)
279 {
280     Q_ASSERT("use imageAtIndex instead");
281     return 0;
282 }
283
284 QPixmap* ImageDecoderQt::imageAtIndex(size_t index) const
285 {
286     if (debugImageDecoderQt)
287         qDebug() << "ImageDecoderQt::imageAtIndex(" << index << ')';
288
289     if (index >= m_imageList.size())
290         return 0;
291
292     if (!m_pixmapCache.contains(index)) {
293         m_pixmapCache.insert(index,
294                              QPixmap::fromImage(m_imageList[index].m_image));
295     }
296     return  &m_pixmapCache[index];
297 }
298
299 void ImageDecoderQt::clearFrame(size_t index)
300 {
301     if (m_imageList.size() < (int)index)
302         m_imageList[index].m_image = QImage();
303     m_pixmapCache.take(index);
304 }
305
306 }
307
308 // vim: ts=4 sw=4 et