Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:42.3:Rings:1-MinimalX
libqt5-qtimageformats
Add-animation-support-to-WebP-plugin.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File Add-animation-support-to-WebP-plugin.patch of Package libqt5-qtimageformats
From 1d4f24820c0fff474d524e006d715e13e409a4b8 Mon Sep 17 00:00:00 2001 From: Dayang Shen <Archangel.SDY@gmail.com> Date: Sat, 5 Mar 2016 15:40:30 +0800 Subject: 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> --- config.tests/libwebp/libwebp.cpp | 5 + config.tests/libwebp/libwebp.pro | 4 +- src/plugins/imageformats/webp/qwebphandler.cpp | 152 +++++++++++++++++++++++-- src/plugins/imageformats/webp/qwebphandler_p.h | 20 +++- tests/auto/webp/images/kollada_animation.webp | Bin 0 -> 24726 bytes tests/auto/webp/tst_qwebp.cpp | 60 ++++++++++ tests/auto/webp/webp.qrc | 1 + 7 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 tests/auto/webp/images/kollada_animation.webp diff --git a/config.tests/libwebp/libwebp.cpp b/config.tests/libwebp/libwebp.cpp index f021a17..720b72b 100644 --- a/config.tests/libwebp/libwebp.cpp +++ b/config.tests/libwebp/libwebp.cpp @@ -28,6 +28,7 @@ #include <webp/decode.h> #include <webp/encode.h> +#include <webp/demux.h> #if WEBP_ABI_IS_INCOMPATIBLE(WEBP_DECODER_ABI_VERSION, 0x0203) || WEBP_ABI_IS_INCOMPATIBLE(WEBP_ENCODER_ABI_VERSION, 0x0202) #error "Incompatible libwebp version" @@ -42,6 +43,10 @@ int main(int, char **) picture.use_argb = 0; WebPConfig config2; config2.lossless = 0; + WebPData data = {}; + WebPDemuxer *demuxer = WebPDemux(&data); + WebPIterator iter; + iter.frame_num = 0; return 0; } diff --git a/config.tests/libwebp/libwebp.pro b/config.tests/libwebp/libwebp.pro index d69b9be..bcbedf8 100644 --- a/config.tests/libwebp/libwebp.pro +++ b/config.tests/libwebp/libwebp.pro @@ -2,5 +2,5 @@ SOURCES = libwebp.cpp CONFIG -= qt dylib mac:CONFIG -= app_bundle win32:CONFIG += console -unix|mingw: LIBS += -lwebp -else:win32: LIBS += libwebp.lib +unix|mingw: LIBS += -lwebp -lwebpdemux +else:win32: LIBS += libwebp.lib libwebpdemux.lib 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 diff --git a/tests/auto/webp/images/kollada_animation.webp b/tests/auto/webp/images/kollada_animation.webp new file mode 100644 index 0000000..c38751a Binary files /dev/null and b/tests/auto/webp/images/kollada_animation.webp differ diff --git a/tests/auto/webp/tst_qwebp.cpp b/tests/auto/webp/tst_qwebp.cpp index 4126076..d1d30db 100644 --- a/tests/auto/webp/tst_qwebp.cpp +++ b/tests/auto/webp/tst_qwebp.cpp @@ -37,6 +37,8 @@ private slots: void initTestCase(); void readImage_data(); void readImage(); + void readAnimation_data(); + void readAnimation(); void writeImage_data(); void writeImage(); }; @@ -69,6 +71,64 @@ void tst_qwebp::readImage() QCOMPARE(image.size(), size); } +void tst_qwebp::readAnimation_data() +{ + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QSize>("size"); + QTest::addColumn<int>("imageCount"); + QTest::addColumn<int>("loopCount"); + QTest::addColumn<QColor>("bgColor"); + QTest::addColumn<QList<QRect> >("imageRects"); + QTest::addColumn<QList<int> >("imageDelays"); + + QTest::newRow("kollada") + << QString("kollada") + << QSize(436, 160) + << 1 + << 0 + << QColor() + << (QList<QRect>() << QRect(0, 0, 436, 160)) + << (QList<int>() << 0); + QTest::newRow("kollada_animation") + << QString("kollada_animation") + << QSize(536, 260) + << 2 + << 2 + << QColor(128, 128, 128, 128) + << (QList<QRect>() << QRect(0, 0, 436, 160) << QRect(100, 100, 436, 160)) + << (QList<int>() << 1000 << 1200); +} + +void tst_qwebp::readAnimation() +{ + QFETCH(QString, fileName); + QFETCH(QSize, size); + QFETCH(int, imageCount); + QFETCH(int, loopCount); + QFETCH(QColor, bgColor); + QFETCH(QList<QRect>, imageRects); + QFETCH(QList<int>, imageDelays); + + const QString path = QStringLiteral(":/images/") + fileName + QStringLiteral(".webp"); + QImageReader reader(path); + QVERIFY(reader.canRead()); + QCOMPARE(reader.size(), size); + QCOMPARE(reader.imageCount(), imageCount); + QCOMPARE(reader.loopCount(), loopCount); + QCOMPARE(reader.backgroundColor(), bgColor); + + for (int i = 0; i < reader.imageCount(); ++i) { + QImage image = reader.read(); + QVERIFY2(!image.isNull(), qPrintable(reader.errorString())); + QCOMPARE(image.size(), size); + QCOMPARE(reader.currentImageNumber(), i); + QCOMPARE(reader.currentImageRect(), imageRects[i]); + QCOMPARE(reader.nextImageDelay(), imageDelays[i]); + } + + QVERIFY(reader.read().isNull()); +} + void tst_qwebp::writeImage_data() { QTest::addColumn<QString>("fileName"); diff --git a/tests/auto/webp/webp.qrc b/tests/auto/webp/webp.qrc index ab0b1b2..6519e58 100644 --- a/tests/auto/webp/webp.qrc +++ b/tests/auto/webp/webp.qrc @@ -3,5 +3,6 @@ <file>images/kollada.png</file> <file>images/kollada.webp</file> <file>images/kollada_lossless.webp</file> + <file>images/kollada_animation.webp</file> </qresource> </RCC> -- cgit v1.0-4-g1e03
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor