15#include "microexif_p.h"
29#ifndef KIMG_AVIF_DEFAULT_QUALITY
30#define KIMG_AVIF_DEFAULT_QUALITY 68
33#ifndef KIMG_AVIF_QUALITY_BEST
34#define KIMG_AVIF_QUALITY_BEST 90
37#ifndef KIMG_AVIF_QUALITY_HIGH
38#define KIMG_AVIF_QUALITY_HIGH 80
41#ifndef KIMG_AVIF_QUALITY_LOW
42#define KIMG_AVIF_QUALITY_LOW 51
45QAVIFHandler::QAVIFHandler()
46 : m_parseState(ParseAvifNotParsed)
47 , m_quality(KIMG_AVIF_DEFAULT_QUALITY)
48 , m_container_width(0)
49 , m_container_height(0)
50 , m_rawAvifData(AVIF_DATA_EMPTY)
52 , m_must_jump_to_next_image(false)
56QAVIFHandler::~QAVIFHandler()
59 avifDecoderDestroy(m_decoder);
63bool QAVIFHandler::canRead()
const
65 if (m_parseState == ParseAvifNotParsed && !canRead(device())) {
69 if (m_parseState != ParseAvifError) {
72 if (m_parseState == ParseAvifFinished) {
81bool QAVIFHandler::canRead(
QIODevice *device)
87 if (header.
size() < 12) {
92 input.
data =
reinterpret_cast<const uint8_t *
>(header.
constData());
93 input.size = header.
size();
95 if (avifPeekCompatibleFileType(&input)) {
101bool QAVIFHandler::ensureParsed()
const
103 if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata || m_parseState == ParseAvifFinished) {
106 if (m_parseState == ParseAvifError) {
110 QAVIFHandler *that =
const_cast<QAVIFHandler *
>(
this);
112 return that->ensureDecoder();
115bool QAVIFHandler::ensureOpened()
const
117 if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifFinished) {
120 if (m_parseState == ParseAvifError) {
124 QAVIFHandler *that =
const_cast<QAVIFHandler *
>(
this);
125 if (ensureParsed()) {
126 if (m_parseState == ParseAvifMetadata) {
127 bool success = that->jumpToNextImage();
128 that->m_parseState = success ? ParseAvifSuccess : ParseAvifError;
133 that->m_parseState = ParseAvifError;
137bool QAVIFHandler::ensureDecoder()
143 m_rawData = device()->
readAll();
145 m_rawAvifData.
data =
reinterpret_cast<const uint8_t *
>(m_rawData.constData());
146 m_rawAvifData.size = m_rawData.size();
148 if (avifPeekCompatibleFileType(&m_rawAvifData) == AVIF_FALSE) {
149 m_parseState = ParseAvifError;
153 m_decoder = avifDecoderCreate();
155#if AVIF_VERSION >= 80400
159#if AVIF_VERSION >= 90100
160 m_decoder->strictFlags = AVIF_STRICT_DISABLED;
163#if AVIF_VERSION >= 110000
164 m_decoder->imageDimensionLimit = 65535;
167 avifResult decodeResult;
169 decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
170 if (decodeResult != AVIF_RESULT_OK) {
171 qWarning(
"ERROR: avifDecoderSetIOMemory failed: %s", avifResultToString(decodeResult));
173 avifDecoderDestroy(m_decoder);
175 m_parseState = ParseAvifError;
179 decodeResult = avifDecoderParse(m_decoder);
180 if (decodeResult != AVIF_RESULT_OK) {
181 qWarning(
"ERROR: Failed to parse input: %s", avifResultToString(decodeResult));
183 avifDecoderDestroy(m_decoder);
185 m_parseState = ParseAvifError;
189 m_container_width = m_decoder->image->width;
190 m_container_height = m_decoder->image->height;
192 if ((m_container_width > 65535) || (m_container_height > 65535)) {
193 qWarning(
"AVIF image (%dx%d) is too large!", m_container_width, m_container_height);
194 m_parseState = ParseAvifError;
198 if ((m_container_width == 0) || (m_container_height == 0)) {
199 qWarning(
"Empty image, nothing to decode");
200 m_parseState = ParseAvifError;
204 if (m_container_width > ((16384 * 16384) / m_container_height)) {
205 qWarning(
"AVIF image (%dx%d) has more than 256 megapixels!", m_container_width, m_container_height);
206 m_parseState = ParseAvifError;
211 int new_width = m_container_width;
212 int new_height = m_container_height;
214 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
215 if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
216 && (m_decoder->image->clap.vertOffD > 0)) {
217 int crop_width = (int)((
double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
218 if (crop_width < new_width && crop_width > 0) {
219 new_width = crop_width;
221 int crop_height = (int)((
double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
222 if (crop_height < new_height && crop_height > 0) {
223 new_height = crop_height;
228 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
229 if (m_decoder->image->irot.angle == 1 || m_decoder->image->irot.angle == 3) {
231 new_width = new_height;
236 m_estimated_dimensions.setWidth(new_width);
237 m_estimated_dimensions.setHeight(new_height);
239 m_parseState = ParseAvifMetadata;
243bool QAVIFHandler::decode_one_frame()
245 if (!ensureParsed()) {
250 bool loadgray =
false;
252 if (m_decoder->image->alphaPlane) {
256 if (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
263 if (m_decoder->image->depth > 8) {
277 QImage result = imageAlloc(m_decoder->image->width, m_decoder->image->height, resultformat);
279 qWarning(
"Memory cannot be allocated");
284 if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) {
285 const QByteArray icc_data(
reinterpret_cast<const char *
>(m_decoder->image->icc.data), m_decoder->image->icc.size);
288 qWarning(
"AVIF image has Qt-unsupported or invalid ICC profile!");
290#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
292 if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
293 qWarning(
"CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
295 }
else if (colorspace.colorModel() == QColorSpace::ColorModel::Rgb && loadgray) {
298 }
else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray && !loadgray) {
301 QPointF gray_whitePoint = colorspace.whitePoint();
302 if (gray_whitePoint.
isNull()) {
303 gray_whitePoint =
QPointF(0.3127f, 0.329f);
306 const QPointF redP(0.64f, 0.33f);
307 const QPointF greenP(0.3f, 0.6f);
308 const QPointF blueP(0.15f, 0.06f);
311 float gamma_new = colorspace.
gamma();
312 if (trc_new == QColorSpace::TransferFunction::Custom) {
313 trc_new = QColorSpace::TransferFunction::SRgb;
315 colorspace =
QColorSpace(gray_whitePoint, redP, greenP, blueP, trc_new, gamma_new);
317 qWarning(
"AVIF plugin created invalid QColorSpace!");
323 float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
325 avifColorPrimariesGetValues(m_decoder->image->colorPrimaries, prim);
327 const QPointF redPoint(QAVIFHandler::CompatibleChromacity(prim[0], prim[1]));
328 const QPointF greenPoint(QAVIFHandler::CompatibleChromacity(prim[2], prim[3]));
329 const QPointF bluePoint(QAVIFHandler::CompatibleChromacity(prim[4], prim[5]));
330 const QPointF whitePoint(QAVIFHandler::CompatibleChromacity(prim[6], prim[7]));
333 float q_trc_gamma = 0.0f;
335 switch (m_decoder->image->transferCharacteristics) {
338 q_trc = QColorSpace::TransferFunction::Gamma;
343 q_trc = QColorSpace::TransferFunction::Gamma;
348 q_trc = QColorSpace::TransferFunction::Linear;
354 q_trc = QColorSpace::TransferFunction::SRgb;
356#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
358 q_trc = QColorSpace::TransferFunction::St2084;
361 q_trc = QColorSpace::TransferFunction::Hlg;
365 qWarning(
"CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
366 m_decoder->image->colorPrimaries,
367 m_decoder->image->transferCharacteristics);
368 q_trc = QColorSpace::TransferFunction::SRgb;
372 if (q_trc != QColorSpace::TransferFunction::Custom) {
373#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
375 colorspace =
QColorSpace(whitePoint, q_trc, q_trc_gamma);
378 switch (m_decoder->image->colorPrimaries) {
383 colorspace =
QColorSpace(QColorSpace::Primaries::SRgb, q_trc, q_trc_gamma);
387 colorspace =
QColorSpace(QColorSpace::Primaries::DciP3D65, q_trc, q_trc_gamma);
390 colorspace =
QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma);
393#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
399 qWarning(
"AVIF plugin created invalid QColorSpace from NCLX/CICP!");
404 avifRGBImageSetDefaults(&rgb, m_decoder->image);
406#if AVIF_VERSION >= 1000000
407 rgb.maxThreads = m_decoder->maxThreads;
410 if (m_decoder->image->depth > 8) {
412 rgb.format = AVIF_RGB_FORMAT_RGBA;
419#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
420 rgb.format = AVIF_RGB_FORMAT_BGRA;
422 rgb.format = AVIF_RGB_FORMAT_ARGB;
425#if AVIF_VERSION >= 80400
426 if (m_decoder->imageCount > 1) {
428 rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
438 rgb.pixels = result.
bits();
440 avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb);
441 if (res != AVIF_RESULT_OK) {
442 qWarning(
"ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
446 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
447 if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
448 && (m_decoder->image->clap.vertOffD > 0)) {
449 int new_width = (int)((
double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
450 if (new_width > result.
width()) {
451 new_width = result.
width();
454 int new_height = (int)((
double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
455 if (new_height > result.
height()) {
456 new_height = result.
height();
459 if (new_width > 0 && new_height > 0) {
461 ((double)((int32_t)m_decoder->image->clap.horizOffN)) / (m_decoder->image->clap.horizOffD) + (result.
width() - new_width) / 2.0 + 0.5;
464 }
else if (offx > (result.
width() - new_width)) {
465 offx = result.
width() - new_width;
469 ((double)((int32_t)m_decoder->image->clap.vertOffN)) / (m_decoder->image->clap.vertOffD) + (result.
height() - new_height) / 2.0 + 0.5;
472 }
else if (offy > (result.
height() - new_height)) {
473 offy = result.
height() - new_height;
476 result = result.
copy(offx, offy, new_width, new_height);
481 qWarning(
"ERROR: Wrong values in avifCleanApertureBox");
485 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
487 switch (m_decoder->image->irot.angle) {
503 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IMIR) {
504#if AVIF_VERSION > 90100 && AVIF_VERSION < 1000000
505 switch (m_decoder->image->imir.mode) {
507 switch (m_decoder->image->imir.axis) {
509#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
511 result = result.
mirrored(
false,
true);
514 result = result.
mirrored(
true,
false);
527 if (resultformat == result.
format()) {
528 m_current_image = result;
533 m_current_image.setColorSpace(colorspace);
535 if (m_decoder->image->exif.size) {
536 auto exif = MicroExif::fromRawData(
reinterpret_cast<const char *
>(m_decoder->image->exif.data), m_decoder->image->exif.size);
537 exif.updateImageResolution(m_current_image);
538 exif.updateImageMetadata(m_current_image);
541 if (m_decoder->image->xmp.size) {
542 auto ba =
QByteArray::fromRawData(
reinterpret_cast<const char *
>(m_decoder->image->xmp.data), m_decoder->image->xmp.size);
543 m_current_image.setText(QStringLiteral(META_KEY_XMP_ADOBE),
QString::fromUtf8(ba));
546 m_estimated_dimensions = m_current_image.size();
548 m_must_jump_to_next_image =
false;
552static void setMetadata(avifImage *avif,
const QImage& image)
554 auto xmp = image.
text(QStringLiteral(META_KEY_XMP_ADOBE)).
toUtf8();
555 if (!xmp.isEmpty()) {
556#if AVIF_VERSION >= 1000000
557 auto res = avifImageSetMetadataXMP(avif,
reinterpret_cast<const uint8_t *
>(xmp.constData()), xmp.size());
558 if (res != AVIF_RESULT_OK) {
559 qWarning(
"ERROR in avifImageSetMetadataXMP: %s", avifResultToString(res));
562 avifImageSetMetadataXMP(avif,
reinterpret_cast<const uint8_t *
>(xmp.constData()), xmp.size());
565 auto exif = MicroExif::fromImage(image).toByteArray();
566 if (!exif.isEmpty()) {
567#if AVIF_VERSION >= 1000000
568 auto res = avifImageSetMetadataExif(avif,
reinterpret_cast<const uint8_t *
>(exif.constData()), exif.size());
569 if (res != AVIF_RESULT_OK) {
570 qWarning(
"ERROR in avifImageSetMetadataExif: %s", avifResultToString(res));
573 avifImageSetMetadataExif(avif,
reinterpret_cast<const uint8_t *
>(exif.constData()), exif.size());
578bool QAVIFHandler::read(
QImage *image)
580 if (!ensureOpened()) {
584 if (m_must_jump_to_next_image) {
588 *image = m_current_image;
589 if (imageCount() >= 2) {
590 m_must_jump_to_next_image =
true;
591 if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
593 m_parseState = ParseAvifFinished;
597 m_parseState = ParseAvifFinished;
602bool QAVIFHandler::write(
const QImage &image)
605 qWarning(
"No image data to save!");
610 if ((image.
width() > 65535) || (image.
height() > 65535)) {
611 qWarning(
"Image (%dx%d) is too large to save!", image.
width(), image.
height());
615 if (image.
width() > ((16384 * 16384) / image.
height())) {
616 qWarning(
"Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.
width(), image.
height());
620 if ((image.
width() > 32768) || (image.
height() > 32768)) {
621 qWarning(
"Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.
width(), image.
height());
624 qWarning(
"Image has zero dimension!");
628 const char *encoder_name = avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE);
630 qWarning(
"Cannot save AVIF images because libavif was built without AV1 encoders!");
634 bool lossless =
false;
635 if (m_quality >= 100) {
636 if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE)) {
639 qWarning(
"You are using %s encoder. It is recommended to enable libAOM encoder in libavif to use lossless compression.", encoder_name);
643 if (m_quality > 100) {
645 }
else if (m_quality < 0) {
646 m_quality = KIMG_AVIF_DEFAULT_QUALITY;
649#if AVIF_VERSION < 1000000
650 int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
651 int minQuantizer = 0;
652 int maxQuantizerAlpha = 0;
660 avifImage *avif =
nullptr;
668 save_grayscale =
true;
674 save_grayscale =
false;
691 if (image.
depth() > 32) {
699#if AVIF_VERSION < 1000000
701 if (maxQuantizer > 20) {
702 minQuantizer = maxQuantizer - 20;
703 if (maxQuantizer > 40) {
704 maxQuantizerAlpha = maxQuantizer - 40;
710 if (save_depth > 8) {
717 avif = avifImageCreate(tmpgrayimage.
width(), tmpgrayimage.
height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
718#if AVIF_VERSION >= 110000
719 res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
720 if (res != AVIF_RESULT_OK) {
721 qWarning(
"ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
725 avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
728 setMetadata(avif, tmpgrayimage);
731 avif->colorPrimaries = (avifColorPrimaries)1;
732 avif->matrixCoefficients = (avifMatrixCoefficients)1;
735 case QColorSpace::TransferFunction::Linear:
737 avif->transferCharacteristics = (avifTransferCharacteristics)8;
739 case QColorSpace::TransferFunction::SRgb:
741 avif->transferCharacteristics = (avifTransferCharacteristics)13;
743#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
744 case QColorSpace::TransferFunction::St2084:
745 avif->transferCharacteristics = (avifTransferCharacteristics)16;
747 case QColorSpace::TransferFunction::Hlg:
748 avif->transferCharacteristics = (avifTransferCharacteristics)18;
757 if (save_depth > 8) {
758 for (
int y = 0; y < tmpgrayimage.
height(); y++) {
759 const uint16_t *src16bit =
reinterpret_cast<const uint16_t *
>(tmpgrayimage.
constScanLine(y));
760 uint16_t *dest16bit =
reinterpret_cast<uint16_t *
>(avif->yuvPlanes[0] + y * avif->yuvRowBytes[0]);
761 for (
int x = 0; x < tmpgrayimage.
width(); x++) {
762 int tmp_pixelval = (int)(((
float)(*src16bit) / 65535.0f) * 1023.0f + 0.5f);
763 *dest16bit = qBound(0, tmp_pixelval, 1023);
769 for (
int y = 0; y < tmpgrayimage.
height(); y++) {
771 uint8_t *dest8bit = avif->yuvPlanes[0] + y * avif->yuvRowBytes[0];
772 for (
int x = 0; x < tmpgrayimage.
width(); x++) {
773 *dest8bit = *src8bit;
781 if (save_depth > 8) {
795#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
798 if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.
format() == QImage::Format_CMYK8888) {
800 }
else if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Gray) {
802 float gamma_new = cs.gamma();
803 if (trc_new == QColorSpace::TransferFunction::Custom) {
804 trc_new = QColorSpace::TransferFunction::SRgb;
814 avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
815 if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
816 if (m_quality >= KIMG_AVIF_QUALITY_BEST) {
817 pixel_format = AVIF_PIXEL_FORMAT_YUV444;
819 pixel_format = AVIF_PIXEL_FORMAT_YUV422;
823 avifMatrixCoefficients matrix_to_save = (avifMatrixCoefficients)1;
825 avifColorPrimaries primaries_to_save = (avifColorPrimaries)2;
826 avifTransferCharacteristics transfer_to_save = (avifTransferCharacteristics)2;
831 case QColorSpace::Primaries::SRgb:
833 primaries_to_save = (avifColorPrimaries)1;
835 matrix_to_save = (avifMatrixCoefficients)1;
837 case QColorSpace::Primaries::DciP3D65:
839 primaries_to_save = (avifColorPrimaries)12;
841 matrix_to_save = (avifMatrixCoefficients)12;
845 primaries_to_save = (avifColorPrimaries)2;
847 matrix_to_save = (avifMatrixCoefficients)2;
852 case QColorSpace::TransferFunction::Linear:
854 transfer_to_save = (avifTransferCharacteristics)8;
856 case QColorSpace::TransferFunction::Gamma:
859 transfer_to_save = (avifTransferCharacteristics)4;
862 transfer_to_save = (avifTransferCharacteristics)5;
865 transfer_to_save = (avifTransferCharacteristics)2;
868 case QColorSpace::TransferFunction::SRgb:
870 transfer_to_save = (avifTransferCharacteristics)13;
872#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
873 case QColorSpace::TransferFunction::St2084:
874 transfer_to_save = (avifTransferCharacteristics)16;
876 case QColorSpace::TransferFunction::Hlg:
877 transfer_to_save = (avifTransferCharacteristics)18;
882 transfer_to_save = (avifTransferCharacteristics)2;
887 if ((primaries_to_save == 2) || (transfer_to_save == 2)) {
892 if (save_depth == 8) {
901 if ((primaries_to_save == 2) && (transfer_to_save != 2)) {
902 primaries_to_save = (avifColorPrimaries)1;
903 matrix_to_save = (avifMatrixCoefficients)1;
905 switch (transfer_to_save) {
915#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
925 transfer_to_save = (avifTransferCharacteristics)13;
928 }
else if ((primaries_to_save != 2) && (transfer_to_save == 2)) {
929 transfer_to_save = (avifTransferCharacteristics)13;
932 primaries_to_save = (avifColorPrimaries)1;
933 transfer_to_save = (avifTransferCharacteristics)13;
934 matrix_to_save = (avifMatrixCoefficients)1;
941 if (iccprofile.
size() > 0) {
942 matrix_to_save = (avifMatrixCoefficients)6;
946 if (lossless && pixel_format == AVIF_PIXEL_FORMAT_YUV444) {
947 matrix_to_save = (avifMatrixCoefficients)0;
949 avif = avifImageCreate(tmpcolorimage.
width(), tmpcolorimage.
height(), save_depth, pixel_format);
950 avif->matrixCoefficients = matrix_to_save;
952 avif->colorPrimaries = primaries_to_save;
953 avif->transferCharacteristics = transfer_to_save;
956 setMetadata(avif, tmpcolorimage);
958 if (iccprofile.
size() > 0) {
959#if AVIF_VERSION >= 1000000
960 res = avifImageSetProfileICC(avif,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
961 if (res != AVIF_RESULT_OK) {
962 qWarning(
"ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
966 avifImageSetProfileICC(avif,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
971 avifRGBImageSetDefaults(&rgb, avif);
973 rgb.pixels =
const_cast<uint8_t *
>(tmpcolorimage.
constBits());
975 if (save_depth > 8) {
979 rgb.ignoreAlpha = AVIF_TRUE;
982 rgb.format = AVIF_RGB_FORMAT_RGBA;
987 rgb.format = AVIF_RGB_FORMAT_RGBA;
989 rgb.format = AVIF_RGB_FORMAT_RGB;
993 res = avifImageRGBToYUV(avif, &rgb);
994 if (res != AVIF_RESULT_OK) {
995 qWarning(
"ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
1000 avifRWData raw = AVIF_DATA_EMPTY;
1001 avifEncoder *encoder = avifEncoderCreate();
1004#if AVIF_VERSION < 1000000
1005 encoder->minQuantizer = minQuantizer;
1006 encoder->maxQuantizer = maxQuantizer;
1009 encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
1010 encoder->maxQuantizerAlpha = maxQuantizerAlpha;
1013 encoder->quality = m_quality;
1016 if (m_quality >= KIMG_AVIF_QUALITY_LOW) {
1017 encoder->qualityAlpha = 100;
1019 encoder->qualityAlpha = 100 - (KIMG_AVIF_QUALITY_LOW - m_quality) / 2;
1026 res = avifEncoderWrite(encoder, avif, &raw);
1027 avifEncoderDestroy(encoder);
1028 avifImageDestroy(avif);
1030 if (res == AVIF_RESULT_OK) {
1031 qint64
status = device()->
write(
reinterpret_cast<const char *
>(raw.data), raw.size);
1032 avifRWDataFree(&raw);
1036 }
else if (
status == -1) {
1037 qWarning(
"Write error: %s", qUtf8Printable(device()->
errorString()));
1041 qWarning(
"ERROR: Failed to encode: %s", avifResultToString(res));
1047QVariant QAVIFHandler::option(ImageOption option)
const
1049 if (option == Quality) {
1053 if (!supportsOption(option) || !ensureParsed()) {
1059 return m_estimated_dimensions;
1061 if (imageCount() >= 2) {
1071void QAVIFHandler::setOption(ImageOption option,
const QVariant &value)
1075 m_quality = value.
toInt();
1076 if (m_quality > 100) {
1078 }
else if (m_quality < 0) {
1079 m_quality = KIMG_AVIF_DEFAULT_QUALITY;
1088bool QAVIFHandler::supportsOption(ImageOption option)
const
1090 return option == Quality || option == Size || option ==
Animation;
1093int QAVIFHandler::imageCount()
const
1095 if (!ensureParsed()) {
1099 if (m_decoder->imageCount >= 1) {
1100 return m_decoder->imageCount;
1105int QAVIFHandler::currentImageNumber()
const
1107 if (m_parseState == ParseAvifNotParsed) {
1111 if (m_parseState == ParseAvifError || !m_decoder) {
1115 if (m_parseState == ParseAvifMetadata) {
1116 if (m_decoder->imageCount >= 2) {
1123 return m_decoder->imageIndex;
1126bool QAVIFHandler::jumpToNextImage()
1128 if (!ensureParsed()) {
1132 avifResult decodeResult;
1134 if (m_decoder->imageIndex >= 0) {
1135 if (m_decoder->imageCount < 2) {
1136 m_parseState = ParseAvifSuccess;
1140 if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
1141 decodeResult = avifDecoderReset(m_decoder);
1142 if (decodeResult != AVIF_RESULT_OK) {
1143 qWarning(
"ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
1144 m_parseState = ParseAvifError;
1150 decodeResult = avifDecoderNextImage(m_decoder);
1152 if (decodeResult != AVIF_RESULT_OK) {
1153 qWarning(
"ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
1154 m_parseState = ParseAvifError;
1158 if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1159 qWarning(
"Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
1160 m_decoder->image->width,
1161 m_decoder->image->height,
1163 m_container_height);
1165 m_parseState = ParseAvifError;
1169 if (decode_one_frame()) {
1170 m_parseState = ParseAvifSuccess;
1173 m_parseState = ParseAvifError;
1178bool QAVIFHandler::jumpToImage(
int imageNumber)
1180 if (!ensureParsed()) {
1184 if (m_decoder->imageCount < 2) {
1185 if (imageNumber == 0) {
1186 if (ensureOpened()) {
1187 m_parseState = ParseAvifSuccess;
1194 if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) {
1198 if (imageNumber == m_decoder->imageIndex) {
1199 m_must_jump_to_next_image =
false;
1200 m_parseState = ParseAvifSuccess;
1204 avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber);
1206 if (decodeResult != AVIF_RESULT_OK) {
1207 qWarning(
"ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
1208 m_parseState = ParseAvifError;
1212 if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1213 qWarning(
"Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
1214 m_decoder->image->width,
1215 m_decoder->image->height,
1217 m_container_height);
1219 m_parseState = ParseAvifError;
1223 if (decode_one_frame()) {
1224 m_parseState = ParseAvifSuccess;
1227 m_parseState = ParseAvifError;
1232int QAVIFHandler::nextImageDelay()
const
1234 if (!ensureOpened()) {
1238 if (m_decoder->imageCount < 2) {
1242 int delay_ms = 1000.0 * m_decoder->imageTiming.duration;
1249int QAVIFHandler::loopCount()
const
1251 if (!ensureParsed()) {
1255 if (m_decoder->imageCount < 2) {
1259#if AVIF_VERSION >= 1000000
1260 if (m_decoder->repetitionCount >= 0) {
1261 return m_decoder->repetitionCount;
1268QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
1270 chrX = qBound(qreal(0.0), chrX, qreal(1.0));
1271 chrY = qBound(qreal(DBL_MIN), chrY, qreal(1.0));
1273 if ((chrX + chrY) > qreal(1.0)) {
1274 chrX = qreal(1.0) - chrY;
1282 static const bool isAvifDecoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_DECODE) !=
nullptr);
1283 static const bool isAvifEncoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE) !=
nullptr);
1285 if (format ==
"avif") {
1287 if (isAvifDecoderAvailable) {
1288 format_cap |= CanRead;
1290 if (isAvifEncoderAvailable) {
1291 format_cap |= CanWrite;
1296 if (format ==
"avifs") {
1298 if (isAvifDecoderAvailable) {
1299 format_cap |= CanRead;
1312 if (device->
isReadable() && QAVIFHandler::canRead(device) && isAvifDecoderAvailable) {
1315 if (device->
isWritable() && isAvifEncoderAvailable) {
1329#include "moc_avif_p.cpp"
Q_SCRIPTABLE CaptureState status()
QFlags< Capability > Capabilities
OKULARCORE_EXPORT QString errorString(SigningResult result, const QVariant &additionalMessage)
const char * constData() const const
QByteArray fromRawData(const char *data, qsizetype size)
bool isEmpty() const const
qsizetype size() const const
QColorSpace fromIccProfile(const QByteArray &iccProfile)
float gamma() const const
QByteArray iccProfile() const const
bool isValid() const const
Primaries primaries() const const
TransferFunction transferFunction() const const
QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma) const const
qsizetype bytesPerLine() const const
QColorSpace colorSpace() const const
const uchar * constBits() const const
const uchar * constScanLine(int i) const const
void convertTo(Format format, Qt::ImageConversionFlags flags)
void convertToColorSpace(const QColorSpace &colorSpace)
QImage convertedToColorSpace(const QColorSpace &colorSpace) const const
QImage copy(const QRect &rectangle) const const
bool hasAlphaChannel() const const
bool isGrayscale() const const
bool isNull() const const
QImage mirrored(bool horizontal, bool vertical) &&
QString text(const QString &key) const const
void setDevice(QIODevice *device)
virtual void setOption(ImageOption option, const QVariant &value)
bool isOpen() const const
bool isReadable() const const
bool isWritable() const const
QByteArray peek(qint64 maxSize)
qint64 write(const QByteArray &data)
bool isNull() const const
QString fromUtf8(QByteArrayView str)
QByteArray toUtf8() const const
int toInt(bool *ok) const const