avif: read performance improvements

This commit is contained in:
Daniel Novomeský 2022-06-20 18:50:03 +02:00
parent 2aec1d3926
commit cf375a207f
2 changed files with 113 additions and 61 deletions

View File

@ -67,7 +67,7 @@ bool QAVIFHandler::canRead(QIODevice *device)
bool QAVIFHandler::ensureParsed() const bool QAVIFHandler::ensureParsed() const
{ {
if (m_parseState == ParseAvifSuccess) { if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata) {
return true; return true;
} }
if (m_parseState == ParseAvifError) { if (m_parseState == ParseAvifError) {
@ -79,6 +79,28 @@ bool QAVIFHandler::ensureParsed() const
return that->ensureDecoder(); return that->ensureDecoder();
} }
bool QAVIFHandler::ensureOpened() const
{
if (m_parseState == ParseAvifSuccess) {
return true;
}
if (m_parseState == ParseAvifError) {
return false;
}
QAVIFHandler *that = const_cast<QAVIFHandler *>(this);
if (ensureParsed()) {
if (m_parseState == ParseAvifMetadata) {
bool success = that->jumpToNextImage();
that->m_parseState = success ? ParseAvifSuccess : ParseAvifError;
return success;
}
}
that->m_parseState = ParseAvifError;
return false;
}
bool QAVIFHandler::ensureDecoder() bool QAVIFHandler::ensureDecoder()
{ {
if (m_decoder) { if (m_decoder) {
@ -97,6 +119,9 @@ bool QAVIFHandler::ensureDecoder()
m_decoder = avifDecoderCreate(); m_decoder = avifDecoderCreate();
m_decoder->ignoreExif = AVIF_TRUE;
m_decoder->ignoreXMP = AVIF_TRUE;
#if AVIF_VERSION >= 80400 #if AVIF_VERSION >= 80400
m_decoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64); m_decoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
#endif #endif
@ -127,9 +152,6 @@ bool QAVIFHandler::ensureDecoder()
return false; return false;
} }
decodeResult = avifDecoderNextImage(m_decoder);
if (decodeResult == AVIF_RESULT_OK) {
m_container_width = m_decoder->image->width; m_container_width = m_decoder->image->width;
m_container_height = m_decoder->image->height; m_container_height = m_decoder->image->height;
@ -151,21 +173,37 @@ bool QAVIFHandler::ensureDecoder()
return false; return false;
} }
m_parseState = ParseAvifSuccess; // calculate final dimensions with crop and rotate operations applied
if (decode_one_frame()) { int new_width = m_container_width;
return true; int new_height = m_container_height;
} else {
m_parseState = ParseAvifError; if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
return false; if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
&& (m_decoder->image->clap.vertOffD > 0)) {
int crop_width = (int)((double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
if (crop_width < new_width && crop_width > 0) {
new_width = crop_width;
}
int crop_height = (int)((double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
if (crop_height < new_height && crop_height > 0) {
new_height = crop_height;
}
} }
} else {
qWarning("ERROR: Failed to decode image: %s", avifResultToString(decodeResult));
} }
avifDecoderDestroy(m_decoder); if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
m_decoder = nullptr; if (m_decoder->image->irot.angle == 1 || m_decoder->image->irot.angle == 3) {
m_parseState = ParseAvifError; int tmp = new_width;
return false; new_width = new_height;
new_height = tmp;
}
}
m_estimated_dimensions.setWidth(new_width);
m_estimated_dimensions.setHeight(new_height);
m_parseState = ParseAvifMetadata;
return true;
} }
bool QAVIFHandler::decode_one_frame() bool QAVIFHandler::decode_one_frame()
@ -192,9 +230,9 @@ bool QAVIFHandler::decode_one_frame()
} }
} else { } else {
if (loadalpha) { if (loadalpha) {
resultformat = QImage::Format_RGBA8888; resultformat = QImage::Format_ARGB32;
} else { } else {
resultformat = QImage::Format_RGBX8888; resultformat = QImage::Format_RGB32;
} }
} }
QImage result(m_decoder->image->width, m_decoder->image->height, resultformat); QImage result(m_decoder->image->width, m_decoder->image->height, resultformat);
@ -285,14 +323,16 @@ bool QAVIFHandler::decode_one_frame()
rgb.depth = 16; rgb.depth = 16;
rgb.format = AVIF_RGB_FORMAT_RGBA; rgb.format = AVIF_RGB_FORMAT_RGBA;
if (!loadalpha) { if (!loadalpha && (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) {
if (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
resultformat = QImage::Format_Grayscale16; resultformat = QImage::Format_Grayscale16;
} }
}
} else { } else {
rgb.depth = 8; rgb.depth = 8;
rgb.format = AVIF_RGB_FORMAT_RGBA; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
rgb.format = AVIF_RGB_FORMAT_BGRA;
#else
rgb.format = AVIF_RGB_FORMAT_ARGB;
#endif
#if AVIF_VERSION >= 80400 #if AVIF_VERSION >= 80400
if (m_decoder->imageCount > 1) { if (m_decoder->imageCount > 1) {
@ -301,14 +341,8 @@ bool QAVIFHandler::decode_one_frame()
} }
#endif #endif
if (loadalpha) { if (!loadalpha && (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) {
resultformat = QImage::Format_ARGB32;
} else {
if (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
resultformat = QImage::Format_Grayscale8; resultformat = QImage::Format_Grayscale8;
} else {
resultformat = QImage::Format_RGB32;
}
} }
} }
@ -399,13 +433,15 @@ bool QAVIFHandler::decode_one_frame()
m_current_image = result.convertToFormat(resultformat); m_current_image = result.convertToFormat(resultformat);
} }
m_estimated_dimensions = m_current_image.size();
m_must_jump_to_next_image = false; m_must_jump_to_next_image = false;
return true; return true;
} }
bool QAVIFHandler::read(QImage *image) bool QAVIFHandler::read(QImage *image)
{ {
if (!ensureParsed()) { if (!ensureOpened()) {
return false; return false;
} }
@ -792,7 +828,7 @@ QVariant QAVIFHandler::option(ImageOption option) const
switch (option) { switch (option) {
case Size: case Size:
return m_current_image.size(); return m_estimated_dimensions;
case Animation: case Animation:
if (imageCount() >= 2) { if (imageCount() >= 2) {
return true; return true;
@ -848,6 +884,14 @@ int QAVIFHandler::currentImageNumber() const
return 0; return 0;
} }
if (m_parseState == ParseAvifMetadata) {
if (m_decoder->imageCount >= 2) {
return -1;
} else {
return 0;
}
}
return m_decoder->imageIndex; return m_decoder->imageIndex;
} }
@ -857,6 +901,7 @@ bool QAVIFHandler::jumpToNextImage()
return false; return false;
} }
if (m_decoder->imageIndex >= 0) {
if (m_decoder->imageCount < 2) { if (m_decoder->imageCount < 2) {
return true; return true;
} }
@ -864,6 +909,7 @@ bool QAVIFHandler::jumpToNextImage()
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
avifDecoderReset(m_decoder); avifDecoderReset(m_decoder);
} }
}
avifResult decodeResult = avifDecoderNextImage(m_decoder); avifResult decodeResult = avifDecoderNextImage(m_decoder);
@ -885,6 +931,7 @@ bool QAVIFHandler::jumpToNextImage()
} }
if (decode_one_frame()) { if (decode_one_frame()) {
m_parseState = ParseAvifSuccess;
return true; return true;
} else { } else {
m_parseState = ParseAvifError; m_parseState = ParseAvifError;
@ -900,7 +947,7 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
if (m_decoder->imageCount < 2) { // not an animation if (m_decoder->imageCount < 2) { // not an animation
if (imageNumber == 0) { if (imageNumber == 0) {
return true; return ensureOpened();
} else { } else {
return false; return false;
} }
@ -935,6 +982,7 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
} }
if (decode_one_frame()) { if (decode_one_frame()) {
m_parseState = ParseAvifSuccess;
return true; return true;
} else { } else {
m_parseState = ParseAvifError; m_parseState = ParseAvifError;
@ -944,7 +992,7 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
int QAVIFHandler::nextImageDelay() const int QAVIFHandler::nextImageDelay() const
{ {
if (!ensureParsed()) { if (!ensureOpened()) {
return 0; return 0;
} }

View File

@ -13,6 +13,7 @@
#include <QImage> #include <QImage>
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QPointF> #include <QPointF>
#include <QSize>
#include <QVariant> #include <QVariant>
#include <avif/avif.h> #include <avif/avif.h>
#include <qimageiohandler.h> #include <qimageiohandler.h>
@ -45,6 +46,7 @@ public:
private: private:
static QPointF CompatibleChromacity(qreal chrX, qreal chrY); static QPointF CompatibleChromacity(qreal chrX, qreal chrY);
bool ensureParsed() const; bool ensureParsed() const;
bool ensureOpened() const;
bool ensureDecoder(); bool ensureDecoder();
bool decode_one_frame(); bool decode_one_frame();
@ -52,6 +54,7 @@ private:
ParseAvifError = -1, ParseAvifError = -1,
ParseAvifNotParsed = 0, ParseAvifNotParsed = 0,
ParseAvifSuccess = 1, ParseAvifSuccess = 1,
ParseAvifMetadata = 2,
}; };
ParseAvifState m_parseState; ParseAvifState m_parseState;
@ -59,6 +62,7 @@ private:
uint32_t m_container_width; uint32_t m_container_width;
uint32_t m_container_height; uint32_t m_container_height;
QSize m_estimated_dimensions;
QByteArray m_rawData; QByteArray m_rawData;
avifROData m_rawAvifData; avifROData m_rawAvifData;