• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 Friedemann Kleint <fkleint@trolltech.com>
3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
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 {
ImageData(const QImage & image,ImageState imageState,int duration)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 
reader()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 
ReadContext(const IncomingData & data,LoadMode loadMode,ImageList & target)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
read(bool allDataReceived)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
141                     << " format " << m_dataFormat <<  " 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
readImageLines(ImageData & imageData)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 
create(const SharedBuffer & data)181 ImageDecoderQt* ImageDecoderQt::create(const SharedBuffer& data)
182 {
183     // We need at least 4 bytes to figure out what kind of image we're dealing with.
184     if (data.size() < 4)
185         return 0;
186 
187     QByteArray bytes = QByteArray::fromRawData(data.data(), data.size());
188     QBuffer buffer(&bytes);
189     if (!buffer.open(QBuffer::ReadOnly))
190         return 0;
191 
192     QString imageFormat = QString::fromLatin1(QImageReader::imageFormat(&buffer).toLower());
193     if (imageFormat.isEmpty())
194         return 0; // Image format not supported
195 
196     return new ImageDecoderQt(imageFormat);
197 }
198 
ImageDecoderQt(const QString & imageFormat)199 ImageDecoderQt::ImageDecoderQt(const QString &imageFormat)
200     : m_hasAlphaChannel(false)
201     , m_imageFormat(imageFormat)
202 {
203 }
204 
~ImageDecoderQt()205 ImageDecoderQt::~ImageDecoderQt()
206 {
207 }
208 
hasFirstImageHeader() const209 bool ImageDecoderQt::hasFirstImageHeader() const
210 {
211     return  !m_imageList.empty() && m_imageList[0].m_imageState >= ImageHeaderValid;
212 }
213 
reset()214 void ImageDecoderQt::reset()
215 {
216     m_hasAlphaChannel = false;
217     m_failed = false;
218     m_imageList.clear();
219     m_pixmapCache.clear();
220     m_loopCount = cAnimationNone;
221 }
222 
setData(const IncomingData & data,bool allDataReceived)223 void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived)
224 {
225     reset();
226     ReadContext readContext(data, ReadContext::LoadComplete, m_imageList);
227 
228     if (debugImageDecoderQt)
229         qDebug() << " setData " << data.size() << " image bytes, complete=" << allDataReceived;
230 
231     const  ReadContext::ReadResult readResult =  readContext.read(allDataReceived);
232 
233     if (hasFirstImageHeader())
234         m_hasAlphaChannel = m_imageList[0].m_image.hasAlphaChannel();
235 
236     if (debugImageDecoderQt)
237         qDebug()  << " read returns " << readResult;
238 
239     switch (readResult) {
240     case ReadContext::ReadFailed:
241         m_failed = true;
242         break;
243     case ReadContext::ReadEOF:
244     case ReadContext::ReadPartial:
245     case ReadContext::ReadComplete:
246         // Did we read anything - try to set the size.
247         if (hasFirstImageHeader()) {
248             QSize imgSize = m_imageList[0].m_image.size();
249             setSize(imgSize.width(), imgSize.height());
250 
251             if (readContext.reader()->supportsAnimation()) {
252                 if (readContext.reader()->loopCount() != -1)
253                     m_loopCount = readContext.reader()->loopCount();
254                 else
255                     m_loopCount = 0; //loop forever
256             }
257         }
258         break;
259     }
260 }
261 
262 
isSizeAvailable()263 bool ImageDecoderQt::isSizeAvailable()
264 {
265     if (debugImageDecoderQt)
266         qDebug() << " ImageDecoderQt::isSizeAvailable() returns" << ImageDecoder::isSizeAvailable();
267     return ImageDecoder::isSizeAvailable();
268 }
269 
frameCount() const270 size_t ImageDecoderQt::frameCount() const
271 {
272     if (debugImageDecoderQt)
273         qDebug() << " ImageDecoderQt::frameCount() returns" << m_imageList.size();
274     return m_imageList.size();
275 }
276 
repetitionCount() const277 int ImageDecoderQt::repetitionCount() const
278 {
279     if (debugImageDecoderQt)
280         qDebug() << " ImageDecoderQt::repetitionCount() returns" << m_loopCount;
281     return m_loopCount;
282 }
283 
supportsAlpha() const284 bool ImageDecoderQt::supportsAlpha() const
285 {
286     return m_hasAlphaChannel;
287 }
288 
duration(size_t index) const289 int ImageDecoderQt::duration(size_t index) const
290 {
291     if (index >= m_imageList.size())
292         return 0;
293     return  m_imageList[index].m_duration;
294 }
295 
filenameExtension() const296 String ImageDecoderQt::filenameExtension() const
297 {
298     if (debugImageDecoderQt)
299            qDebug() << " ImageDecoderQt::filenameExtension() returns" << m_imageFormat;
300     return m_imageFormat;
301 };
302 
frameBufferAtIndex(size_t index)303 RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index)
304 {
305     Q_ASSERT("use imageAtIndex instead");
306     return 0;
307 }
308 
imageAtIndex(size_t index) const309 QPixmap* ImageDecoderQt::imageAtIndex(size_t index) const
310 {
311     if (debugImageDecoderQt)
312         qDebug() << "ImageDecoderQt::imageAtIndex(" << index << ')';
313 
314     if (index >= m_imageList.size())
315         return 0;
316 
317     if (!m_pixmapCache.contains(index)) {
318         m_pixmapCache.insert(index,
319                              QPixmap::fromImage(m_imageList[index].m_image));
320 
321         // store null image since the converted pixmap is already in pixmap cache
322         Q_ASSERT(m_imageList[index].m_imageState == ImageComplete);
323         m_imageList[index].m_image = QImage();
324     }
325     return  &m_pixmapCache[index];
326 }
327 
clearFrame(size_t index)328 void ImageDecoderQt::clearFrame(size_t index)
329 {
330     if (m_imageList.size() < (int)index)
331         m_imageList[index].m_image = QImage();
332     m_pixmapCache.take(index);
333 }
334 
335 }
336 
337 // vim: ts=4 sw=4 et
338