eaef4cda7d66091acc4bbb3292d210f2fa21289d
[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 #if !defined(Q_OS_WIN)
39 Q_IMPORT_PLUGIN(qtwebico) //For ico format...
40 #endif
41
42 namespace {
43     const  QImage::Format DesiredFormat = QImage::Format_ARGB32;
44     const  bool debugImageDecoderQt = false;
45 }
46
47 namespace WebCore {
48 ImageDecoderQt::ImageData::ImageData(const QImage& image, ImageState imageState, int duration) :
49     m_image(image), m_imageState(imageState), m_duration(duration)
50 {
51 }
52
53 // Context, maintains IODevice on a data buffer.
54 class ImageDecoderQt::ReadContext {
55 public:
56
57     enum LoadMode {
58         // Load images incrementally. This is still experimental and
59         // will cause the image plugins to report errors.
60         // Also note that as of Qt 4.2.2, the JPEG loader does not return error codes
61         // on "preliminary end of data".
62         LoadIncrementally,
63             // Load images only if  all data have been received
64             LoadComplete };
65
66     ReadContext(const IncomingData & data, LoadMode loadMode, ImageList &target);
67
68     enum ReadResult { ReadEOF, ReadFailed, ReadPartial, ReadComplete };
69
70     // Append data and read out all images. Returns the result
71     // of the last read operation, so, even if  ReadPartial is returned,
72     // a few images might have been read.
73     ReadResult read(bool allDataReceived);
74
75     QImageReader *reader() { return &m_reader; }
76
77 private:
78     enum IncrementalReadResult { IncrementalReadFailed, IncrementalReadPartial, IncrementalReadComplete };
79     // Incrementally read an image
80     IncrementalReadResult readImageLines(ImageData &);
81
82     const LoadMode m_loadMode;
83
84     QByteArray m_data;
85     QBuffer m_buffer;
86     QImageReader m_reader;
87
88     ImageList &m_target;
89
90     // Detected data format of the stream
91     enum QImage::Format m_dataFormat;
92     QSize m_size;
93
94 };
95
96 ImageDecoderQt::ReadContext::ReadContext(const IncomingData & data, LoadMode loadMode, ImageList &target) :
97     m_loadMode(loadMode),
98     m_data(data.data(), data.size()),
99     m_buffer(&m_data),
100     m_reader(&m_buffer),
101     m_target(target),
102     m_dataFormat(QImage::Format_Invalid)
103 {
104     m_buffer.open(QIODevice::ReadOnly);
105 }
106
107
108 ImageDecoderQt::ReadContext::ReadResult
109         ImageDecoderQt::ReadContext::read(bool allDataReceived)
110 {
111     // Complete mode: Read only all all data received
112     if (m_loadMode == LoadComplete && !allDataReceived)
113         return ReadPartial;
114
115     // Attempt to read out all images
116     while (true) {
117         if (m_target.empty() || m_target.back().m_imageState == ImageComplete) {
118             // Start a new image.
119             if (!m_reader.canRead())
120                 return ReadEOF;
121
122             // Attempt to construct an empty image of the matching size and format
123             // for efficient reading
124             QImage newImage = m_dataFormat != QImage::Format_Invalid  ?
125                           QImage(m_size,m_dataFormat) : QImage();
126             m_target.push_back(ImageData(newImage));
127         }
128
129         // read chunks
130         switch (readImageLines(m_target.back())) {
131         case IncrementalReadFailed:
132             m_target.pop_back();
133             return ReadFailed;
134         case IncrementalReadPartial:
135             return ReadPartial;
136         case IncrementalReadComplete:
137             m_target.back().m_imageState = ImageComplete;
138             //store for next
139             m_dataFormat = m_target.back().m_image.format();
140             m_size = m_target.back().m_image.size();
141             const bool supportsAnimation = m_reader.supportsAnimation();
142
143             if (debugImageDecoderQt)
144                 qDebug() << "readImage(): #" << m_target.size() << " complete, " << m_size << " format " << m_dataFormat
145                 <<  " supportsAnimation=" <<  supportsAnimation ;
146             // No point in readinfg further
147             if (!supportsAnimation)
148                 return ReadComplete;
149
150             break;
151         }
152     }
153     return ReadComplete;
154 }
155
156
157
158 ImageDecoderQt::ReadContext::IncrementalReadResult
159         ImageDecoderQt::ReadContext::readImageLines(ImageData &imageData)
160 {
161     // TODO: Implement incremental reading here,
162     // set state to reflect complete header, etc.
163     // For now, we read the whole image.
164
165     const qint64 startPos = m_buffer.pos ();
166     // Oops, failed. Rewind.
167     if (!m_reader.read(&imageData.m_image)) {
168         m_buffer.seek(startPos);
169         const bool gotHeader = imageData.m_image.size().width();
170
171         if (debugImageDecoderQt)
172             qDebug() << "readImageLines(): read() failed: " << m_reader.errorString()
173                 << " got header=" << gotHeader;
174         // [Experimental] Did we manage to read the header?
175         if (gotHeader) {
176             imageData.m_imageState = ImageHeaderValid;
177             return IncrementalReadPartial;
178         }
179         return IncrementalReadFailed;
180     }
181     imageData.m_duration = m_reader.nextImageDelay();
182     return IncrementalReadComplete;
183 }
184
185
186 // ImageDecoderQt
187 ImageDecoderQt::ImageDecoderQt( )
188 {
189 }
190
191 ImageDecoderQt::~ImageDecoderQt()
192 {
193 }
194
195 bool ImageDecoderQt::hasFirstImageHeader() const
196 {
197     return  !m_imageList.empty() && m_imageList[0].m_imageState >= ImageHeaderValid;
198 }
199
200 void ImageDecoderQt::reset()
201 {
202     m_failed = false;
203     m_imageList.clear();
204     m_pixmapCache.clear();
205     m_sizeAvailable = false;
206     m_loopCount = cAnimationNone;
207     m_size = IntSize(-1, -1);
208 }
209
210 void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived)
211 {
212     reset();
213     ReadContext readContext(data, ReadContext::LoadComplete, m_imageList);
214
215     if (debugImageDecoderQt)
216         qDebug() << " setData " << data.size() << " image bytes, complete=" << allDataReceived;
217
218     const  ReadContext::ReadResult readResult =  readContext.read(allDataReceived);
219
220     if (debugImageDecoderQt)
221         qDebug()  << " read returns " << readResult;
222
223     switch ( readResult)    {
224     case ReadContext::ReadFailed:
225         m_failed = true;
226         break;
227     case ReadContext::ReadEOF:
228     case ReadContext::ReadPartial:
229     case ReadContext::ReadComplete:
230         // Did we read anything - try to set the size.
231         if (hasFirstImageHeader()) {
232             m_sizeAvailable = true;
233             m_size = m_imageList[0].m_image.size();
234
235             if (readContext.reader()->supportsAnimation()) {
236                 if (readContext.reader()->loopCount() != -1)
237                     m_loopCount = readContext.reader()->loopCount();
238                 else
239                     m_loopCount = 0; //loop forever
240             }
241         }
242         break;
243     }
244 }
245
246
247 bool ImageDecoderQt::isSizeAvailable() const
248 {
249     if (debugImageDecoderQt)
250         qDebug() << " ImageDecoderQt::isSizeAvailable() returns" << m_sizeAvailable;
251     return m_sizeAvailable;
252 }
253
254 int ImageDecoderQt::frameCount() const
255 {
256     if (debugImageDecoderQt)
257         qDebug() << " ImageDecoderQt::frameCount() returns" << m_imageList.size();
258     return m_imageList.size();
259 }
260
261
262 int ImageDecoderQt::repetitionCount() const
263 {
264     if (debugImageDecoderQt)
265         qDebug() << " ImageDecoderQt::repetitionCount() returns" << m_loopCount;
266     return m_loopCount;
267 }
268
269
270 bool ImageDecoderQt::supportsAlpha() const
271 {
272     return hasFirstImageHeader() && m_imageList[0].m_image.hasAlphaChannel();
273 }
274
275 int ImageDecoderQt::duration(size_t index) const
276 {
277     if (index >= m_imageList.size())
278         return 0;
279     return  m_imageList[index].m_duration;
280 }
281
282 RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index)
283 {
284     Q_ASSERT("use imageAtIndex instead");
285     return 0;
286 }
287
288 QPixmap* ImageDecoderQt::imageAtIndex(size_t index) const
289 {
290     if (debugImageDecoderQt)
291         qDebug() << "ImageDecoderQt::imageAtIndex(" << index << ')';
292
293     if (index >= m_imageList.size())
294         return 0;
295
296     if (!m_pixmapCache.contains(index)) {
297         m_pixmapCache.insert(index,
298                              QPixmap::fromImage(m_imageList[index].m_image));
299     }
300     return  &m_pixmapCache[index];
301 }
302
303 void ImageDecoderQt::clearFrame(size_t index)
304 {
305     if (m_imageList.size() < (int)index)
306         m_imageList[index].m_image = QImage();
307     m_pixmapCache.take(index);
308 }
309
310 }
311
312 // vim: ts=4 sw=4 et