File indexing completed on 2024-04-28 15:25:39

0001 /*
0002     AV1 Image File Format (AVIF) support for QImage.
0003 
0004     SPDX-FileCopyrightText: 2020 Daniel Novomesky <dnovomesky@gmail.com>
0005 
0006     SPDX-License-Identifier: BSD-2-Clause
0007 */
0008 
0009 #include <QThread>
0010 #include <QtGlobal>
0011 
0012 #include <QColorSpace>
0013 
0014 #include "avif_p.h"
0015 #include "util_p.h"
0016 
0017 #include <cfloat>
0018 
0019 /*
0020 Quality range - compression/subsampling
0021 100 - lossless RGB compression
0022 < KIMG_AVIF_QUALITY_BEST, 100 ) - YUV444 color subsampling
0023 < KIMG_AVIF_QUALITY_HIGH, KIMG_AVIF_QUALITY_BEST ) - YUV422 color subsampling
0024 < 0, KIMG_AVIF_QUALITY_HIGH ) - YUV420 color subsampling
0025 < 0, KIMG_AVIF_QUALITY_LOW ) - lossy compression of alpha channel
0026 */
0027 
0028 #ifndef KIMG_AVIF_DEFAULT_QUALITY
0029 #define KIMG_AVIF_DEFAULT_QUALITY 68
0030 #endif
0031 
0032 #ifndef KIMG_AVIF_QUALITY_BEST
0033 #define KIMG_AVIF_QUALITY_BEST 90
0034 #endif
0035 
0036 #ifndef KIMG_AVIF_QUALITY_HIGH
0037 #define KIMG_AVIF_QUALITY_HIGH 80
0038 #endif
0039 
0040 #ifndef KIMG_AVIF_QUALITY_LOW
0041 #define KIMG_AVIF_QUALITY_LOW 51
0042 #endif
0043 
0044 QAVIFHandler::QAVIFHandler()
0045     : m_parseState(ParseAvifNotParsed)
0046     , m_quality(KIMG_AVIF_DEFAULT_QUALITY)
0047     , m_container_width(0)
0048     , m_container_height(0)
0049     , m_rawAvifData(AVIF_DATA_EMPTY)
0050     , m_decoder(nullptr)
0051     , m_must_jump_to_next_image(false)
0052 {
0053 }
0054 
0055 QAVIFHandler::~QAVIFHandler()
0056 {
0057     if (m_decoder) {
0058         avifDecoderDestroy(m_decoder);
0059     }
0060 }
0061 
0062 bool QAVIFHandler::canRead() const
0063 {
0064     if (m_parseState == ParseAvifNotParsed && !canRead(device())) {
0065         return false;
0066     }
0067 
0068     if (m_parseState != ParseAvifError) {
0069         setFormat("avif");
0070 
0071         if (m_parseState == ParseAvifFinished) {
0072             return false;
0073         }
0074 
0075         return true;
0076     }
0077     return false;
0078 }
0079 
0080 bool QAVIFHandler::canRead(QIODevice *device)
0081 {
0082     if (!device) {
0083         return false;
0084     }
0085     QByteArray header = device->peek(144);
0086     if (header.size() < 12) {
0087         return false;
0088     }
0089 
0090     avifROData input;
0091     input.data = reinterpret_cast<const uint8_t *>(header.constData());
0092     input.size = header.size();
0093 
0094     if (avifPeekCompatibleFileType(&input)) {
0095         return true;
0096     }
0097     return false;
0098 }
0099 
0100 bool QAVIFHandler::ensureParsed() const
0101 {
0102     if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata || m_parseState == ParseAvifFinished) {
0103         return true;
0104     }
0105     if (m_parseState == ParseAvifError) {
0106         return false;
0107     }
0108 
0109     QAVIFHandler *that = const_cast<QAVIFHandler *>(this);
0110 
0111     return that->ensureDecoder();
0112 }
0113 
0114 bool QAVIFHandler::ensureOpened() const
0115 {
0116     if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifFinished) {
0117         return true;
0118     }
0119     if (m_parseState == ParseAvifError) {
0120         return false;
0121     }
0122 
0123     QAVIFHandler *that = const_cast<QAVIFHandler *>(this);
0124     if (ensureParsed()) {
0125         if (m_parseState == ParseAvifMetadata) {
0126             bool success = that->jumpToNextImage();
0127             that->m_parseState = success ? ParseAvifSuccess : ParseAvifError;
0128             return success;
0129         }
0130     }
0131 
0132     that->m_parseState = ParseAvifError;
0133     return false;
0134 }
0135 
0136 bool QAVIFHandler::ensureDecoder()
0137 {
0138     if (m_decoder) {
0139         return true;
0140     }
0141 
0142     m_rawData = device()->readAll();
0143 
0144     m_rawAvifData.data = reinterpret_cast<const uint8_t *>(m_rawData.constData());
0145     m_rawAvifData.size = m_rawData.size();
0146 
0147     if (avifPeekCompatibleFileType(&m_rawAvifData) == AVIF_FALSE) {
0148         m_parseState = ParseAvifError;
0149         return false;
0150     }
0151 
0152     m_decoder = avifDecoderCreate();
0153 
0154     m_decoder->ignoreExif = AVIF_TRUE;
0155     m_decoder->ignoreXMP = AVIF_TRUE;
0156 
0157 #if AVIF_VERSION >= 80400
0158     m_decoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
0159 #endif
0160 
0161 #if AVIF_VERSION >= 90100
0162     m_decoder->strictFlags = AVIF_STRICT_DISABLED;
0163 #endif
0164 
0165 #if AVIF_VERSION >= 110000
0166     m_decoder->imageDimensionLimit = 65535;
0167 #endif
0168 
0169     avifResult decodeResult;
0170 
0171     decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
0172     if (decodeResult != AVIF_RESULT_OK) {
0173         qWarning("ERROR: avifDecoderSetIOMemory failed: %s", avifResultToString(decodeResult));
0174 
0175         avifDecoderDestroy(m_decoder);
0176         m_decoder = nullptr;
0177         m_parseState = ParseAvifError;
0178         return false;
0179     }
0180 
0181     decodeResult = avifDecoderParse(m_decoder);
0182     if (decodeResult != AVIF_RESULT_OK) {
0183         qWarning("ERROR: Failed to parse input: %s", avifResultToString(decodeResult));
0184 
0185         avifDecoderDestroy(m_decoder);
0186         m_decoder = nullptr;
0187         m_parseState = ParseAvifError;
0188         return false;
0189     }
0190 
0191     m_container_width = m_decoder->image->width;
0192     m_container_height = m_decoder->image->height;
0193 
0194     if ((m_container_width > 65535) || (m_container_height > 65535)) {
0195         qWarning("AVIF image (%dx%d) is too large!", m_container_width, m_container_height);
0196         m_parseState = ParseAvifError;
0197         return false;
0198     }
0199 
0200     if ((m_container_width == 0) || (m_container_height == 0)) {
0201         qWarning("Empty image, nothing to decode");
0202         m_parseState = ParseAvifError;
0203         return false;
0204     }
0205 
0206     if (m_container_width > ((16384 * 16384) / m_container_height)) {
0207         qWarning("AVIF image (%dx%d) has more than 256 megapixels!", m_container_width, m_container_height);
0208         m_parseState = ParseAvifError;
0209         return false;
0210     }
0211 
0212     // calculate final dimensions with crop and rotate operations applied
0213     int new_width = m_container_width;
0214     int new_height = m_container_height;
0215 
0216     if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
0217         if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
0218             && (m_decoder->image->clap.vertOffD > 0)) {
0219             int crop_width = (int)((double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
0220             if (crop_width < new_width && crop_width > 0) {
0221                 new_width = crop_width;
0222             }
0223             int crop_height = (int)((double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
0224             if (crop_height < new_height && crop_height > 0) {
0225                 new_height = crop_height;
0226             }
0227         }
0228     }
0229 
0230     if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
0231         if (m_decoder->image->irot.angle == 1 || m_decoder->image->irot.angle == 3) {
0232             int tmp = new_width;
0233             new_width = new_height;
0234             new_height = tmp;
0235         }
0236     }
0237 
0238     m_estimated_dimensions.setWidth(new_width);
0239     m_estimated_dimensions.setHeight(new_height);
0240 
0241     m_parseState = ParseAvifMetadata;
0242     return true;
0243 }
0244 
0245 bool QAVIFHandler::decode_one_frame()
0246 {
0247     if (!ensureParsed()) {
0248         return false;
0249     }
0250 
0251     bool loadalpha;
0252 
0253     if (m_decoder->image->alphaPlane) {
0254         loadalpha = true;
0255     } else {
0256         loadalpha = false;
0257     }
0258 
0259     QImage::Format resultformat;
0260 
0261     if (m_decoder->image->depth > 8) {
0262         if (loadalpha) {
0263             resultformat = QImage::Format_RGBA64;
0264         } else {
0265             resultformat = QImage::Format_RGBX64;
0266         }
0267     } else {
0268         if (loadalpha) {
0269             resultformat = QImage::Format_ARGB32;
0270         } else {
0271             resultformat = QImage::Format_RGB32;
0272         }
0273     }
0274 
0275     QImage result = imageAlloc(m_decoder->image->width, m_decoder->image->height, resultformat);
0276     if (result.isNull()) {
0277         qWarning("Memory cannot be allocated");
0278         return false;
0279     }
0280 
0281     QColorSpace colorspace;
0282     if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) {
0283         const QByteArray icc_data(reinterpret_cast<const char *>(m_decoder->image->icc.data), m_decoder->image->icc.size);
0284         colorspace = QColorSpace::fromIccProfile(icc_data);
0285         if (!colorspace.isValid()) {
0286             qWarning("AVIF image has Qt-unsupported or invalid ICC profile!");
0287         }
0288     } else {
0289         float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
0290         // outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY
0291         avifColorPrimariesGetValues(m_decoder->image->colorPrimaries, prim);
0292 
0293         const QPointF redPoint(QAVIFHandler::CompatibleChromacity(prim[0], prim[1]));
0294         const QPointF greenPoint(QAVIFHandler::CompatibleChromacity(prim[2], prim[3]));
0295         const QPointF bluePoint(QAVIFHandler::CompatibleChromacity(prim[4], prim[5]));
0296         const QPointF whitePoint(QAVIFHandler::CompatibleChromacity(prim[6], prim[7]));
0297 
0298         QColorSpace::TransferFunction q_trc = QColorSpace::TransferFunction::Custom;
0299         float q_trc_gamma = 0.0f;
0300 
0301         switch (m_decoder->image->transferCharacteristics) {
0302         /* AVIF_TRANSFER_CHARACTERISTICS_BT470M */
0303         case 4:
0304             q_trc = QColorSpace::TransferFunction::Gamma;
0305             q_trc_gamma = 2.2f;
0306             break;
0307         /* AVIF_TRANSFER_CHARACTERISTICS_BT470BG */
0308         case 5:
0309             q_trc = QColorSpace::TransferFunction::Gamma;
0310             q_trc_gamma = 2.8f;
0311             break;
0312         /* AVIF_TRANSFER_CHARACTERISTICS_LINEAR */
0313         case 8:
0314             q_trc = QColorSpace::TransferFunction::Linear;
0315             break;
0316         /* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
0317         case 0:
0318         case 2: /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
0319         case 13:
0320             q_trc = QColorSpace::TransferFunction::SRgb;
0321             break;
0322         default:
0323             qWarning("CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
0324                      m_decoder->image->colorPrimaries,
0325                      m_decoder->image->transferCharacteristics);
0326             q_trc = QColorSpace::TransferFunction::SRgb;
0327             break;
0328         }
0329 
0330         if (q_trc != QColorSpace::TransferFunction::Custom) { // we create new colorspace using Qt
0331             switch (m_decoder->image->colorPrimaries) {
0332             /* AVIF_COLOR_PRIMARIES_BT709 */
0333             case 0:
0334             case 1:
0335             case 2: /* AVIF_COLOR_PRIMARIES_UNSPECIFIED */
0336                 colorspace = QColorSpace(QColorSpace::Primaries::SRgb, q_trc, q_trc_gamma);
0337                 break;
0338             /* AVIF_COLOR_PRIMARIES_SMPTE432 */
0339             case 12:
0340                 colorspace = QColorSpace(QColorSpace::Primaries::DciP3D65, q_trc, q_trc_gamma);
0341                 break;
0342             default:
0343                 colorspace = QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma);
0344                 break;
0345             }
0346         }
0347 
0348         if (!colorspace.isValid()) {
0349             qWarning("AVIF plugin created invalid QColorSpace from NCLX/CICP!");
0350         }
0351     }
0352 
0353     result.setColorSpace(colorspace);
0354 
0355     avifRGBImage rgb;
0356     avifRGBImageSetDefaults(&rgb, m_decoder->image);
0357 
0358 #if AVIF_VERSION >= 1000000
0359     rgb.maxThreads = m_decoder->maxThreads;
0360 #endif
0361 
0362     if (m_decoder->image->depth > 8) {
0363         rgb.depth = 16;
0364         rgb.format = AVIF_RGB_FORMAT_RGBA;
0365 
0366         if (!loadalpha && (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) {
0367             resultformat = QImage::Format_Grayscale16;
0368         }
0369     } else {
0370         rgb.depth = 8;
0371 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0372         rgb.format = AVIF_RGB_FORMAT_BGRA;
0373 #else
0374         rgb.format = AVIF_RGB_FORMAT_ARGB;
0375 #endif
0376 
0377 #if AVIF_VERSION >= 80400
0378         if (m_decoder->imageCount > 1) {
0379             /* accelerate animated AVIF */
0380             rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
0381         }
0382 #endif
0383 
0384         if (!loadalpha && (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) {
0385             resultformat = QImage::Format_Grayscale8;
0386         }
0387     }
0388 
0389     rgb.rowBytes = result.bytesPerLine();
0390     rgb.pixels = result.bits();
0391 
0392     avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb);
0393     if (res != AVIF_RESULT_OK) {
0394         qWarning("ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
0395         return false;
0396     }
0397 
0398     if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
0399         if ((m_decoder->image->clap.widthD > 0) && (m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
0400             && (m_decoder->image->clap.vertOffD > 0)) {
0401             int new_width = (int)((double)(m_decoder->image->clap.widthN) / (m_decoder->image->clap.widthD) + 0.5);
0402             if (new_width > result.width()) {
0403                 new_width = result.width();
0404             }
0405 
0406             int new_height = (int)((double)(m_decoder->image->clap.heightN) / (m_decoder->image->clap.heightD) + 0.5);
0407             if (new_height > result.height()) {
0408                 new_height = result.height();
0409             }
0410 
0411             if (new_width > 0 && new_height > 0) {
0412                 int offx =
0413                     ((double)((int32_t)m_decoder->image->clap.horizOffN)) / (m_decoder->image->clap.horizOffD) + (result.width() - new_width) / 2.0 + 0.5;
0414                 if (offx < 0) {
0415                     offx = 0;
0416                 } else if (offx > (result.width() - new_width)) {
0417                     offx = result.width() - new_width;
0418                 }
0419 
0420                 int offy =
0421                     ((double)((int32_t)m_decoder->image->clap.vertOffN)) / (m_decoder->image->clap.vertOffD) + (result.height() - new_height) / 2.0 + 0.5;
0422                 if (offy < 0) {
0423                     offy = 0;
0424                 } else if (offy > (result.height() - new_height)) {
0425                     offy = result.height() - new_height;
0426                 }
0427 
0428                 result = result.copy(offx, offy, new_width, new_height);
0429             }
0430         }
0431 
0432         else { // Zero values, we need to avoid 0 divide.
0433             qWarning("ERROR: Wrong values in avifCleanApertureBox");
0434         }
0435     }
0436 
0437     if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
0438         QTransform transform;
0439         switch (m_decoder->image->irot.angle) {
0440         case 1:
0441             transform.rotate(-90);
0442             result = result.transformed(transform);
0443             break;
0444         case 2:
0445             transform.rotate(180);
0446             result = result.transformed(transform);
0447             break;
0448         case 3:
0449             transform.rotate(90);
0450             result = result.transformed(transform);
0451             break;
0452         }
0453     }
0454 
0455     if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IMIR) {
0456 #if AVIF_VERSION > 90100 && AVIF_VERSION < 1000000
0457         switch (m_decoder->image->imir.mode) {
0458 #else
0459         switch (m_decoder->image->imir.axis) {
0460 #endif
0461         case 0: // top-to-bottom
0462             result = result.mirrored(false, true);
0463             break;
0464         case 1: // left-to-right
0465             result = result.mirrored(true, false);
0466             break;
0467         }
0468     }
0469 
0470     if (resultformat == result.format()) {
0471         m_current_image = result;
0472     } else {
0473         m_current_image = result.convertToFormat(resultformat);
0474     }
0475 
0476     m_estimated_dimensions = m_current_image.size();
0477 
0478     m_must_jump_to_next_image = false;
0479     return true;
0480 }
0481 
0482 bool QAVIFHandler::read(QImage *image)
0483 {
0484     if (!ensureOpened()) {
0485         return false;
0486     }
0487 
0488     if (m_must_jump_to_next_image) {
0489         jumpToNextImage();
0490     }
0491 
0492     *image = m_current_image;
0493     if (imageCount() >= 2) {
0494         m_must_jump_to_next_image = true;
0495         if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
0496             // all frames in animation have been read
0497             m_parseState = ParseAvifFinished;
0498         }
0499     } else {
0500         // the static image has been read
0501         m_parseState = ParseAvifFinished;
0502     }
0503     return true;
0504 }
0505 
0506 bool QAVIFHandler::write(const QImage &image)
0507 {
0508     if (image.format() == QImage::Format_Invalid) {
0509         qWarning("No image data to save!");
0510         return false;
0511     }
0512 
0513     if ((image.width() > 0) && (image.height() > 0)) {
0514         if ((image.width() > 65535) || (image.height() > 65535)) {
0515             qWarning("Image (%dx%d) is too large to save!", image.width(), image.height());
0516             return false;
0517         }
0518 
0519         if (image.width() > ((16384 * 16384) / image.height())) {
0520             qWarning("Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.width(), image.height());
0521             return false;
0522         }
0523 
0524         if ((image.width() > 32768) || (image.height() > 32768)) {
0525             qWarning("Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.width(), image.height());
0526         }
0527     } else {
0528         qWarning("Image has zero dimension!");
0529         return false;
0530     }
0531 
0532     const char *encoder_name = avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE);
0533     if (!encoder_name) {
0534         qWarning("Cannot save AVIF images because libavif was built without AV1 encoders!");
0535         return false;
0536     }
0537 
0538     bool lossless = false;
0539     if (m_quality >= 100) {
0540         if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE)) {
0541             lossless = true;
0542         } else {
0543             qWarning("You are using %s encoder. It is recommended to enable libAOM encoder in libavif to use lossless compression.", encoder_name);
0544         }
0545     }
0546 
0547     if (m_quality > 100) {
0548         m_quality = 100;
0549     } else if (m_quality < 0) {
0550         m_quality = KIMG_AVIF_DEFAULT_QUALITY;
0551     }
0552 
0553 #if AVIF_VERSION < 1000000
0554     int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
0555     int minQuantizer = 0;
0556     int maxQuantizerAlpha = 0;
0557 #endif
0558     avifResult res;
0559 
0560     bool save_grayscale; // true - monochrome, false - colors
0561     int save_depth; // 8 or 10bit per channel
0562     QImage::Format tmpformat; // format for temporary image
0563 
0564     avifImage *avif = nullptr;
0565 
0566     // grayscale detection
0567     switch (image.format()) {
0568     case QImage::Format_Mono:
0569     case QImage::Format_MonoLSB:
0570     case QImage::Format_Grayscale8:
0571     case QImage::Format_Grayscale16:
0572         save_grayscale = true;
0573         break;
0574     case QImage::Format_Indexed8:
0575         save_grayscale = image.isGrayscale();
0576         break;
0577     default:
0578         save_grayscale = false;
0579         break;
0580     }
0581 
0582     // depth detection
0583     switch (image.format()) {
0584     case QImage::Format_BGR30:
0585     case QImage::Format_A2BGR30_Premultiplied:
0586     case QImage::Format_RGB30:
0587     case QImage::Format_A2RGB30_Premultiplied:
0588     case QImage::Format_Grayscale16:
0589     case QImage::Format_RGBX64:
0590     case QImage::Format_RGBA64:
0591     case QImage::Format_RGBA64_Premultiplied:
0592         save_depth = 10;
0593         break;
0594     default:
0595         if (image.depth() > 32) {
0596             save_depth = 10;
0597         } else {
0598             save_depth = 8;
0599         }
0600         break;
0601     }
0602 
0603 #if AVIF_VERSION < 1000000
0604     // deprecated quality settings
0605     if (maxQuantizer > 20) {
0606         minQuantizer = maxQuantizer - 20;
0607         if (maxQuantizer > 40) { // we decrease quality of alpha channel here
0608             maxQuantizerAlpha = maxQuantizer - 40;
0609         }
0610     }
0611 #endif
0612 
0613     if (save_grayscale && !image.hasAlphaChannel()) { // we are going to save grayscale image without alpha channel
0614         if (save_depth > 8) {
0615             tmpformat = QImage::Format_Grayscale16;
0616         } else {
0617             tmpformat = QImage::Format_Grayscale8;
0618         }
0619         QImage tmpgrayimage = image.convertToFormat(tmpformat);
0620 
0621         avif = avifImageCreate(tmpgrayimage.width(), tmpgrayimage.height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
0622         avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
0623 
0624         if (tmpgrayimage.colorSpace().isValid()) {
0625             avif->colorPrimaries = (avifColorPrimaries)1;
0626             avif->matrixCoefficients = (avifMatrixCoefficients)1;
0627 
0628             switch (tmpgrayimage.colorSpace().transferFunction()) {
0629             case QColorSpace::TransferFunction::Linear:
0630                 /* AVIF_TRANSFER_CHARACTERISTICS_LINEAR */
0631                 avif->transferCharacteristics = (avifTransferCharacteristics)8;
0632                 break;
0633             case QColorSpace::TransferFunction::SRgb:
0634                 /* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
0635                 avif->transferCharacteristics = (avifTransferCharacteristics)13;
0636                 break;
0637             default:
0638                 /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
0639                 break;
0640             }
0641         }
0642 
0643         if (save_depth > 8) { // QImage::Format_Grayscale16
0644             for (int y = 0; y < tmpgrayimage.height(); y++) {
0645                 const uint16_t *src16bit = reinterpret_cast<const uint16_t *>(tmpgrayimage.constScanLine(y));
0646                 uint16_t *dest16bit = reinterpret_cast<uint16_t *>(avif->yuvPlanes[0] + y * avif->yuvRowBytes[0]);
0647                 for (int x = 0; x < tmpgrayimage.width(); x++) {
0648                     int tmp_pixelval = (int)(((float)(*src16bit) / 65535.0f) * 1023.0f + 0.5f); // downgrade to 10 bits
0649                     *dest16bit = qBound(0, tmp_pixelval, 1023);
0650                     dest16bit++;
0651                     src16bit++;
0652                 }
0653             }
0654         } else { // QImage::Format_Grayscale8
0655             for (int y = 0; y < tmpgrayimage.height(); y++) {
0656                 const uchar *src8bit = tmpgrayimage.constScanLine(y);
0657                 uint8_t *dest8bit = avif->yuvPlanes[0] + y * avif->yuvRowBytes[0];
0658                 for (int x = 0; x < tmpgrayimage.width(); x++) {
0659                     *dest8bit = *src8bit;
0660                     dest8bit++;
0661                     src8bit++;
0662                 }
0663             }
0664         }
0665 
0666     } else { // we are going to save color image
0667         if (save_depth > 8) {
0668             if (image.hasAlphaChannel()) {
0669                 tmpformat = QImage::Format_RGBA64;
0670             } else {
0671                 tmpformat = QImage::Format_RGBX64;
0672             }
0673         } else { // 8bit depth
0674             if (image.hasAlphaChannel()) {
0675                 tmpformat = QImage::Format_RGBA8888;
0676             } else {
0677                 tmpformat = QImage::Format_RGB888;
0678             }
0679         }
0680 
0681         QImage tmpcolorimage = image.convertToFormat(tmpformat);
0682 
0683         avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
0684         if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
0685             if (m_quality >= KIMG_AVIF_QUALITY_BEST) {
0686                 pixel_format = AVIF_PIXEL_FORMAT_YUV444; // best quality
0687             } else {
0688                 pixel_format = AVIF_PIXEL_FORMAT_YUV422; // high quality
0689             }
0690         }
0691 
0692         avifMatrixCoefficients matrix_to_save = (avifMatrixCoefficients)1; // default for Qt 5.12 and 5.13;
0693 
0694         avifColorPrimaries primaries_to_save = (avifColorPrimaries)2;
0695         avifTransferCharacteristics transfer_to_save = (avifTransferCharacteristics)2;
0696         QByteArray iccprofile;
0697 
0698         if (tmpcolorimage.colorSpace().isValid()) {
0699             switch (tmpcolorimage.colorSpace().primaries()) {
0700             case QColorSpace::Primaries::SRgb:
0701                 /* AVIF_COLOR_PRIMARIES_BT709 */
0702                 primaries_to_save = (avifColorPrimaries)1;
0703                 /* AVIF_MATRIX_COEFFICIENTS_BT709 */
0704                 matrix_to_save = (avifMatrixCoefficients)1;
0705                 break;
0706             case QColorSpace::Primaries::DciP3D65:
0707                 /* AVIF_NCLX_COLOUR_PRIMARIES_P3, AVIF_NCLX_COLOUR_PRIMARIES_SMPTE432 */
0708                 primaries_to_save = (avifColorPrimaries)12;
0709                 /* AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL */
0710                 matrix_to_save = (avifMatrixCoefficients)12;
0711                 break;
0712             default:
0713                 /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
0714                 primaries_to_save = (avifColorPrimaries)2;
0715                 /* AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED */
0716                 matrix_to_save = (avifMatrixCoefficients)2;
0717                 break;
0718             }
0719 
0720             switch (tmpcolorimage.colorSpace().transferFunction()) {
0721             case QColorSpace::TransferFunction::Linear:
0722                 /* AVIF_TRANSFER_CHARACTERISTICS_LINEAR */
0723                 transfer_to_save = (avifTransferCharacteristics)8;
0724                 break;
0725             case QColorSpace::TransferFunction::Gamma:
0726                 if (qAbs(tmpcolorimage.colorSpace().gamma() - 2.2f) < 0.1f) {
0727                     /* AVIF_TRANSFER_CHARACTERISTICS_BT470M */
0728                     transfer_to_save = (avifTransferCharacteristics)4;
0729                 } else if (qAbs(tmpcolorimage.colorSpace().gamma() - 2.8f) < 0.1f) {
0730                     /* AVIF_TRANSFER_CHARACTERISTICS_BT470BG */
0731                     transfer_to_save = (avifTransferCharacteristics)5;
0732                 } else {
0733                     /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
0734                     transfer_to_save = (avifTransferCharacteristics)2;
0735                 }
0736                 break;
0737             case QColorSpace::TransferFunction::SRgb:
0738                 /* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
0739                 transfer_to_save = (avifTransferCharacteristics)13;
0740                 break;
0741             default:
0742                 /* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
0743                 transfer_to_save = (avifTransferCharacteristics)2;
0744                 break;
0745             }
0746 
0747             // in case primaries or trc were not identified
0748             if ((primaries_to_save == 2) || (transfer_to_save == 2)) {
0749                 if (lossless) {
0750                     iccprofile = tmpcolorimage.colorSpace().iccProfile();
0751                 } else {
0752                     // upgrade image to higher bit depth
0753                     if (save_depth == 8) {
0754                         save_depth = 10;
0755                         if (tmpcolorimage.hasAlphaChannel()) {
0756                             tmpcolorimage.convertTo(QImage::Format_RGBA64);
0757                         } else {
0758                             tmpcolorimage.convertTo(QImage::Format_RGBX64);
0759                         }
0760                     }
0761 
0762                     if ((primaries_to_save == 2) && (transfer_to_save != 2)) { // other primaries but known trc
0763                         primaries_to_save = (avifColorPrimaries)1; // AVIF_COLOR_PRIMARIES_BT709
0764                         matrix_to_save = (avifMatrixCoefficients)1; // AVIF_MATRIX_COEFFICIENTS_BT709
0765 
0766                         switch (transfer_to_save) {
0767                         case 8: // AVIF_TRANSFER_CHARACTERISTICS_LINEAR
0768                             tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::Linear));
0769                             break;
0770                         case 4: // AVIF_TRANSFER_CHARACTERISTICS_BT470M
0771                             tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, 2.2f));
0772                             break;
0773                         case 5: // AVIF_TRANSFER_CHARACTERISTICS_BT470BG
0774                             tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, 2.8f));
0775                             break;
0776                         default: // AVIF_TRANSFER_CHARACTERISTICS_SRGB + any other
0777                             tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::SRgb));
0778                             transfer_to_save = (avifTransferCharacteristics)13;
0779                             break;
0780                         }
0781                     } else if ((primaries_to_save != 2) && (transfer_to_save == 2)) { // recognized primaries but other trc
0782                         transfer_to_save = (avifTransferCharacteristics)13;
0783                         tmpcolorimage.convertToColorSpace(tmpcolorimage.colorSpace().withTransferFunction(QColorSpace::TransferFunction::SRgb));
0784                     } else { // unrecognized profile
0785                         primaries_to_save = (avifColorPrimaries)1; // AVIF_COLOR_PRIMARIES_BT709
0786                         transfer_to_save = (avifTransferCharacteristics)13;
0787                         matrix_to_save = (avifMatrixCoefficients)1; // AVIF_MATRIX_COEFFICIENTS_BT709
0788                         tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::SRgb));
0789                     }
0790                 }
0791             }
0792         } else { // profile is unsupported by Qt
0793             iccprofile = tmpcolorimage.colorSpace().iccProfile();
0794             if (iccprofile.size() > 0) {
0795                 matrix_to_save = (avifMatrixCoefficients)6;
0796             }
0797         }
0798 
0799         if (lossless && pixel_format == AVIF_PIXEL_FORMAT_YUV444) {
0800             matrix_to_save = (avifMatrixCoefficients)0;
0801         }
0802         avif = avifImageCreate(tmpcolorimage.width(), tmpcolorimage.height(), save_depth, pixel_format);
0803         avif->matrixCoefficients = matrix_to_save;
0804 
0805         avif->colorPrimaries = primaries_to_save;
0806         avif->transferCharacteristics = transfer_to_save;
0807 
0808         if (iccprofile.size() > 0) {
0809             avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
0810         }
0811 
0812         avifRGBImage rgb;
0813         avifRGBImageSetDefaults(&rgb, avif);
0814         rgb.rowBytes = tmpcolorimage.bytesPerLine();
0815         rgb.pixels = const_cast<uint8_t *>(tmpcolorimage.constBits());
0816 
0817         if (save_depth > 8) { // 10bit depth
0818             rgb.depth = 16;
0819 
0820             if (!tmpcolorimage.hasAlphaChannel()) {
0821                 rgb.ignoreAlpha = AVIF_TRUE;
0822             }
0823 
0824             rgb.format = AVIF_RGB_FORMAT_RGBA;
0825         } else { // 8bit depth
0826             rgb.depth = 8;
0827 
0828             if (tmpcolorimage.hasAlphaChannel()) {
0829                 rgb.format = AVIF_RGB_FORMAT_RGBA;
0830             } else {
0831                 rgb.format = AVIF_RGB_FORMAT_RGB;
0832             }
0833         }
0834 
0835         res = avifImageRGBToYUV(avif, &rgb);
0836         if (res != AVIF_RESULT_OK) {
0837             qWarning("ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
0838             return false;
0839         }
0840     }
0841 
0842     avifRWData raw = AVIF_DATA_EMPTY;
0843     avifEncoder *encoder = avifEncoderCreate();
0844     encoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
0845 
0846 #if AVIF_VERSION < 1000000
0847     encoder->minQuantizer = minQuantizer;
0848     encoder->maxQuantizer = maxQuantizer;
0849 
0850     if (image.hasAlphaChannel()) {
0851         encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
0852         encoder->maxQuantizerAlpha = maxQuantizerAlpha;
0853     }
0854 #else
0855     encoder->quality = m_quality;
0856 
0857     if (image.hasAlphaChannel()) {
0858         if (m_quality >= KIMG_AVIF_QUALITY_LOW) {
0859             encoder->qualityAlpha = 100;
0860         } else {
0861             encoder->qualityAlpha = 100 - (KIMG_AVIF_QUALITY_LOW - m_quality) / 2;
0862         }
0863     }
0864 #endif
0865 
0866     encoder->speed = 6;
0867 
0868     res = avifEncoderWrite(encoder, avif, &raw);
0869     avifEncoderDestroy(encoder);
0870     avifImageDestroy(avif);
0871 
0872     if (res == AVIF_RESULT_OK) {
0873         qint64 status = device()->write(reinterpret_cast<const char *>(raw.data), raw.size);
0874         avifRWDataFree(&raw);
0875 
0876         if (status > 0) {
0877             return true;
0878         } else if (status == -1) {
0879             qWarning("Write error: %s", qUtf8Printable(device()->errorString()));
0880             return false;
0881         }
0882     } else {
0883         qWarning("ERROR: Failed to encode: %s", avifResultToString(res));
0884     }
0885 
0886     return false;
0887 }
0888 
0889 QVariant QAVIFHandler::option(ImageOption option) const
0890 {
0891     if (option == Quality) {
0892         return m_quality;
0893     }
0894 
0895     if (!supportsOption(option) || !ensureParsed()) {
0896         return QVariant();
0897     }
0898 
0899     switch (option) {
0900     case Size:
0901         return m_estimated_dimensions;
0902     case Animation:
0903         if (imageCount() >= 2) {
0904             return true;
0905         } else {
0906             return false;
0907         }
0908     default:
0909         return QVariant();
0910     }
0911 }
0912 
0913 void QAVIFHandler::setOption(ImageOption option, const QVariant &value)
0914 {
0915     switch (option) {
0916     case Quality:
0917         m_quality = value.toInt();
0918         if (m_quality > 100) {
0919             m_quality = 100;
0920         } else if (m_quality < 0) {
0921             m_quality = KIMG_AVIF_DEFAULT_QUALITY;
0922         }
0923         return;
0924     default:
0925         break;
0926     }
0927     QImageIOHandler::setOption(option, value);
0928 }
0929 
0930 bool QAVIFHandler::supportsOption(ImageOption option) const
0931 {
0932     return option == Quality || option == Size || option == Animation;
0933 }
0934 
0935 int QAVIFHandler::imageCount() const
0936 {
0937     if (!ensureParsed()) {
0938         return 0;
0939     }
0940 
0941     if (m_decoder->imageCount >= 1) {
0942         return m_decoder->imageCount;
0943     }
0944     return 0;
0945 }
0946 
0947 int QAVIFHandler::currentImageNumber() const
0948 {
0949     if (m_parseState == ParseAvifNotParsed) {
0950         return -1;
0951     }
0952 
0953     if (m_parseState == ParseAvifError || !m_decoder) {
0954         return 0;
0955     }
0956 
0957     if (m_parseState == ParseAvifMetadata) {
0958         if (m_decoder->imageCount >= 2) {
0959             return -1;
0960         } else {
0961             return 0;
0962         }
0963     }
0964 
0965     return m_decoder->imageIndex;
0966 }
0967 
0968 bool QAVIFHandler::jumpToNextImage()
0969 {
0970     if (!ensureParsed()) {
0971         return false;
0972     }
0973 
0974     if (m_decoder->imageIndex >= 0) {
0975         if (m_decoder->imageCount < 2) {
0976             m_parseState = ParseAvifSuccess;
0977             return true;
0978         }
0979 
0980         if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
0981             avifDecoderReset(m_decoder);
0982         }
0983     }
0984 
0985     avifResult decodeResult = avifDecoderNextImage(m_decoder);
0986 
0987     if (decodeResult != AVIF_RESULT_OK) {
0988         qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
0989         m_parseState = ParseAvifError;
0990         return false;
0991     }
0992 
0993     if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
0994         qWarning("Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
0995                  m_decoder->image->width,
0996                  m_decoder->image->height,
0997                  m_container_width,
0998                  m_container_height);
0999 
1000         m_parseState = ParseAvifError;
1001         return false;
1002     }
1003 
1004     if (decode_one_frame()) {
1005         m_parseState = ParseAvifSuccess;
1006         return true;
1007     } else {
1008         m_parseState = ParseAvifError;
1009         return false;
1010     }
1011 }
1012 
1013 bool QAVIFHandler::jumpToImage(int imageNumber)
1014 {
1015     if (!ensureParsed()) {
1016         return false;
1017     }
1018 
1019     if (m_decoder->imageCount < 2) { // not an animation
1020         if (imageNumber == 0) {
1021             if (ensureOpened()) {
1022                 m_parseState = ParseAvifSuccess;
1023                 return true;
1024             }
1025         }
1026         return false;
1027     }
1028 
1029     if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) { // wrong index
1030         return false;
1031     }
1032 
1033     if (imageNumber == m_decoder->imageIndex) { // we are here already
1034         m_must_jump_to_next_image = false;
1035         m_parseState = ParseAvifSuccess;
1036         return true;
1037     }
1038 
1039     avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber);
1040 
1041     if (decodeResult != AVIF_RESULT_OK) {
1042         qWarning("ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
1043         m_parseState = ParseAvifError;
1044         return false;
1045     }
1046 
1047     if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
1048         qWarning("Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
1049                  m_decoder->image->width,
1050                  m_decoder->image->height,
1051                  m_container_width,
1052                  m_container_height);
1053 
1054         m_parseState = ParseAvifError;
1055         return false;
1056     }
1057 
1058     if (decode_one_frame()) {
1059         m_parseState = ParseAvifSuccess;
1060         return true;
1061     } else {
1062         m_parseState = ParseAvifError;
1063         return false;
1064     }
1065 }
1066 
1067 int QAVIFHandler::nextImageDelay() const
1068 {
1069     if (!ensureOpened()) {
1070         return 0;
1071     }
1072 
1073     if (m_decoder->imageCount < 2) {
1074         return 0;
1075     }
1076 
1077     int delay_ms = 1000.0 * m_decoder->imageTiming.duration;
1078     if (delay_ms < 1) {
1079         delay_ms = 1;
1080     }
1081     return delay_ms;
1082 }
1083 
1084 int QAVIFHandler::loopCount() const
1085 {
1086     if (!ensureParsed()) {
1087         return 0;
1088     }
1089 
1090     if (m_decoder->imageCount < 2) {
1091         return 0;
1092     }
1093 
1094 #if AVIF_VERSION >= 1000000
1095     if (m_decoder->repetitionCount >= 0) {
1096         return m_decoder->repetitionCount;
1097     }
1098 #endif
1099     // Endless loop to work around https://github.com/AOMediaCodec/libavif/issues/347
1100     return -1;
1101 }
1102 
1103 QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
1104 {
1105     chrX = qBound(qreal(0.0), chrX, qreal(1.0));
1106     chrY = qBound(qreal(DBL_MIN), chrY, qreal(1.0));
1107 
1108     if ((chrX + chrY) > qreal(1.0)) {
1109         chrX = qreal(1.0) - chrY;
1110     }
1111 
1112     return QPointF(chrX, chrY);
1113 }
1114 
1115 QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
1116 {
1117     static const bool isAvifDecoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_DECODE) != nullptr);
1118     static const bool isAvifEncoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE) != nullptr);
1119 
1120     if (format == "avif") {
1121         Capabilities format_cap;
1122         if (isAvifDecoderAvailable) {
1123             format_cap |= CanRead;
1124         }
1125         if (isAvifEncoderAvailable) {
1126             format_cap |= CanWrite;
1127         }
1128         return format_cap;
1129     }
1130 
1131     if (format == "avifs") {
1132         Capabilities format_cap;
1133         if (isAvifDecoderAvailable) {
1134             format_cap |= CanRead;
1135         }
1136         return format_cap;
1137     }
1138 
1139     if (!format.isEmpty()) {
1140         return {};
1141     }
1142     if (!device->isOpen()) {
1143         return {};
1144     }
1145 
1146     Capabilities cap;
1147     if (device->isReadable() && QAVIFHandler::canRead(device) && isAvifDecoderAvailable) {
1148         cap |= CanRead;
1149     }
1150     if (device->isWritable() && isAvifEncoderAvailable) {
1151         cap |= CanWrite;
1152     }
1153     return cap;
1154 }
1155 
1156 QImageIOHandler *QAVIFPlugin::create(QIODevice *device, const QByteArray &format) const
1157 {
1158     QImageIOHandler *handler = new QAVIFHandler;
1159     handler->setDevice(device);
1160     handler->setFormat(format);
1161     return handler;
1162 }
1163 
1164 #include "moc_avif_p.cpp"