Check primaries returned from libavif

Due to various double vs float arithmetic,
some primaries could be rejected by Qt.
If necessary, we adjust the values so they
will be accepted by Qt.

Remove newline from the ends of error strings.
This commit is contained in:
Daniel Novomesky 2021-02-27 19:11:56 +01:00
parent ca52d4ddf5
commit 1462c3abd6
2 changed files with 35 additions and 19 deletions

View File

@ -12,6 +12,7 @@
#include <QColorSpace> #include <QColorSpace>
#include "avif_p.h" #include "avif_p.h"
#include <cfloat>
QAVIFHandler::QAVIFHandler() : QAVIFHandler::QAVIFHandler() :
@ -102,7 +103,7 @@ bool QAVIFHandler::ensureDecoder()
decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size); decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
if (decodeResult != AVIF_RESULT_OK) { if (decodeResult != AVIF_RESULT_OK) {
qWarning("ERROR: avifDecoderSetIOMemory failed: %s\n", avifResultToString(decodeResult)); qWarning("ERROR: avifDecoderSetIOMemory failed: %s", avifResultToString(decodeResult));
avifDecoderDestroy(m_decoder); avifDecoderDestroy(m_decoder);
m_decoder = nullptr; m_decoder = nullptr;
@ -112,7 +113,7 @@ bool QAVIFHandler::ensureDecoder()
decodeResult = avifDecoderParse(m_decoder); decodeResult = avifDecoderParse(m_decoder);
if (decodeResult != AVIF_RESULT_OK) { if (decodeResult != AVIF_RESULT_OK) {
qWarning("ERROR: Failed to parse input: %s\n", avifResultToString(decodeResult)); qWarning("ERROR: Failed to parse input: %s", avifResultToString(decodeResult));
avifDecoderDestroy(m_decoder); avifDecoderDestroy(m_decoder);
m_decoder = nullptr; m_decoder = nullptr;
@ -147,7 +148,7 @@ bool QAVIFHandler::ensureDecoder()
return false; return false;
} }
} else { } else {
qWarning("ERROR: Failed to decode image: %s\n", avifResultToString(decodeResult)); qWarning("ERROR: Failed to decode image: %s", avifResultToString(decodeResult));
} }
avifDecoderDestroy(m_decoder); avifDecoderDestroy(m_decoder);
@ -195,16 +196,17 @@ bool QAVIFHandler::decode_one_frame()
if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) { if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) {
result.setColorSpace(QColorSpace::fromIccProfile(QByteArray::fromRawData((const char *) m_decoder->image->icc.data, (int) m_decoder->image->icc.size))); result.setColorSpace(QColorSpace::fromIccProfile(QByteArray::fromRawData((const char *) m_decoder->image->icc.data, (int) m_decoder->image->icc.size)));
if (! result.colorSpace().isValid()) { if (! result.colorSpace().isValid()) {
qWarning("Invalid QColorSpace created from ICC!\n"); qWarning("Invalid QColorSpace created from ICC!");
} }
} else { } else {
float prim[8]; // outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY float prim[8] = { 0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f };
// outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY
avifColorPrimariesGetValues(m_decoder->image->colorPrimaries, prim); avifColorPrimariesGetValues(m_decoder->image->colorPrimaries, prim);
QPointF redPoint(prim[0], prim[1]); const QPointF redPoint(QAVIFHandler::CompatibleChromacity(prim[0], prim[1]));
QPointF greenPoint(prim[2], prim[3]); const QPointF greenPoint(QAVIFHandler::CompatibleChromacity(prim[2], prim[3]));
QPointF bluePoint(prim[4], prim[5]); const QPointF bluePoint(QAVIFHandler::CompatibleChromacity(prim[4], prim[5]));
QPointF whitePoint(prim[6], prim[7]); const QPointF whitePoint(QAVIFHandler::CompatibleChromacity(prim[6], prim[7]));
QColorSpace::TransferFunction q_trc = QColorSpace::TransferFunction::Custom; QColorSpace::TransferFunction q_trc = QColorSpace::TransferFunction::Custom;
@ -257,7 +259,7 @@ bool QAVIFHandler::decode_one_frame()
} }
if (! result.colorSpace().isValid()) { if (! result.colorSpace().isValid()) {
qWarning("Invalid QColorSpace created from NCLX/CICP!\n"); qWarning("Invalid QColorSpace created from NCLX/CICP!");
} }
} }
@ -296,7 +298,7 @@ bool QAVIFHandler::decode_one_frame()
avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb); avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb);
if (res != AVIF_RESULT_OK) { if (res != AVIF_RESULT_OK) {
qWarning("ERROR in avifImageYUVToRGB: %s\n", avifResultToString(res)); qWarning("ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
return false; return false;
} }
@ -338,7 +340,7 @@ bool QAVIFHandler::decode_one_frame()
} }
else { //Zero values, we need to avoid 0 divide. else { //Zero values, we need to avoid 0 divide.
qWarning("ERROR: Wrong values in avifCleanApertureBox\n"); qWarning("ERROR: Wrong values in avifCleanApertureBox");
} }
} }
@ -680,7 +682,7 @@ bool QAVIFHandler::write(const QImage &image)
res = avifImageRGBToYUV(avif, &rgb); res = avifImageRGBToYUV(avif, &rgb);
if (res != AVIF_RESULT_OK) { if (res != AVIF_RESULT_OK) {
qWarning("ERROR in avifImageRGBToYUV: %s\n", avifResultToString(res)); qWarning("ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
return false; return false;
} }
} }
@ -709,11 +711,11 @@ bool QAVIFHandler::write(const QImage &image)
if (status > 0) { if (status > 0) {
return true; return true;
} else if (status == -1) { } else if (status == -1) {
qWarning("Write error: %s\n", qUtf8Printable(device()->errorString())); qWarning("Write error: %s", qUtf8Printable(device()->errorString()));
return false; return false;
} }
} else { } else {
qWarning("ERROR: Failed to encode: %s\n", avifResultToString(res)); qWarning("ERROR: Failed to encode: %s", avifResultToString(res));
} }
return false; return false;
@ -810,14 +812,14 @@ bool QAVIFHandler::jumpToNextImage()
avifResult decodeResult = avifDecoderNextImage(m_decoder); avifResult decodeResult = avifDecoderNextImage(m_decoder);
if (decodeResult != AVIF_RESULT_OK) { if (decodeResult != AVIF_RESULT_OK) {
qWarning("ERROR: Failed to decode Next image in sequence: %s\n", avifResultToString(decodeResult)); qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
m_parseState = ParseAvifError; m_parseState = ParseAvifError;
return false; return false;
} }
if ((m_container_width != m_decoder->image->width) || if ((m_container_width != m_decoder->image->width) ||
(m_container_height != m_decoder->image->height)) { (m_container_height != m_decoder->image->height)) {
qWarning("Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!\n", qWarning("Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
m_decoder->image->width, m_decoder->image->height, m_decoder->image->width, m_decoder->image->height,
m_container_width, m_container_height); m_container_width, m_container_height);
@ -859,14 +861,14 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber); avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber);
if (decodeResult != AVIF_RESULT_OK) { if (decodeResult != AVIF_RESULT_OK) {
qWarning("ERROR: Failed to decode %d th Image in sequence: %s\n", imageNumber, avifResultToString(decodeResult)); qWarning("ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
m_parseState = ParseAvifError; m_parseState = ParseAvifError;
return false; return false;
} }
if ((m_container_width != m_decoder->image->width) || if ((m_container_width != m_decoder->image->width) ||
(m_container_height != m_decoder->image->height)) { (m_container_height != m_decoder->image->height)) {
qWarning("Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!\n", qWarning("Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
m_decoder->image->width, m_decoder->image->height, m_decoder->image->width, m_decoder->image->height,
m_container_width, m_container_height); m_container_width, m_container_height);
@ -912,6 +914,18 @@ int QAVIFHandler::loopCount() const
return 1; return 1;
} }
QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
{
chrX = qBound(qreal(0.0), chrX, qreal(1.0));
chrY = qBound(qreal(DBL_MIN), chrY, qreal(1.0));
if ((chrX + chrY) > qreal(1.0)) {
chrX = qreal(1.0) - chrY;
}
return QPointF(chrX, chrY);
}
QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{ {
if (format == "avif") { if (format == "avif") {

View File

@ -14,6 +14,7 @@
#include <qimageiohandler.h> #include <qimageiohandler.h>
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QByteArray> #include <QByteArray>
#include <QPointF>
#include <avif/avif.h> #include <avif/avif.h>
class QAVIFHandler : public QImageIOHandler class QAVIFHandler : public QImageIOHandler
@ -41,6 +42,7 @@ public:
int loopCount() const override; int loopCount() const override;
private: private:
static QPointF CompatibleChromacity(qreal chrX, qreal chrY);
bool ensureParsed() const; bool ensureParsed() const;
bool ensureDecoder(); bool ensureDecoder();
bool decode_one_frame(); bool decode_one_frame();