summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDayang Shen <Archangel.SDY@gmail.com>2016-03-05 15:40:30 +0800
committerLiang Qi <liang.qi@theqtcompany.com>2016-03-10 14:38:14 +0000
commit1d4f24820c0fff474d524e006d715e13e409a4b8 (patch)
tree903ca840e78e32504699ddb24d535ed25d748796 /src
parent35b4108a4e2090a856226ac15b796e118655fa8b (diff)
Add animation support to WebP plugin
We now use WebP Demux API to decode both single image format and muxed animation format. Change-Id: Ia2922892a3a626e9921c3910801d7c975d9fc6a2 Reviewed-by: aavit <eirik.aavitsland@theqtcompany.com> Reviewed-by: Liang Qi <liang.qi@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/imageformats/webp/qwebphandler.cpp152
-rw-r--r--src/plugins/imageformats/webp/qwebphandler_p.h20
2 files changed, 159 insertions, 13 deletions
diff --git a/src/plugins/imageformats/webp/qwebphandler.cpp b/src/plugins/imageformats/webp/qwebphandler.cpp
index 636c4d8..a59e6bd 100644
--- a/src/plugins/imageformats/webp/qwebphandler.cpp
+++ b/src/plugins/imageformats/webp/qwebphandler.cpp
@@ -39,8 +39,10 @@
#include "qwebphandler_p.h"
#include "webp/encode.h"
+#include <qcolor.h>
#include <qimage.h>
#include <qdebug.h>
+#include <qpainter.h>
#include <qvariant.h>
static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h
@@ -48,8 +50,20 @@ static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_const
QWebpHandler::QWebpHandler() :
m_lossless(false),
m_quality(75),
- m_scanState(ScanNotScanned)
+ m_scanState(ScanNotScanned),
+ m_loop(0),
+ m_frameCount(0),
+ m_demuxer(NULL),
+ m_composited(NULL)
{
+ memset(&m_iter, 0, sizeof(m_iter));
+}
+
+QWebpHandler::~QWebpHandler()
+{
+ WebPDemuxReleaseIterator(&m_iter);
+ WebPDemuxDelete(m_demuxer);
+ delete m_composited;
}
bool QWebpHandler::canRead() const
@@ -92,31 +106,93 @@ bool QWebpHandler::ensureScanned() const
QWebpHandler *that = const_cast<QWebpHandler *>(this);
QByteArray header = device()->peek(sizeof(WebPBitstreamFeatures));
- if (WebPGetFeatures((const uint8_t*)header.constData(), header.size(), &(that->m_features)) == VP8_STATUS_OK)
- m_scanState = ScanSuccess;
+ if (WebPGetFeatures((const uint8_t*)header.constData(), header.size(), &(that->m_features)) == VP8_STATUS_OK) {
+ if (m_features.has_animation) {
+ // For animation, we have to read and scan whole file to determine loop count and images count
+ device()->seek(oldPos);
+
+ if (that->ensureDemuxer()) {
+ that->m_loop = WebPDemuxGetI(m_demuxer, WEBP_FF_LOOP_COUNT);
+ that->m_frameCount = WebPDemuxGetI(m_demuxer, WEBP_FF_FRAME_COUNT);
+ that->m_bgColor = QColor::fromRgba(QRgb(WebPDemuxGetI(m_demuxer, WEBP_FF_BACKGROUND_COLOR)));
+
+ that->m_composited = new QImage(that->m_features.width, that->m_features.height, QImage::Format_ARGB32);
+
+ // We do not reset device position since we have read in all data
+ m_scanState = ScanSuccess;
+ return true;
+ }
+ } else {
+ m_scanState = ScanSuccess;
+ }
+ }
device()->seek(oldPos);
return m_scanState == ScanSuccess;
}
+bool QWebpHandler::ensureDemuxer()
+{
+ if (m_demuxer)
+ return true;
+
+ m_rawData = device()->readAll();
+ m_webpData.bytes = reinterpret_cast<const uint8_t *>(m_rawData.constData());
+ m_webpData.size = m_rawData.size();
+
+ m_demuxer = WebPDemux(&m_webpData);
+ if (m_demuxer == NULL)
+ return false;
+
+ return true;
+}
+
bool QWebpHandler::read(QImage *image)
{
- if (!ensureScanned() || device()->isSequential())
+ if (!ensureScanned() || device()->isSequential() || !ensureDemuxer())
+ return false;
+
+ if (m_iter.frame_num == 0) {
+ // Go to first frame
+ if (!WebPDemuxGetFrame(m_demuxer, 1, &m_iter))
+ return false;
+ } else {
+ // Go to next frame
+ if (!WebPDemuxNextFrame(&m_iter))
+ return false;
+ }
+
+ WebPBitstreamFeatures features;
+ VP8StatusCode status = WebPGetFeatures(m_iter.fragment.bytes, m_iter.fragment.size, &features);
+ if (status != VP8_STATUS_OK)
return false;
- QByteArray data = device()->readAll();
- QImage result(m_features.width, m_features.height, QImage::Format_ARGB32);
- uint8_t *output = result.bits();
- size_t output_size = result.byteCount();
+ QImage frame(m_iter.width, m_iter.height, QImage::Format_ARGB32);
+ uint8_t *output = frame.bits();
+ size_t output_size = frame.byteCount();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- if (!WebPDecodeBGRAInto(reinterpret_cast<const uint8_t*>(data.constData()), data.size(), output, output_size, result.bytesPerLine()))
+ if (!WebPDecodeBGRAInto(
+ reinterpret_cast<const uint8_t*>(m_iter.fragment.bytes), m_iter.fragment.size,
+ output, output_size, frame.bytesPerLine()))
#else
- if (!WebPDecodeARGBInto(reinterpret_cast<const uint8_t*>(data.constData()), data.size(), output, output_size, result.bytesPerLine()))
+ if (!WebPDecodeARGBInto(
+ reinterpret_cast<const uint8_t*>(m_iter.fragment.bytes), m_iter.fragment.size,
+ output, output_size, frame.bytesPerLine()))
#endif
return false;
- *image = result;
+ if (!m_features.has_animation) {
+ // Single image
+ *image = frame;
+ } else {
+ // Animation
+ QPainter painter(m_composited);
+ painter.drawImage(currentImageRect(), frame);
+
+ *image = *m_composited;
+ }
+
return true;
}
@@ -191,6 +267,10 @@ QVariant QWebpHandler::option(ImageOption option) const
return m_quality;
case Size:
return QSize(m_features.width, m_features.height);
+ case Animation:
+ return m_features.has_animation;
+ case BackgroundColor:
+ return m_bgColor;
default:
return QVariant();
}
@@ -211,10 +291,58 @@ void QWebpHandler::setOption(ImageOption option, const QVariant &value)
bool QWebpHandler::supportsOption(ImageOption option) const
{
- return option == Quality || option == Size;
+ return option == Quality
+ || option == Size
+ || option == Animation
+ || option == BackgroundColor;
}
QByteArray QWebpHandler::name() const
{
return QByteArrayLiteral("webp");
}
+
+int QWebpHandler::imageCount() const
+{
+ if (!ensureScanned())
+ return 0;
+
+ if (!m_features.has_animation)
+ return 1;
+
+ return m_frameCount;
+}
+
+int QWebpHandler::currentImageNumber() const
+{
+ if (!ensureScanned() || !m_features.has_animation)
+ return 0;
+
+ // Frame number in WebP starts from 1
+ return m_iter.frame_num - 1;
+}
+
+QRect QWebpHandler::currentImageRect() const
+{
+ if (!ensureScanned())
+ return QRect();
+
+ return QRect(m_iter.x_offset, m_iter.y_offset, m_iter.width, m_iter.height);
+}
+
+int QWebpHandler::loopCount() const
+{
+ if (!ensureScanned() || !m_features.has_animation)
+ return 0;
+
+ // Loop count in WebP starts from 0
+ return m_loop - 1;
+}
+
+int QWebpHandler::nextImageDelay() const
+{
+ if (!ensureScanned() || !m_features.has_animation)
+ return 0;
+
+ return m_iter.duration;
+}
diff --git a/src/plugins/imageformats/webp/qwebphandler_p.h b/src/plugins/imageformats/webp/qwebphandler_p.h
index 05e614a..36dfed7 100644
--- a/src/plugins/imageformats/webp/qwebphandler_p.h
+++ b/src/plugins/imageformats/webp/qwebphandler_p.h
@@ -40,17 +40,20 @@
#ifndef QWEBPHANDLER_P_H
#define QWEBPHANDLER_P_H
+#include <QtGui/qcolor.h>
+#include <QtGui/qimage.h>
#include <QtGui/qimageiohandler.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qsize.h>
#include "webp/decode.h"
+#include "webp/demux.h"
class QWebpHandler : public QImageIOHandler
{
public:
QWebpHandler();
- ~QWebpHandler() {}
+ ~QWebpHandler();
public:
QByteArray name() const;
@@ -65,8 +68,15 @@ public:
void setOption(ImageOption option, const QVariant &value);
bool supportsOption(ImageOption option) const;
+ int imageCount() const;
+ int currentImageNumber() const;
+ QRect currentImageRect() const;
+ int loopCount() const;
+ int nextImageDelay() const;
+
private:
bool ensureScanned() const;
+ bool ensureDemuxer();
private:
enum ScanState {
@@ -79,6 +89,14 @@ private:
int m_quality;
mutable ScanState m_scanState;
WebPBitstreamFeatures m_features;
+ int m_loop;
+ int m_frameCount;
+ QColor m_bgColor;
+ QByteArray m_rawData;
+ WebPData m_webpData;
+ WebPDemuxer *m_demuxer;
+ WebPIterator m_iter;
+ QImage *m_composited; // For animation frames composition
};
#endif // WEBPHANDLER_H