28#ifndef KIMG_AVIF_DEFAULT_QUALITY
29#define KIMG_AVIF_DEFAULT_QUALITY 68
32#ifndef KIMG_AVIF_QUALITY_BEST
33#define KIMG_AVIF_QUALITY_BEST 90
36#ifndef KIMG_AVIF_QUALITY_HIGH
37#define KIMG_AVIF_QUALITY_HIGH 80
40#ifndef KIMG_AVIF_QUALITY_LOW
41#define KIMG_AVIF_QUALITY_LOW 51
44QAVIFHandler::QAVIFHandler()
45 : m_parseState(ParseAvifNotParsed)
46 , m_quality(KIMG_AVIF_DEFAULT_QUALITY)
47 , m_container_width(0)
48 , m_container_height(0)
49 , m_rawAvifData(AVIF_DATA_EMPTY)
51 , m_must_jump_to_next_image(false)
55QAVIFHandler::~QAVIFHandler()
58 avifDecoderDestroy(m_decoder);
62bool QAVIFHandler::canRead()
const
64 if (m_parseState == ParseAvifNotParsed && !canRead(device())) {
68 if (m_parseState != ParseAvifError) {
71 if (m_parseState == ParseAvifFinished) {
80bool QAVIFHandler::canRead(
QIODevice *device)
86 if (header.
size() < 12) {
91 input.
data =
reinterpret_cast<const uint8_t *
>(header.
constData());
92 input.size = header.
size();
94 if (avifPeekCompatibleFileType(&input)) {
100bool QAVIFHandler::ensureParsed()
const
102 if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata || m_parseState == ParseAvifFinished) {
105 if (m_parseState == ParseAvifError) {
109 QAVIFHandler *that =
const_cast<QAVIFHandler *
>(
this);
111 return that->ensureDecoder();
114bool QAVIFHandler::ensureOpened()
const
116 if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifFinished) {
119 if (m_parseState == ParseAvifError) {
123 QAVIFHandler *that =
const_cast<QAVIFHandler *
>(
this);
124 if (ensureParsed()) {
125 if (m_parseState == ParseAvifMetadata) {
126 bool success = that->jumpToNextImage();
127 that->m_parseState = success ? ParseAvifSuccess : ParseAvifError;
132 that->m_parseState = ParseAvifError;
136bool QAVIFHandler::ensureDecoder()
142 m_rawData = device()->
readAll();
144 m_rawAvifData.
data =
reinterpret_cast<const uint8_t *
>(m_rawData.constData());
145 m_rawAvifData.size = m_rawData.size();
147 if (avifPeekCompatibleFileType(&m_rawAvifData) == AVIF_FALSE) {
148 m_parseState = ParseAvifError;
152 m_decoder = avifDecoderCreate();
154 m_decoder->ignoreExif = AVIF_TRUE;
155 m_decoder->ignoreXMP = AVIF_TRUE;
157#if AVIF_VERSION >= 80400
161#if AVIF_VERSION >= 90100
162 m_decoder->strictFlags = AVIF_STRICT_DISABLED;
165#if AVIF_VERSION >= 110000
166 m_decoder->imageDimensionLimit = 65535;
169 avifResult decodeResult;
171 decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
172 if (decodeResult != AVIF_RESULT_OK) {
173 qWarning(
"ERROR: avifDecoderSetIOMemory failed: %s", avifResultToString(decodeResult));
175 avifDecoderDestroy(m_decoder);
177 m_parseState = ParseAvifError;
181 decodeResult = avifDecoderParse(m_decoder);
182 if (decodeResult != AVIF_RESULT_OK) {
183 qWarning(
"ERROR: Failed to parse input: %s", avifResultToString(decodeResult));
185 avifDecoderDestroy(m_decoder);
187 m_parseState = ParseAvifError;
191 m_container_width = m_decoder->image->width;
192 m_container_height = m_decoder->image->height;
194 if ((m_container_width > 65535) || (m_container_height > 65535)) {
195 qWarning(
"AVIF image (%dx%d) is too large!", m_container_width, m_container_height);
196 m_parseState = ParseAvifError;
200 if ((m_container_width == 0) || (m_container_height == 0)) {
201 qWarning(
"Empty image, nothing to decode");
202 m_parseState = ParseAvifError;
206 if (m_container_width > ((16384 * 16384) / m_container_height)) {
207 qWarning(
"AVIF image (%dx%d) has more than 256 megapixels!", m_container_width, m_container_height);
208 m_parseState = ParseAvifError;
213 int new_width = m_container_width;
214 int new_height = m_container_height;
216 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
217 if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
218 && (m_decoder->image->clap.vertOffD > 0)) {
219 int crop_width = (int)((
double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
220 if (crop_width < new_width && crop_width > 0) {
221 new_width = crop_width;
223 int crop_height = (int)((
double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
224 if (crop_height < new_height && crop_height > 0) {
225 new_height = crop_height;
230 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
231 if (m_decoder->image->irot.angle == 1 || m_decoder->image->irot.angle == 3) {
233 new_width = new_height;
238 m_estimated_dimensions.setWidth(new_width);
239 m_estimated_dimensions.setHeight(new_height);
241 m_parseState = ParseAvifMetadata;
245bool QAVIFHandler::decode_one_frame()
247 if (!ensureParsed()) {
252 bool loadgray =
false;
254 if (m_decoder->image->alphaPlane) {
258 if (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
265 if (m_decoder->image->depth > 8) {
279 QImage result = imageAlloc(m_decoder->image->width, m_decoder->image->height, resultformat);
281 qWarning(
"Memory cannot be allocated");
286 if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) {
287 const QByteArray icc_data(
reinterpret_cast<const char *
>(m_decoder->image->icc.data), m_decoder->image->icc.size);
290 qWarning(
"AVIF image has Qt-unsupported or invalid ICC profile!");
292#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
294 if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
295 qWarning(
"CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
297 }
else if (colorspace.colorModel() == QColorSpace::ColorModel::Rgb && loadgray) {
300 }
else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray && !loadgray) {
303 QPointF gray_whitePoint = colorspace.whitePoint();
304 if (gray_whitePoint.
isNull()) {
305 gray_whitePoint =
QPointF(0.3127f, 0.329f);
308 const QPointF redP(0.64f, 0.33f);
309 const QPointF greenP(0.3f, 0.6f);
310 const QPointF blueP(0.15f, 0.06f);
313 float gamma_new = colorspace.
gamma();
314 if (trc_new == QColorSpace::TransferFunction::Custom) {
315 trc_new = QColorSpace::TransferFunction::SRgb;
317 colorspace =
QColorSpace(gray_whitePoint, redP, greenP, blueP, trc_new, gamma_new);
319 qWarning(
"AVIF plugin created invalid QColorSpace!");
325 float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
327 avifColorPrimariesGetValues(m_decoder->image->colorPrimaries, prim);
329 const QPointF redPoint(QAVIFHandler::CompatibleChromacity(prim[0], prim[1]));
330 const QPointF greenPoint(QAVIFHandler::CompatibleChromacity(prim[2], prim[3]));
331 const QPointF bluePoint(QAVIFHandler::CompatibleChromacity(prim[4], prim[5]));
332 const QPointF whitePoint(QAVIFHandler::CompatibleChromacity(prim[6], prim[7]));
335 float q_trc_gamma = 0.0f;
337 switch (m_decoder->image->transferCharacteristics) {
340 q_trc = QColorSpace::TransferFunction::Gamma;
345 q_trc = QColorSpace::TransferFunction::Gamma;
350 q_trc = QColorSpace::TransferFunction::Linear;
356 q_trc = QColorSpace::TransferFunction::SRgb;
358#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
360 q_trc = QColorSpace::TransferFunction::St2084;
363 q_trc = QColorSpace::TransferFunction::Hlg;
367 qWarning(
"CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
368 m_decoder->image->colorPrimaries,
369 m_decoder->image->transferCharacteristics);
370 q_trc = QColorSpace::TransferFunction::SRgb;
374 if (q_trc != QColorSpace::TransferFunction::Custom) {
375#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
377 colorspace =
QColorSpace(whitePoint, q_trc, q_trc_gamma);
380 switch (m_decoder->image->colorPrimaries) {
385 colorspace =
QColorSpace(QColorSpace::Primaries::SRgb, q_trc, q_trc_gamma);
389 colorspace =
QColorSpace(QColorSpace::Primaries::DciP3D65, q_trc, q_trc_gamma);
392 colorspace =
QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma);
395#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
401 qWarning(
"AVIF plugin created invalid QColorSpace from NCLX/CICP!");
406 avifRGBImageSetDefaults(&rgb, m_decoder->image);
408#if AVIF_VERSION >= 1000000
409 rgb.maxThreads = m_decoder->maxThreads;
412 if (m_decoder->image->depth > 8) {
414 rgb.format = AVIF_RGB_FORMAT_RGBA;
421#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
422 rgb.format = AVIF_RGB_FORMAT_BGRA;
424 rgb.format = AVIF_RGB_FORMAT_ARGB;
427#if AVIF_VERSION >= 80400
428 if (m_decoder->imageCount > 1) {
430 rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
440 rgb.pixels = result.
bits();
442 avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb);
443 if (res != AVIF_RESULT_OK) {
444 qWarning(
"ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
448 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
449 if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
450 && (m_decoder->image->clap.vertOffD > 0)) {
451 int new_width = (int)((
double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
452 if (new_width > result.
width()) {
453 new_width = result.
width();
456 int new_height = (int)((
double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
457 if (new_height > result.
height()) {
458 new_height = result.
height();
461 if (new_width > 0 && new_height > 0) {
463 ((double)((int32_t)m_decoder->image->clap.horizOffN)) / (m_decoder->image->clap.horizOffD) + (result.
width() - new_width) / 2.0 + 0.5;
466 }
else if (offx > (result.
width() - new_width)) {
467 offx = result.
width() - new_width;
471 ((double)((int32_t)m_decoder->image->clap.vertOffN)) / (m_decoder->image->clap.vertOffD) + (result.
height() - new_height) / 2.0 + 0.5;
474 }
else if (offy > (result.
height() - new_height)) {
475 offy = result.
height() - new_height;
478 result = result.
copy(offx, offy, new_width, new_height);
483 qWarning(
"ERROR: Wrong values in avifCleanApertureBox");
487 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
489 switch (m_decoder->image->irot.angle) {
505 if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IMIR) {
506#if AVIF_VERSION > 90100 && AVIF_VERSION < 1000000
507 switch (m_decoder->image->imir.mode) {
509 switch (m_decoder->image->imir.axis) {
512 result = result.
mirrored(
false,
true);
515 result = result.
mirrored(
true,
false);
520 if (resultformat == result.
format()) {
521 m_current_image = result;
526 m_current_image.setColorSpace(colorspace);
528 m_estimated_dimensions = m_current_image.size();
530 m_must_jump_to_next_image =
false;
534bool QAVIFHandler::read(
QImage *image)
536 if (!ensureOpened()) {
540 if (m_must_jump_to_next_image) {
544 *image = m_current_image;
545 if (imageCount() >= 2) {
546 m_must_jump_to_next_image =
true;
547 if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
549 m_parseState = ParseAvifFinished;
553 m_parseState = ParseAvifFinished;
558bool QAVIFHandler::write(
const QImage &image)
561 qWarning(
"No image data to save!");
566 if ((image.
width() > 65535) || (image.
height() > 65535)) {
567 qWarning(
"Image (%dx%d) is too large to save!", image.
width(), image.
height());
571 if (image.
width() > ((16384 * 16384) / image.
height())) {
572 qWarning(
"Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.
width(), image.
height());
576 if ((image.
width() > 32768) || (image.
height() > 32768)) {
577 qWarning(
"Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.
width(), image.
height());
580 qWarning(
"Image has zero dimension!");
584 const char *encoder_name = avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE);
586 qWarning(
"Cannot save AVIF images because libavif was built without AV1 encoders!");
590 bool lossless =
false;
591 if (m_quality >= 100) {
592 if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE)) {
595 qWarning(
"You are using %s encoder. It is recommended to enable libAOM encoder in libavif to use lossless compression.", encoder_name);
599 if (m_quality > 100) {
601 }
else if (m_quality < 0) {
602 m_quality = KIMG_AVIF_DEFAULT_QUALITY;
605#if AVIF_VERSION < 1000000
606 int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
607 int minQuantizer = 0;
608 int maxQuantizerAlpha = 0;
616 avifImage *avif =
nullptr;
624 save_grayscale =
true;
630 save_grayscale =
false;
647 if (image.
depth() > 32) {
655#if AVIF_VERSION < 1000000
657 if (maxQuantizer > 20) {
658 minQuantizer = maxQuantizer - 20;
659 if (maxQuantizer > 40) {
660 maxQuantizerAlpha = maxQuantizer - 40;
666 if (save_depth > 8) {
673 avif = avifImageCreate(tmpgrayimage.
width(), tmpgrayimage.
height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
674#if AVIF_VERSION >= 110000
675 res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
676 if (res != AVIF_RESULT_OK) {
677 qWarning(
"ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
681 avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
685 avif->colorPrimaries = (avifColorPrimaries)1;
686 avif->matrixCoefficients = (avifMatrixCoefficients)1;
689 case QColorSpace::TransferFunction::Linear:
691 avif->transferCharacteristics = (avifTransferCharacteristics)8;
693 case QColorSpace::TransferFunction::SRgb:
695 avif->transferCharacteristics = (avifTransferCharacteristics)13;
697#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
698 case QColorSpace::TransferFunction::St2084:
699 avif->transferCharacteristics = (avifTransferCharacteristics)16;
701 case QColorSpace::TransferFunction::Hlg:
702 avif->transferCharacteristics = (avifTransferCharacteristics)18;
711 if (save_depth > 8) {
712 for (
int y = 0; y < tmpgrayimage.
height(); y++) {
713 const uint16_t *src16bit =
reinterpret_cast<const uint16_t *
>(tmpgrayimage.
constScanLine(y));
714 uint16_t *dest16bit =
reinterpret_cast<uint16_t *
>(avif->yuvPlanes[0] + y * avif->yuvRowBytes[0]);
715 for (
int x = 0; x < tmpgrayimage.
width(); x++) {
716 int tmp_pixelval = (int)(((
float)(*src16bit) / 65535.0f) * 1023.0f + 0.5f);
717 *dest16bit = qBound(0, tmp_pixelval, 1023);
723 for (
int y = 0; y < tmpgrayimage.
height(); y++) {
725 uint8_t *dest8bit = avif->yuvPlanes[0] + y * avif->yuvRowBytes[0];
726 for (
int x = 0; x < tmpgrayimage.
width(); x++) {
727 *dest8bit = *src8bit;
735 if (save_depth > 8) {
749#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
752 if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.
format() == QImage::Format_CMYK8888) {
754 }
else if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Gray) {
756 float gamma_new = cs.gamma();
757 if (trc_new == QColorSpace::TransferFunction::Custom) {
758 trc_new = QColorSpace::TransferFunction::SRgb;
768 avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
769 if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
770 if (m_quality >= KIMG_AVIF_QUALITY_BEST) {
771 pixel_format = AVIF_PIXEL_FORMAT_YUV444;
773 pixel_format = AVIF_PIXEL_FORMAT_YUV422;
777 avifMatrixCoefficients matrix_to_save = (avifMatrixCoefficients)1;
779 avifColorPrimaries primaries_to_save = (avifColorPrimaries)2;
780 avifTransferCharacteristics transfer_to_save = (avifTransferCharacteristics)2;
785 case QColorSpace::Primaries::SRgb:
787 primaries_to_save = (avifColorPrimaries)1;
789 matrix_to_save = (avifMatrixCoefficients)1;
791 case QColorSpace::Primaries::DciP3D65:
793 primaries_to_save = (avifColorPrimaries)12;
795 matrix_to_save = (avifMatrixCoefficients)12;
799 primaries_to_save = (avifColorPrimaries)2;
801 matrix_to_save = (avifMatrixCoefficients)2;
806 case QColorSpace::TransferFunction::Linear:
808 transfer_to_save = (avifTransferCharacteristics)8;
810 case QColorSpace::TransferFunction::Gamma:
813 transfer_to_save = (avifTransferCharacteristics)4;
816 transfer_to_save = (avifTransferCharacteristics)5;
819 transfer_to_save = (avifTransferCharacteristics)2;
822 case QColorSpace::TransferFunction::SRgb:
824 transfer_to_save = (avifTransferCharacteristics)13;
826#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
827 case QColorSpace::TransferFunction::St2084:
828 transfer_to_save = (avifTransferCharacteristics)16;
830 case QColorSpace::TransferFunction::Hlg:
831 transfer_to_save = (avifTransferCharacteristics)18;
836 transfer_to_save = (avifTransferCharacteristics)2;
841 if ((primaries_to_save == 2) || (transfer_to_save == 2)) {
846 if (save_depth == 8) {
855 if ((primaries_to_save == 2) && (transfer_to_save != 2)) {
856 primaries_to_save = (avifColorPrimaries)1;
857 matrix_to_save = (avifMatrixCoefficients)1;
859 switch (transfer_to_save) {
869#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
879 transfer_to_save = (avifTransferCharacteristics)13;
882 }
else if ((primaries_to_save != 2) && (transfer_to_save == 2)) {
883 transfer_to_save = (avifTransferCharacteristics)13;
886 primaries_to_save = (avifColorPrimaries)1;
887 transfer_to_save = (avifTransferCharacteristics)13;
888 matrix_to_save = (avifMatrixCoefficients)1;
895 if (iccprofile.
size() > 0) {
896 matrix_to_save = (avifMatrixCoefficients)6;
900 if (lossless && pixel_format == AVIF_PIXEL_FORMAT_YUV444) {
901 matrix_to_save = (avifMatrixCoefficients)0;
903 avif = avifImageCreate(tmpcolorimage.
width(), tmpcolorimage.
height(), save_depth, pixel_format);
904 avif->matrixCoefficients = matrix_to_save;
906 avif->colorPrimaries = primaries_to_save;
907 avif->transferCharacteristics = transfer_to_save;
909 if (iccprofile.
size() > 0) {
910#if AVIF_VERSION >= 1000000
911 res = avifImageSetProfileICC(avif,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
912 if (res != AVIF_RESULT_OK) {
913 qWarning(
"ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
917 avifImageSetProfileICC(avif,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
922 avifRGBImageSetDefaults(&rgb, avif);
924 rgb.pixels =
const_cast<uint8_t *
>(tmpcolorimage.
constBits());
926 if (save_depth > 8) {
930 rgb.ignoreAlpha = AVIF_TRUE;
933 rgb.format = AVIF_RGB_FORMAT_RGBA;
938 rgb.format = AVIF_RGB_FORMAT_RGBA;
940 rgb.format = AVIF_RGB_FORMAT_RGB;
944 res = avifImageRGBToYUV(avif, &rgb);
945 if (res != AVIF_RESULT_OK) {
946 qWarning(
"ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
951 avifRWData raw = AVIF_DATA_EMPTY;
952 avifEncoder *encoder = avifEncoderCreate();
955#if AVIF_VERSION < 1000000
956 encoder->minQuantizer = minQuantizer;
957 encoder->maxQuantizer = maxQuantizer;
960 encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
961 encoder->maxQuantizerAlpha = maxQuantizerAlpha;
964 encoder->quality = m_quality;
967 if (m_quality >= KIMG_AVIF_QUALITY_LOW) {
968 encoder->qualityAlpha = 100;
970 encoder->qualityAlpha = 100 - (KIMG_AVIF_QUALITY_LOW - m_quality) / 2;
977 res = avifEncoderWrite(encoder, avif, &raw);
978 avifEncoderDestroy(encoder);
979 avifImageDestroy(avif);
981 if (res == AVIF_RESULT_OK) {
982 qint64
status = device()->
write(
reinterpret_cast<const char *
>(raw.data), raw.size);
983 avifRWDataFree(&raw);
987 }
else if (
status == -1) {
988 qWarning(
"Write error: %s", qUtf8Printable(device()->errorString()));
992 qWarning(
"ERROR: Failed to encode: %s", avifResultToString(res));
998QVariant QAVIFHandler::option(ImageOption option)
const
1000 if (option == Quality) {
1004 if (!supportsOption(option) || !ensureParsed()) {
1010 return m_estimated_dimensions;
1012 if (imageCount() >= 2) {
1022void QAVIFHandler::setOption(ImageOption option,
const QVariant &value)
1026 m_quality = value.
toInt();
1027 if (m_quality > 100) {
1029 }
else if (m_quality < 0) {
1030 m_quality = KIMG_AVIF_DEFAULT_QUALITY;
1039bool QAVIFHandler::supportsOption(ImageOption option)
const
1041 return option == Quality || option == Size || option ==
Animation;
1044int QAVIFHandler::imageCount()
const
1046 if (!ensureParsed()) {
1050 if (m_decoder->imageCount >= 1) {
1051 return m_decoder->imageCount;
1056int QAVIFHandler::currentImageNumber()
const
1058 if (m_parseState == ParseAvifNotParsed) {
1062 if (m_parseState == ParseAvifError || !m_decoder) {
1066 if (m_parseState == ParseAvifMetadata) {
1067 if (m_decoder->imageCount >= 2) {
1074 return m_decoder->imageIndex;
1077bool QAVIFHandler::jumpToNextImage()
1079 if (!ensureParsed()) {
1083 avifResult decodeResult;
1085 if (m_decoder->imageIndex >= 0) {
1086 if (m_decoder->imageCount < 2) {
1087 m_parseState = ParseAvifSuccess;
1091 if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
1092 decodeResult = avifDecoderReset(m_decoder);
1093 if (decodeResult != AVIF_RESULT_OK) {
1094 qWarning(
"ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
1095 m_parseState = ParseAvifError;
1101 decodeResult = avifDecoderNextImage(m_decoder);
1103 if (decodeResult != AVIF_RESULT_OK) {
1104 qWarning(
"ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
1105 m_parseState = ParseAvifError;
1109 if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1110 qWarning(
"Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
1111 m_decoder->image->width,
1112 m_decoder->image->height,
1114 m_container_height);
1116 m_parseState = ParseAvifError;
1120 if (decode_one_frame()) {
1121 m_parseState = ParseAvifSuccess;
1124 m_parseState = ParseAvifError;
1129bool QAVIFHandler::jumpToImage(
int imageNumber)
1131 if (!ensureParsed()) {
1135 if (m_decoder->imageCount < 2) {
1136 if (imageNumber == 0) {
1137 if (ensureOpened()) {
1138 m_parseState = ParseAvifSuccess;
1145 if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) {
1149 if (imageNumber == m_decoder->imageIndex) {
1150 m_must_jump_to_next_image =
false;
1151 m_parseState = ParseAvifSuccess;
1155 avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber);
1157 if (decodeResult != AVIF_RESULT_OK) {
1158 qWarning(
"ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
1159 m_parseState = ParseAvifError;
1163 if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1164 qWarning(
"Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
1165 m_decoder->image->width,
1166 m_decoder->image->height,
1168 m_container_height);
1170 m_parseState = ParseAvifError;
1174 if (decode_one_frame()) {
1175 m_parseState = ParseAvifSuccess;
1178 m_parseState = ParseAvifError;
1183int QAVIFHandler::nextImageDelay()
const
1185 if (!ensureOpened()) {
1189 if (m_decoder->imageCount < 2) {
1193 int delay_ms = 1000.0 * m_decoder->imageTiming.duration;
1200int QAVIFHandler::loopCount()
const
1202 if (!ensureParsed()) {
1206 if (m_decoder->imageCount < 2) {
1210#if AVIF_VERSION >= 1000000
1211 if (m_decoder->repetitionCount >= 0) {
1212 return m_decoder->repetitionCount;
1219QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
1221 chrX = qBound(qreal(0.0), chrX, qreal(1.0));
1222 chrY = qBound(qreal(DBL_MIN), chrY, qreal(1.0));
1224 if ((chrX + chrY) > qreal(1.0)) {
1225 chrX = qreal(1.0) - chrY;
1233 static const bool isAvifDecoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_DECODE) !=
nullptr);
1234 static const bool isAvifEncoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE) !=
nullptr);
1236 if (format ==
"avif") {
1238 if (isAvifDecoderAvailable) {
1239 format_cap |= CanRead;
1241 if (isAvifEncoderAvailable) {
1242 format_cap |= CanWrite;
1247 if (format ==
"avifs") {
1249 if (isAvifDecoderAvailable) {
1250 format_cap |= CanRead;
1263 if (device->
isReadable() && QAVIFHandler::canRead(device) && isAvifDecoderAvailable) {
1266 if (device->
isWritable() && isAvifEncoderAvailable) {
1280#include "moc_avif_p.cpp"
Q_SCRIPTABLE CaptureState status()
QFlags< Capability > Capabilities
const char * constData() const const
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) &&
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
int toInt(bool *ok) const const