ImageDecoderQt.cpp [plain text]
#include "config.h"
#include "ImageDecoderQt.h"
#include <QtCore/QByteArray>
#include <QtCore/QBuffer>
#include <QtGui/QImageReader>
#include <qdebug.h>
namespace {
const QImage::Format DesiredFormat = QImage::Format_ARGB32;
const bool debugImageDecoderQt = false;
}
namespace WebCore {
ImageDecoderQt::ImageData::ImageData(const QImage& image, ImageState imageState, int duration) :
m_image(image), m_imageState(imageState), m_duration(duration)
{
}
class ImageDecoderQt::ReadContext {
public:
enum LoadMode {
LoadIncrementally,
LoadComplete };
ReadContext(const IncomingData & data, LoadMode loadMode, ImageList &target);
enum ReadResult { ReadEOF, ReadFailed, ReadPartial, ReadComplete };
ReadResult read(bool allDataReceived);
QImageReader *reader() { return &m_reader; }
private:
enum IncrementalReadResult { IncrementalReadFailed, IncrementalReadPartial, IncrementalReadComplete };
IncrementalReadResult readImageLines(ImageData &);
const LoadMode m_loadMode;
QByteArray m_data;
QBuffer m_buffer;
QImageReader m_reader;
ImageList &m_target;
enum QImage::Format m_dataFormat;
QSize m_size;
};
ImageDecoderQt::ReadContext::ReadContext(const IncomingData & data, LoadMode loadMode, ImageList &target)
: m_loadMode(loadMode)
, m_data(data.data(), data.size())
, m_buffer(&m_data)
, m_reader(&m_buffer)
, m_target(target)
, m_dataFormat(QImage::Format_Invalid)
{
m_buffer.open(QIODevice::ReadOnly);
}
ImageDecoderQt::ReadContext::ReadResult
ImageDecoderQt::ReadContext::read(bool allDataReceived)
{
if (m_loadMode == LoadComplete && !allDataReceived)
return ReadPartial;
while (true) {
if (m_target.empty() || m_target.back().m_imageState == ImageComplete) {
if (!m_reader.canRead())
return ReadEOF;
QImage newImage = m_dataFormat != QImage::Format_Invalid ?
QImage(m_size,m_dataFormat) : QImage();
m_target.push_back(ImageData(newImage));
}
switch (readImageLines(m_target.back())) {
case IncrementalReadFailed:
m_target.pop_back();
return ReadFailed;
case IncrementalReadPartial:
return ReadPartial;
case IncrementalReadComplete:
m_target.back().m_imageState = ImageComplete;
m_dataFormat = m_target.back().m_image.format();
m_size = m_target.back().m_image.size();
const bool supportsAnimation = m_reader.supportsAnimation();
if (debugImageDecoderQt)
qDebug() << "readImage(): #" << m_target.size() << " complete, " << m_size << " format " << m_dataFormat
<< " supportsAnimation=" << supportsAnimation ;
if (!supportsAnimation)
return ReadComplete;
break;
}
}
return ReadComplete;
}
ImageDecoderQt::ReadContext::IncrementalReadResult
ImageDecoderQt::ReadContext::readImageLines(ImageData &imageData)
{
const qint64 startPos = m_buffer.pos ();
if (!m_reader.read(&imageData.m_image)) {
m_buffer.seek(startPos);
const bool gotHeader = imageData.m_image.size().width();
if (debugImageDecoderQt)
qDebug() << "readImageLines(): read() failed: " << m_reader.errorString()
<< " got header=" << gotHeader;
if (gotHeader) {
imageData.m_imageState = ImageHeaderValid;
return IncrementalReadPartial;
}
return IncrementalReadFailed;
}
imageData.m_duration = m_reader.nextImageDelay();
return IncrementalReadComplete;
}
ImageDecoderQt* ImageDecoderQt::create(const SharedBuffer& data)
{
if (data.size() < 4)
return 0;
QByteArray bytes = QByteArray::fromRawData(data.data(), data.size());
QBuffer buffer(&bytes);
if (!buffer.open(QBuffer::ReadOnly))
return 0;
QString imageFormat = QString::fromLatin1(QImageReader::imageFormat(&buffer).toLower());
if (imageFormat.isEmpty())
return 0;
return new ImageDecoderQt(imageFormat);
}
ImageDecoderQt::ImageDecoderQt(const QString &imageFormat)
: m_hasAlphaChannel(false)
, m_imageFormat(imageFormat)
{
}
ImageDecoderQt::~ImageDecoderQt()
{
}
bool ImageDecoderQt::hasFirstImageHeader() const
{
return !m_imageList.empty() && m_imageList[0].m_imageState >= ImageHeaderValid;
}
void ImageDecoderQt::reset()
{
m_hasAlphaChannel = false;
m_failed = false;
m_imageList.clear();
m_pixmapCache.clear();
m_sizeAvailable = false;
m_loopCount = cAnimationNone;
m_size = IntSize(-1, -1);
}
void ImageDecoderQt::setData(const IncomingData &data, bool allDataReceived)
{
reset();
ReadContext readContext(data, ReadContext::LoadComplete, m_imageList);
if (debugImageDecoderQt)
qDebug() << " setData " << data.size() << " image bytes, complete=" << allDataReceived;
const ReadContext::ReadResult readResult = readContext.read(allDataReceived);
if (hasFirstImageHeader())
m_hasAlphaChannel = m_imageList[0].m_image.hasAlphaChannel();
if (debugImageDecoderQt)
qDebug() << " read returns " << readResult;
switch ( readResult) {
case ReadContext::ReadFailed:
m_failed = true;
break;
case ReadContext::ReadEOF:
case ReadContext::ReadPartial:
case ReadContext::ReadComplete:
if (hasFirstImageHeader()) {
m_sizeAvailable = true;
m_size = m_imageList[0].m_image.size();
if (readContext.reader()->supportsAnimation()) {
if (readContext.reader()->loopCount() != -1)
m_loopCount = readContext.reader()->loopCount();
else
m_loopCount = 0; }
}
break;
}
}
bool ImageDecoderQt::isSizeAvailable() const
{
if (debugImageDecoderQt)
qDebug() << " ImageDecoderQt::isSizeAvailable() returns" << m_sizeAvailable;
return m_sizeAvailable;
}
int ImageDecoderQt::frameCount() const
{
if (debugImageDecoderQt)
qDebug() << " ImageDecoderQt::frameCount() returns" << m_imageList.size();
return m_imageList.size();
}
int ImageDecoderQt::repetitionCount() const
{
if (debugImageDecoderQt)
qDebug() << " ImageDecoderQt::repetitionCount() returns" << m_loopCount;
return m_loopCount;
}
bool ImageDecoderQt::supportsAlpha() const
{
return m_hasAlphaChannel;
}
int ImageDecoderQt::duration(size_t index) const
{
if (index >= m_imageList.size())
return 0;
return m_imageList[index].m_duration;
}
String ImageDecoderQt::filenameExtension() const
{
if (debugImageDecoderQt)
qDebug() << " ImageDecoderQt::filenameExtension() returns" << m_imageFormat;
return m_imageFormat;
};
RGBA32Buffer* ImageDecoderQt::frameBufferAtIndex(size_t index)
{
Q_ASSERT("use imageAtIndex instead");
return 0;
}
QPixmap* ImageDecoderQt::imageAtIndex(size_t index) const
{
if (debugImageDecoderQt)
qDebug() << "ImageDecoderQt::imageAtIndex(" << index << ')';
if (index >= m_imageList.size())
return 0;
if (!m_pixmapCache.contains(index)) {
m_pixmapCache.insert(index,
QPixmap::fromImage(m_imageList[index].m_image));
Q_ASSERT(m_imageList[index].m_imageState == ImageComplete);
m_imageList[index].m_image = QImage();
}
return &m_pixmapCache[index];
}
void ImageDecoderQt::clearFrame(size_t index)
{
if (m_imageList.size() < (int)index)
m_imageList[index].m_image = QImage();
m_pixmapCache.take(index);
}
}