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) {
511#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
513 result = result.
mirrored(
false,
true);
516 result = result.
mirrored(
true,
false);
529 if (resultformat == result.
format()) {
530 m_current_image = result;
535 m_current_image.setColorSpace(colorspace);
537 m_estimated_dimensions = m_current_image.size();
539 m_must_jump_to_next_image =
false;
543bool QAVIFHandler::read(
QImage *image)
545 if (!ensureOpened()) {
549 if (m_must_jump_to_next_image) {
553 *image = m_current_image;
554 if (imageCount() >= 2) {
555 m_must_jump_to_next_image =
true;
556 if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
558 m_parseState = ParseAvifFinished;
562 m_parseState = ParseAvifFinished;
567bool QAVIFHandler::write(
const QImage &image)
570 qWarning(
"No image data to save!");
575 if ((image.
width() > 65535) || (image.
height() > 65535)) {
576 qWarning(
"Image (%dx%d) is too large to save!", image.
width(), image.
height());
580 if (image.
width() > ((16384 * 16384) / image.
height())) {
581 qWarning(
"Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.
width(), image.
height());
585 if ((image.
width() > 32768) || (image.
height() > 32768)) {
586 qWarning(
"Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.
width(), image.
height());
589 qWarning(
"Image has zero dimension!");
593 const char *encoder_name = avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE);
595 qWarning(
"Cannot save AVIF images because libavif was built without AV1 encoders!");
599 bool lossless =
false;
600 if (m_quality >= 100) {
601 if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE)) {
604 qWarning(
"You are using %s encoder. It is recommended to enable libAOM encoder in libavif to use lossless compression.", encoder_name);
608 if (m_quality > 100) {
610 }
else if (m_quality < 0) {
611 m_quality = KIMG_AVIF_DEFAULT_QUALITY;
614#if AVIF_VERSION < 1000000
615 int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
616 int minQuantizer = 0;
617 int maxQuantizerAlpha = 0;
625 avifImage *avif =
nullptr;
633 save_grayscale =
true;
639 save_grayscale =
false;
656 if (image.
depth() > 32) {
664#if AVIF_VERSION < 1000000
666 if (maxQuantizer > 20) {
667 minQuantizer = maxQuantizer - 20;
668 if (maxQuantizer > 40) {
669 maxQuantizerAlpha = maxQuantizer - 40;
675 if (save_depth > 8) {
682 avif = avifImageCreate(tmpgrayimage.
width(), tmpgrayimage.
height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
683#if AVIF_VERSION >= 110000
684 res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
685 if (res != AVIF_RESULT_OK) {
686 qWarning(
"ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
690 avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
694 avif->colorPrimaries = (avifColorPrimaries)1;
695 avif->matrixCoefficients = (avifMatrixCoefficients)1;
698 case QColorSpace::TransferFunction::Linear:
700 avif->transferCharacteristics = (avifTransferCharacteristics)8;
702 case QColorSpace::TransferFunction::SRgb:
704 avif->transferCharacteristics = (avifTransferCharacteristics)13;
706#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
707 case QColorSpace::TransferFunction::St2084:
708 avif->transferCharacteristics = (avifTransferCharacteristics)16;
710 case QColorSpace::TransferFunction::Hlg:
711 avif->transferCharacteristics = (avifTransferCharacteristics)18;
720 if (save_depth > 8) {
721 for (
int y = 0; y < tmpgrayimage.
height(); y++) {
722 const uint16_t *src16bit =
reinterpret_cast<const uint16_t *
>(tmpgrayimage.
constScanLine(y));
723 uint16_t *dest16bit =
reinterpret_cast<uint16_t *
>(avif->yuvPlanes[0] + y * avif->yuvRowBytes[0]);
724 for (
int x = 0; x < tmpgrayimage.
width(); x++) {
725 int tmp_pixelval = (int)(((
float)(*src16bit) / 65535.0f) * 1023.0f + 0.5f);
726 *dest16bit = qBound(0, tmp_pixelval, 1023);
732 for (
int y = 0; y < tmpgrayimage.
height(); y++) {
734 uint8_t *dest8bit = avif->yuvPlanes[0] + y * avif->yuvRowBytes[0];
735 for (
int x = 0; x < tmpgrayimage.
width(); x++) {
736 *dest8bit = *src8bit;
744 if (save_depth > 8) {
758#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
761 if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.
format() == QImage::Format_CMYK8888) {
763 }
else if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Gray) {
765 float gamma_new = cs.gamma();
766 if (trc_new == QColorSpace::TransferFunction::Custom) {
767 trc_new = QColorSpace::TransferFunction::SRgb;
777 avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
778 if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
779 if (m_quality >= KIMG_AVIF_QUALITY_BEST) {
780 pixel_format = AVIF_PIXEL_FORMAT_YUV444;
782 pixel_format = AVIF_PIXEL_FORMAT_YUV422;
786 avifMatrixCoefficients matrix_to_save = (avifMatrixCoefficients)1;
788 avifColorPrimaries primaries_to_save = (avifColorPrimaries)2;
789 avifTransferCharacteristics transfer_to_save = (avifTransferCharacteristics)2;
794 case QColorSpace::Primaries::SRgb:
796 primaries_to_save = (avifColorPrimaries)1;
798 matrix_to_save = (avifMatrixCoefficients)1;
800 case QColorSpace::Primaries::DciP3D65:
802 primaries_to_save = (avifColorPrimaries)12;
804 matrix_to_save = (avifMatrixCoefficients)12;
808 primaries_to_save = (avifColorPrimaries)2;
810 matrix_to_save = (avifMatrixCoefficients)2;
815 case QColorSpace::TransferFunction::Linear:
817 transfer_to_save = (avifTransferCharacteristics)8;
819 case QColorSpace::TransferFunction::Gamma:
822 transfer_to_save = (avifTransferCharacteristics)4;
825 transfer_to_save = (avifTransferCharacteristics)5;
828 transfer_to_save = (avifTransferCharacteristics)2;
831 case QColorSpace::TransferFunction::SRgb:
833 transfer_to_save = (avifTransferCharacteristics)13;
835#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
836 case QColorSpace::TransferFunction::St2084:
837 transfer_to_save = (avifTransferCharacteristics)16;
839 case QColorSpace::TransferFunction::Hlg:
840 transfer_to_save = (avifTransferCharacteristics)18;
845 transfer_to_save = (avifTransferCharacteristics)2;
850 if ((primaries_to_save == 2) || (transfer_to_save == 2)) {
855 if (save_depth == 8) {
864 if ((primaries_to_save == 2) && (transfer_to_save != 2)) {
865 primaries_to_save = (avifColorPrimaries)1;
866 matrix_to_save = (avifMatrixCoefficients)1;
868 switch (transfer_to_save) {
878#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
888 transfer_to_save = (avifTransferCharacteristics)13;
891 }
else if ((primaries_to_save != 2) && (transfer_to_save == 2)) {
892 transfer_to_save = (avifTransferCharacteristics)13;
895 primaries_to_save = (avifColorPrimaries)1;
896 transfer_to_save = (avifTransferCharacteristics)13;
897 matrix_to_save = (avifMatrixCoefficients)1;
904 if (iccprofile.
size() > 0) {
905 matrix_to_save = (avifMatrixCoefficients)6;
909 if (lossless && pixel_format == AVIF_PIXEL_FORMAT_YUV444) {
910 matrix_to_save = (avifMatrixCoefficients)0;
912 avif = avifImageCreate(tmpcolorimage.
width(), tmpcolorimage.
height(), save_depth, pixel_format);
913 avif->matrixCoefficients = matrix_to_save;
915 avif->colorPrimaries = primaries_to_save;
916 avif->transferCharacteristics = transfer_to_save;
918 if (iccprofile.
size() > 0) {
919#if AVIF_VERSION >= 1000000
920 res = avifImageSetProfileICC(avif,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
921 if (res != AVIF_RESULT_OK) {
922 qWarning(
"ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
926 avifImageSetProfileICC(avif,
reinterpret_cast<const uint8_t *
>(iccprofile.
constData()), iccprofile.
size());
931 avifRGBImageSetDefaults(&rgb, avif);
933 rgb.pixels =
const_cast<uint8_t *
>(tmpcolorimage.
constBits());
935 if (save_depth > 8) {
939 rgb.ignoreAlpha = AVIF_TRUE;
942 rgb.format = AVIF_RGB_FORMAT_RGBA;
947 rgb.format = AVIF_RGB_FORMAT_RGBA;
949 rgb.format = AVIF_RGB_FORMAT_RGB;
953 res = avifImageRGBToYUV(avif, &rgb);
954 if (res != AVIF_RESULT_OK) {
955 qWarning(
"ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
960 avifRWData raw = AVIF_DATA_EMPTY;
961 avifEncoder *encoder = avifEncoderCreate();
964#if AVIF_VERSION < 1000000
965 encoder->minQuantizer = minQuantizer;
966 encoder->maxQuantizer = maxQuantizer;
969 encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
970 encoder->maxQuantizerAlpha = maxQuantizerAlpha;
973 encoder->quality = m_quality;
976 if (m_quality >= KIMG_AVIF_QUALITY_LOW) {
977 encoder->qualityAlpha = 100;
979 encoder->qualityAlpha = 100 - (KIMG_AVIF_QUALITY_LOW - m_quality) / 2;
986 res = avifEncoderWrite(encoder, avif, &raw);
987 avifEncoderDestroy(encoder);
988 avifImageDestroy(avif);
990 if (res == AVIF_RESULT_OK) {
991 qint64
status = device()->
write(
reinterpret_cast<const char *
>(raw.data), raw.size);
992 avifRWDataFree(&raw);
996 }
else if (
status == -1) {
997 qWarning(
"Write error: %s", qUtf8Printable(device()->errorString()));
1001 qWarning(
"ERROR: Failed to encode: %s", avifResultToString(res));
1007QVariant QAVIFHandler::option(ImageOption option)
const
1009 if (option == Quality) {
1013 if (!supportsOption(option) || !ensureParsed()) {
1019 return m_estimated_dimensions;
1021 if (imageCount() >= 2) {
1031void QAVIFHandler::setOption(ImageOption option,
const QVariant &value)
1035 m_quality = value.
toInt();
1036 if (m_quality > 100) {
1038 }
else if (m_quality < 0) {
1039 m_quality = KIMG_AVIF_DEFAULT_QUALITY;
1048bool QAVIFHandler::supportsOption(ImageOption option)
const
1050 return option == Quality || option == Size || option ==
Animation;
1053int QAVIFHandler::imageCount()
const
1055 if (!ensureParsed()) {
1059 if (m_decoder->imageCount >= 1) {
1060 return m_decoder->imageCount;
1065int QAVIFHandler::currentImageNumber()
const
1067 if (m_parseState == ParseAvifNotParsed) {
1071 if (m_parseState == ParseAvifError || !m_decoder) {
1075 if (m_parseState == ParseAvifMetadata) {
1076 if (m_decoder->imageCount >= 2) {
1083 return m_decoder->imageIndex;
1086bool QAVIFHandler::jumpToNextImage()
1088 if (!ensureParsed()) {
1092 avifResult decodeResult;
1094 if (m_decoder->imageIndex >= 0) {
1095 if (m_decoder->imageCount < 2) {
1096 m_parseState = ParseAvifSuccess;
1100 if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
1101 decodeResult = avifDecoderReset(m_decoder);
1102 if (decodeResult != AVIF_RESULT_OK) {
1103 qWarning(
"ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
1104 m_parseState = ParseAvifError;
1110 decodeResult = avifDecoderNextImage(m_decoder);
1112 if (decodeResult != AVIF_RESULT_OK) {
1113 qWarning(
"ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
1114 m_parseState = ParseAvifError;
1118 if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1119 qWarning(
"Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
1120 m_decoder->image->width,
1121 m_decoder->image->height,
1123 m_container_height);
1125 m_parseState = ParseAvifError;
1129 if (decode_one_frame()) {
1130 m_parseState = ParseAvifSuccess;
1133 m_parseState = ParseAvifError;
1138bool QAVIFHandler::jumpToImage(
int imageNumber)
1140 if (!ensureParsed()) {
1144 if (m_decoder->imageCount < 2) {
1145 if (imageNumber == 0) {
1146 if (ensureOpened()) {
1147 m_parseState = ParseAvifSuccess;
1154 if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) {
1158 if (imageNumber == m_decoder->imageIndex) {
1159 m_must_jump_to_next_image =
false;
1160 m_parseState = ParseAvifSuccess;
1164 avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber);
1166 if (decodeResult != AVIF_RESULT_OK) {
1167 qWarning(
"ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
1168 m_parseState = ParseAvifError;
1172 if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1173 qWarning(
"Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
1174 m_decoder->image->width,
1175 m_decoder->image->height,
1177 m_container_height);
1179 m_parseState = ParseAvifError;
1183 if (decode_one_frame()) {
1184 m_parseState = ParseAvifSuccess;
1187 m_parseState = ParseAvifError;
1192int QAVIFHandler::nextImageDelay()
const
1194 if (!ensureOpened()) {
1198 if (m_decoder->imageCount < 2) {
1202 int delay_ms = 1000.0 * m_decoder->imageTiming.duration;
1209int QAVIFHandler::loopCount()
const
1211 if (!ensureParsed()) {
1215 if (m_decoder->imageCount < 2) {
1219#if AVIF_VERSION >= 1000000
1220 if (m_decoder->repetitionCount >= 0) {
1221 return m_decoder->repetitionCount;
1228QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
1230 chrX = qBound(qreal(0.0), chrX, qreal(1.0));
1231 chrY = qBound(qreal(DBL_MIN), chrY, qreal(1.0));
1233 if ((chrX + chrY) > qreal(1.0)) {
1234 chrX = qreal(1.0) - chrY;
1242 static const bool isAvifDecoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_DECODE) !=
nullptr);
1243 static const bool isAvifEncoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE) !=
nullptr);
1245 if (format ==
"avif") {
1247 if (isAvifDecoderAvailable) {
1248 format_cap |= CanRead;
1250 if (isAvifEncoderAvailable) {
1251 format_cap |= CanWrite;
1256 if (format ==
"avifs") {
1258 if (isAvifDecoderAvailable) {
1259 format_cap |= CanRead;
1272 if (device->
isReadable() && QAVIFHandler::canRead(device) && isAvifDecoderAvailable) {
1275 if (device->
isWritable() && isAvifEncoderAvailable) {
1289#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