Blob Blame History Raw
From c8faa098b93e9b893daedf4e9a8c602500f969f3 Mon Sep 17 00:00:00 2001
From: Eirik Aavitsland <eirik.aavitsland@qt.io>
Date: Thu, 8 Sep 2022 14:52:19 +0200
Subject: [PATCH 02/12] webp: support sequential input device if full file is
 available

Since we do no random access during decoding, just a readAll() of the
whole image file. So if it is all available already, we can handle a
sequential device. That is useful for Quick AnimationImage, which will
pass a finished QNetworkReply as the input device.

This commit removes some seek() calls in the header checking, that
supposedly should reset the device position. These were in practice
either no-ops or bugs, since the device is only being peeked, so the
position never changes in the first place, and a QImageIOHandler is
supposed to read from the device at the position it is at when passed.

Fixes: QTBUG-70245
Change-Id: I5a4ff5fa4bbd19b0545ad41645969d714b4dc7d5
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>


(cherry picked from commit 369be99d82a7c1182e3693756ab545cea86bb90d)
---
 .../imageformats/webp/qwebphandler.cpp        | 29 +++++++++----------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/src/plugins/imageformats/webp/qwebphandler.cpp b/src/plugins/imageformats/webp/qwebphandler.cpp
index 82d38cb..d02eb05 100644
--- a/src/plugins/imageformats/webp/qwebphandler.cpp
+++ b/src/plugins/imageformats/webp/qwebphandler.cpp
@@ -45,6 +45,7 @@
 #include <qdebug.h>
 #include <qpainter.h>
 #include <qvariant.h>
+#include <QtEndian>
 
 static const int riffHeaderSize = 12; // RIFF_HEADER_SIZE from webp/format_constants.h
 
@@ -102,21 +103,23 @@ bool QWebpHandler::ensureScanned() const
 
     m_scanState = ScanError;
 
-    if (device()->isSequential()) {
-        qWarning() << "Sequential devices are not supported";
+    QWebpHandler *that = const_cast<QWebpHandler *>(this);
+    const int headerBytesNeeded = sizeof(WebPBitstreamFeatures);
+    QByteArray header = device()->peek(headerBytesNeeded);
+    if (header.size() < headerBytesNeeded)
         return false;
-    }
 
-    qint64 oldPos = device()->pos();
-    device()->seek(0);
-
-    QWebpHandler *that = const_cast<QWebpHandler *>(this);
-    QByteArray header = device()->peek(sizeof(WebPBitstreamFeatures));
+    // We do no random access during decoding, just a readAll() of the whole image file. So if
+    // if it is all available already, we can accept a sequential device. The riff header contains
+    // the file size minus 8 bytes header
+    qint64 byteSize = qFromLittleEndian<quint32>(header.constData() + 4);
+    if (device()->isSequential() && device()->bytesAvailable() < byteSize + 8) {
+        qWarning() << "QWebpHandler: Insufficient data available in sequential device";
+        return false;
+    }
     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);
@@ -126,17 +129,13 @@ bool QWebpHandler::ensureScanned() const
                 if (that->m_features.has_alpha)
                     that->m_composited->fill(Qt::transparent);
 
-                // 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;
 }
 
@@ -159,7 +158,7 @@ bool QWebpHandler::ensureDemuxer()
 
 bool QWebpHandler::read(QImage *image)
 {
-    if (!ensureScanned() || device()->isSequential() || !ensureDemuxer())
+    if (!ensureScanned() || !ensureDemuxer())
         return false;
 
     QRect prevFrameRect;
-- 
2.41.0