File indexing completed on 2024-06-09 04:27:34

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Dirk Farin <farin@struktur.de>
0003  *  SPDX-FileCopyrightText: 2020-2021 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0004  *  SPDX-FileCopyrightText: 2021 Daniel Novomesky <dnovomesky@gmail.com>
0005  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0006  *
0007  *  SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "HeifExport.h"
0011 #include "HeifError.h"
0012 
0013 #include <QApplication>
0014 #include <QBuffer>
0015 #include <QCheckBox>
0016 #include <QFileInfo>
0017 #include <QScopedPointer>
0018 #include <QSlider>
0019 
0020 #include <algorithm>
0021 #include <kpluginfactory.h>
0022 #include <libheif/heif_cxx.h>
0023 
0024 #include <KisDocument.h>
0025 #include <KisExportCheckRegistry.h>
0026 #include <KisImportExportManager.h>
0027 #include <KoColorModelStandardIds.h>
0028 #include <KoColorProfile.h>
0029 #include <KoColorSpaceConstants.h>
0030 #include <KoColorSpaceRegistry.h>
0031 #include <KoColorTransferFunctions.h>
0032 #include <kis_assert.h>
0033 #include <kis_config.h>
0034 #include <kis_exif_info_visitor.h>
0035 #include <kis_group_layer.h>
0036 #include <kis_image.h>
0037 #include <kis_iterator_ng.h>
0038 #include <kis_meta_data_backend_registry.h>
0039 #include <kis_meta_data_entry.h>
0040 #include <kis_meta_data_filter_registry_model.h>
0041 #include <kis_meta_data_schema.h>
0042 #include <kis_meta_data_schema_registry.h>
0043 #include <kis_meta_data_store.h>
0044 #include <kis_meta_data_value.h>
0045 #include <kis_paint_device.h>
0046 #include <kis_paint_layer.h>
0047 #include <kis_properties_configuration.h>
0048 
0049 using heif::Error;
0050 
0051 class KisExternalLayer;
0052 
0053 K_PLUGIN_FACTORY_WITH_JSON(ExportFactory, "krita_heif_export.json", registerPlugin<HeifExport>();)
0054 
0055 HeifExport::HeifExport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent)
0056 {
0057 }
0058 
0059 HeifExport::~HeifExport()
0060 {
0061 }
0062 
0063 KisPropertiesConfigurationSP HeifExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
0064 {
0065     KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
0066     cfg->setProperty("quality", 100);
0067     cfg->setProperty("lossless", true);
0068     cfg->setProperty("chroma", "444");
0069     cfg->setProperty("floatingPointConversionOption", "KeepSame");
0070     cfg->setProperty("monochromeToSRGB", false);
0071     cfg->setProperty("HLGnominalPeak", 1000.0);
0072     cfg->setProperty("HLGgamma", 1.2);
0073     cfg->setProperty("removeHGLOOTF", true);
0074     return cfg;
0075 }
0076 
0077 KisConfigWidget *HeifExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
0078 {
0079     return new KisWdgOptionsHeif(parent);
0080 }
0081 
0082 
0083 
0084 class Writer_QIODevice : public heif::Context::Writer
0085 {
0086 public:
0087     Writer_QIODevice(QIODevice* io)
0088         : m_io(io)
0089     {
0090     }
0091 
0092     heif_error write(const void* data, size_t size) override {
0093         qint64 n = m_io->write(static_cast<const char *>(data),
0094                                static_cast<int>(size));
0095         if (n != static_cast<qint64>(size)) {
0096             QString error = m_io->errorString();
0097 
0098             heif_error err = {
0099                 heif_error_Encoding_error,
0100                 heif_suberror_Cannot_write_output_data,
0101                 "Could not write output data" };
0102 
0103             return err;
0104         }
0105 
0106         struct heif_error heif_error_ok = { heif_error_Ok, heif_suberror_Unspecified, "Success" };
0107         return heif_error_ok;
0108     }
0109 
0110 private:
0111     QIODevice* m_io;
0112 };
0113 
0114 #if LIBHEIF_HAVE_VERSION(1, 13, 0)
0115 class Q_DECL_HIDDEN HeifLock
0116 {
0117 public:
0118     HeifLock()
0119         : p()
0120     {
0121         heif_init(&p);
0122     }
0123 
0124     ~HeifLock()
0125     {
0126         heif_deinit();
0127     }
0128 
0129 private:
0130     heif_init_params p;
0131 };
0132 #endif
0133 
0134 KisImportExportErrorCode HeifExport::convert(KisDocument *document, QIODevice *io,  KisPropertiesConfigurationSP configuration)
0135 {
0136 #if LIBHEIF_HAVE_VERSION(1, 13, 0)
0137     HeifLock lock;
0138 #endif
0139 
0140     KisImageSP image = document->savingImage();
0141     const KoColorSpace *cs = image->colorSpace();
0142 
0143 
0144 
0145     dbgFile << "Starting" << mimeType() << "encoding.";
0146 
0147     bool convertToSRGB = (configuration->getBool("monochromeToSRGB") && cs->colorModelId() == GrayAColorModelID);
0148 
0149     // Convert to 8 bits rgba on saving if not rgba or graya.
0150     if ( (cs->colorModelId() != RGBAColorModelID && cs->colorModelId() != GrayAColorModelID) || convertToSRGB) {
0151         const KoColorSpace *sRgb = KoColorSpaceRegistry::instance()->rgb8();
0152         image->convertImageColorSpace(sRgb,
0153                                       KoColorConversionTransformation::internalRenderingIntent(),
0154                                       KoColorConversionTransformation::internalConversionFlags());
0155     }
0156 
0157     if (cs->colorModelId() == GrayAColorModelID && cs->hasHighDynamicRange() && !convertToSRGB) {
0158         const KoColorSpace *gray = KoColorSpaceRegistry::instance()->graya16(cs->profile()->name());
0159         image->convertImageColorSpace(gray,
0160                                       KoColorConversionTransformation::internalRenderingIntent(),
0161                                       KoColorConversionTransformation::internalConversionFlags());
0162     }
0163 
0164     ConversionPolicy conversionPolicy = ConversionPolicy::KeepTheSame;
0165     bool convertToRec2020 = false;
0166     
0167     if (cs->hasHighDynamicRange() && cs->colorModelId() != GrayAColorModelID) {
0168         QString conversionOption =
0169             (configuration->getString("floatingPointConversionOption",
0170                                       "KeepSame"));
0171         if (conversionOption == "Rec2100PQ") {
0172             convertToRec2020 = true;
0173             conversionPolicy = ConversionPolicy::ApplyPQ;
0174         } else if (conversionOption == "Rec2100HLG") {
0175             convertToRec2020 = true;
0176             conversionPolicy = ConversionPolicy::ApplyHLG;
0177         } else if (conversionOption == "ApplyPQ") {
0178             conversionPolicy = ConversionPolicy::ApplyPQ;
0179         } else if (conversionOption == "ApplyHLG") {
0180             conversionPolicy = ConversionPolicy::ApplyHLG;
0181         }  else if (conversionOption == "ApplySMPTE428") {
0182             conversionPolicy = ConversionPolicy::ApplySMPTE428;
0183         }
0184     }
0185 
0186     if (cs->hasHighDynamicRange() && convertToRec2020) {
0187         const KoColorProfile *linear = KoColorSpaceRegistry::instance()->profileFor(QVector<double>(),
0188                                                                                    PRIMARIES_ITU_R_BT_2020_2_AND_2100_0,
0189                                                                                    TRC_LINEAR);
0190         const KoColorSpace *linearRec2020 = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", linear);
0191         image->convertImageColorSpace(linearRec2020,
0192                                       KoColorConversionTransformation::internalRenderingIntent(),
0193                                       KoColorConversionTransformation::internalConversionFlags());
0194     }
0195 
0196     image->waitForDone();
0197     cs = image->colorSpace();
0198 
0199     int quality = configuration->getInt("quality", 50);
0200     bool lossless = configuration->getBool("lossless", false);
0201     bool hasAlpha = configuration->getBool(KisImportExportFilter::ImageContainsTransparencyTag, false);
0202     float hlgGamma = configuration->getFloat("HLGgamma", 1.2f);
0203     float hlgNominalPeak = configuration->getFloat("HLGnominalPeak", 1000.0f);
0204     bool removeHGLOOTF = configuration->getBool("removeHGLOOTF", true);
0205 
0206     // If we want to add information from the document to the metadata,
0207     // we should do that here.
0208 
0209     try {
0210         // --- use standard HEVC encoder
0211 
0212 
0213         heif::Encoder encoder(heif_compression_HEVC);
0214 
0215 
0216         if (mimeType() == "image/avif") {
0217             encoder = heif::Encoder(heif_compression_AV1);
0218         }
0219 
0220 
0221         encoder.set_lossy_quality(quality);
0222         if (lossless) {
0223             //https://invent.kde.org/graphics/krita/-/merge_requests/530#note_169521
0224             encoder.set_lossy_quality(100);
0225         }
0226         encoder.set_lossless(lossless);
0227         if (cs->colorModelId() != GrayAColorModelID) {
0228         encoder.set_parameter("chroma", configuration->getString("chroma", "444").toStdString());
0229         }
0230 
0231 
0232         // --- convert KisImage to HEIF image ---
0233         int width = image->width();
0234         int height = image->height();
0235 
0236         heif::Context ctx;
0237 
0238         heif_chroma chroma = hasAlpha? heif_chroma_interleaved_RRGGBBAA_LE: heif_chroma_interleaved_RRGGBB_LE;
0239         if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
0240             chroma = hasAlpha? heif_chroma_interleaved_RRGGBBAA_BE: heif_chroma_interleaved_RRGGBB_BE;
0241         }
0242 
0243         heif::Image img;
0244 
0245         if (cs->colorModelId() == RGBAColorModelID) {
0246             if (cs->colorDepthId() == Integer8BitsColorDepthID) {
0247                 dbgFile << "saving as 8bit rgba";
0248                 img.create(width,height, heif_colorspace_RGB, heif_chroma_444);
0249                 img.add_plane(heif_channel_R, width,height, 8);
0250                 img.add_plane(heif_channel_G, width,height, 8);
0251                 img.add_plane(heif_channel_B, width,height, 8);
0252 
0253                 int strideR = 0;
0254                 int strideG = 0;
0255                 int strideB = 0;
0256                 int strideA = 0;
0257 
0258                 uint8_t *ptrR = img.get_plane(heif_channel_R, &strideR);
0259                 uint8_t *ptrG = img.get_plane(heif_channel_G, &strideG);
0260                 uint8_t *ptrB = img.get_plane(heif_channel_B, &strideB);
0261 
0262                 uint8_t *ptrA = [&]() -> uint8_t * {
0263                     if (hasAlpha) {
0264                         img.add_plane(heif_channel_Alpha, width, height, 8);
0265                         return img.get_plane(heif_channel_Alpha, &strideA);
0266                     } else {
0267                         return nullptr;
0268                     }
0269                 }();
0270 
0271                 KisPaintDeviceSP pd = image->projection();
0272                 KisHLineConstIteratorSP it =
0273                     pd->createHLineConstIteratorNG(0, 0, width);
0274 
0275                 Planar::writeLayer(hasAlpha,
0276                                    width,
0277                                    height,
0278                                    ptrR,
0279                                    strideR,
0280                                    ptrG,
0281                                    strideG,
0282                                    ptrB,
0283                                    strideB,
0284                                    ptrA,
0285                                    strideA,
0286                                    it);
0287             } else {
0288                 dbgFile << "Saving as 12bit rgba";
0289                 img.create(width, height, heif_colorspace_RGB, chroma);
0290                 img.add_plane(heif_channel_interleaved, width, height, 12);
0291 
0292                 int stride = 0;
0293 
0294                 uint8_t *ptr = img.get_plane(heif_channel_interleaved, &stride);
0295 
0296                 KisPaintDeviceSP pd = image->projection();
0297                 KisHLineConstIteratorSP it =
0298                     pd->createHLineConstIteratorNG(0, 0, width);
0299 
0300                 if (cs->colorDepthId() == Integer16BitsColorDepthID) {
0301                     HDRInt::writeInterleavedLayer(QSysInfo::ByteOrder,
0302                                                   hasAlpha,
0303                                                   width,
0304                                                   height,
0305                                                   ptr,
0306                                                   stride,
0307                                                   it);
0308                 } else {
0309                     HDRFloat::writeInterleavedLayer(cs->colorDepthId(),
0310                                                     QSysInfo::ByteOrder,
0311                                                     hasAlpha,
0312                                                     convertToRec2020,
0313                                                     cs->profile()->isLinear(),
0314                                                     conversionPolicy,
0315                                                     removeHGLOOTF,
0316                                                     width,
0317                                                     height,
0318                                                     ptr,
0319                                                     stride,
0320                                                     it,
0321                                                     hlgGamma,
0322                                                     hlgNominalPeak,
0323                                                     cs);
0324                 }
0325             }
0326         } else {
0327             if (cs->colorDepthId() == Integer8BitsColorDepthID) {
0328                 dbgFile << "Saving as 8 bit monochrome.";
0329                 img.create(width, height, heif_colorspace_monochrome, heif_chroma_monochrome);
0330 
0331                 img.add_plane(heif_channel_Y, width, height, 8);
0332 
0333                 int strideG = 0;
0334                 int strideA = 0;
0335 
0336                 uint8_t *ptrG = img.get_plane(heif_channel_Y, &strideG);
0337                 uint8_t *ptrA = [&]() -> uint8_t * {
0338                     if (hasAlpha) {
0339                         img.add_plane(heif_channel_Alpha, width, height, 8);
0340                         return img.get_plane(heif_channel_Alpha, &strideA);
0341                     } else {
0342                         return nullptr;
0343                     }
0344                 }();
0345 
0346                 KisPaintDeviceSP pd = image->projection();
0347                 KisHLineConstIteratorSP it =
0348                     pd->createHLineConstIteratorNG(0, 0, width);
0349 
0350                 Gray::writePlanarLayer(QSysInfo::ByteOrder,
0351                                        8,
0352                                        hasAlpha,
0353                                        width,
0354                                        height,
0355                                        ptrG,
0356                                        strideG,
0357                                        ptrA,
0358                                        strideA,
0359                                        it);
0360             } else {
0361                 dbgFile << "Saving as 12 bit monochrome";
0362                 img.create(width, height, heif_colorspace_monochrome, heif_chroma_monochrome);
0363 
0364                 img.add_plane(heif_channel_Y, width, height, 12);
0365 
0366                 int strideG = 0;
0367                 int strideA = 0;
0368 
0369                 uint8_t *ptrG = img.get_plane(heif_channel_Y, &strideG);
0370                 uint8_t *ptrA = [&]() -> uint8_t * {
0371                     if (hasAlpha) {
0372                         img.add_plane(heif_channel_Alpha, width, height, 12);
0373                         return img.get_plane(heif_channel_Alpha, &strideA);
0374                     } else {
0375                         return nullptr;
0376                     }
0377                 }();
0378 
0379                 KisPaintDeviceSP pd = image->projection();
0380                 KisHLineConstIteratorSP it =
0381                     pd->createHLineConstIteratorNG(0, 0, width);
0382 
0383                 Gray::writePlanarLayer(QSysInfo::ByteOrder,
0384                                        12,
0385                                        hasAlpha,
0386                                        width,
0387                                        height,
0388                                        ptrG,
0389                                        strideG,
0390                                        ptrA,
0391                                        strideA,
0392                                        it);
0393             }
0394         }
0395 
0396         // --- save the color profile.
0397         if (conversionPolicy == ConversionPolicy::KeepTheSame) {
0398             QByteArray rawProfileBA = image->colorSpace()->profile()->rawData();
0399             std::vector<uint8_t> rawProfile(rawProfileBA.begin(), rawProfileBA.end());
0400             img.set_raw_color_profile(heif_color_profile_type_prof, rawProfile);
0401         } else {
0402            heif::ColorProfile_nclx nclxDescription;
0403            nclxDescription.set_full_range_flag(true);
0404            nclxDescription.set_matrix_coefficients(heif_matrix_coefficients_RGB_GBR);
0405            if (convertToRec2020) {
0406 #if LIBHEIF_HAVE_VERSION(1, 14, 1)
0407                nclxDescription.set_color_primaries(heif_color_primaries_ITU_R_BT_2020_2_and_2100_0);
0408 #else
0409                nclxDescription.set_color_primaties(heif_color_primaries_ITU_R_BT_2020_2_and_2100_0);
0410 #endif
0411            } else {
0412                 const ColorPrimaries primaries =
0413                     image->colorSpace()->profile()->getColorPrimaries();
0414                 // PRIMARIES_ADOBE_RGB_1998 and higher are not valid for CICP.
0415                 // But this should have already been caught by the KeepTheSame
0416                 // clause...
0417                 KIS_SAFE_ASSERT_RECOVER(primaries <= PRIMARIES_EBU_Tech_3213_E) {
0418                     errFile << "Attempt to export a file with unsupported primaries" << primaries;
0419                     return ImportExportCodes::FormatColorSpaceUnsupported;
0420                 }
0421 #if LIBHEIF_HAVE_VERSION(1, 14, 1)
0422                 nclxDescription.set_color_primaries(heif_color_primaries(primaries));
0423 #else
0424                 nclxDescription.set_color_primaties(heif_color_primaries(primaries));
0425 #endif
0426            }
0427 
0428            if (conversionPolicy == ConversionPolicy::ApplyPQ) {
0429                nclxDescription.set_transfer_characteristics(heif_transfer_characteristic_ITU_R_BT_2100_0_PQ);
0430            } else if (conversionPolicy == ConversionPolicy::ApplyHLG) {
0431                nclxDescription.set_transfer_characteristics(heif_transfer_characteristic_ITU_R_BT_2100_0_HLG);
0432            } else if (conversionPolicy == ConversionPolicy::ApplySMPTE428) {
0433                nclxDescription.set_transfer_characteristics(heif_transfer_characteristic_SMPTE_ST_428_1);
0434            }
0435 
0436            img.set_nclx_color_profile(nclxDescription);
0437         }
0438 
0439 
0440         // --- encode and write image
0441 
0442         heif::Context::EncodingOptions options;
0443 
0444         // iOS gets confused when a heif file contains an nclx.
0445         // but we absolutely need it for hdr.
0446         if (conversionPolicy != ConversionPolicy::KeepTheSame && cs->hasHighDynamicRange()) {
0447             options.macOS_compatibility_workaround_no_nclx_profile = false;
0448         }
0449 
0450         heif::ImageHandle handle = ctx.encode_image(img, encoder, options);
0451 
0452 
0453         // --- add Exif / XMP metadata
0454 
0455         KisExifInfoVisitor exivInfoVisitor;
0456         exivInfoVisitor.visit(image->rootLayer().data());
0457 
0458         QScopedPointer<KisMetaData::Store> metaDataStore;
0459         if (exivInfoVisitor.metaDataCount() == 1) {
0460             metaDataStore.reset(new KisMetaData::Store(*exivInfoVisitor.exifInfo()));
0461         }
0462         else {
0463             metaDataStore.reset(new KisMetaData::Store());
0464         }
0465 
0466         if (!metaDataStore->empty()) {
0467             {
0468                 KisMetaData::IOBackend *exifIO = KisMetadataBackendRegistry::instance()->value("exif");
0469                 QBuffer buffer;
0470                 exifIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else?
0471                 QByteArray data = buffer.data();
0472 
0473                 // Write the data to the file
0474                 if (data.size() > 4) {
0475                     ctx.add_exif_metadata(handle, data.constData(), data.size());
0476                 }
0477             }
0478             {
0479                 KisMetaData::IOBackend *xmpIO = KisMetadataBackendRegistry::instance()->value("xmp");
0480                 QBuffer buffer;
0481                 xmpIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else?
0482                 QByteArray data = buffer.data();
0483 
0484                 // Write the data to the file
0485                 if (data.size() > 0) {
0486                     ctx.add_XMP_metadata(handle, data.constData(), data.size());
0487                 }
0488             }
0489         }
0490 
0491 
0492         // --- write HEIF file
0493 
0494         Writer_QIODevice writer(io);
0495 
0496         ctx.write(writer);
0497     } catch (Error &err) {
0498         return setHeifError(document, err);
0499     }
0500 
0501     return ImportExportCodes::OK;
0502 }
0503 
0504 void HeifExport::initializeCapabilities()
0505 {
0506     // This checks before saving for what the file format supports: anything that is supported needs to be mentioned here
0507 
0508     QList<QPair<KoID, KoID> > supportedColorModels;
0509     addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED));
0510     supportedColorModels << QPair<KoID, KoID>()
0511             << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
0512             << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
0513             << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
0514             << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID)
0515             ;
0516     addSupportedColorModels(supportedColorModels, "HEIF");
0517 }
0518 
0519 void KisWdgOptionsHeif::setConfiguration(const KisPropertiesConfigurationSP cfg)
0520 {
0521     // the export manager should have prepared some info for us!
0522     KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::ImageContainsTransparencyTag));
0523     KIS_SAFE_ASSERT_RECOVER_NOOP(cfg->hasProperty(KisImportExportFilter::ColorModelIDTag));
0524 
0525     QStringList chromaOptions;
0526     chromaOptions << "420" << "422" << "444";
0527     cmbChroma->addItems(chromaOptions);
0528     cmbChroma->setItemData(0, i18nc("@tooltip", "The brightness of the image will be at full resolution, while the colorfulness will be halved in both dimensions."), Qt::ToolTipRole);
0529     cmbChroma->setItemData(1, i18nc("@tooltip", "The brightness of the image will be at full resolution, while the colorfulness will be halved horizontally."), Qt::ToolTipRole);
0530     cmbChroma->setItemData(2, i18nc("@tooltip", "Both brightness and colorfulness of the image will be at full resolution."), Qt::ToolTipRole);
0531     chkLossless->setChecked(cfg->getBool("lossless", true));
0532     sliderQuality->setValue(qreal(cfg->getInt("quality", 50)));
0533     cmbChroma->setCurrentIndex(chromaOptions.indexOf(cfg->getString("chroma", "444")));
0534     m_hasAlpha = cfg->getBool(KisImportExportFilter::ImageContainsTransparencyTag, false);
0535 
0536     int cicpPrimaries = cfg->getInt(KisImportExportFilter::CICPPrimariesTag,
0537                                     static_cast<int>(PRIMARIES_UNSPECIFIED));
0538 
0539     // Rav1e doesn't support monochrome. To get around this, people may need to convert to sRGB first.
0540     chkMonochromesRGB->setVisible(cfg->getString(KisImportExportFilter::ColorModelIDTag) == "GRAYA");
0541     
0542     conversionSettings->setVisible(cfg->getBool(KisImportExportFilter::HDRTag, false));
0543 
0544     QStringList conversionOptionsList = { i18nc("Color space name", "Rec 2100 PQ"), i18nc("Color space name", "Rec 2100 HLG")};
0545     QStringList toolTipList = {i18nc("@tooltip", "The image will be converted to Rec 2020 linear first, and then encoded with a perceptual quantizer curve"
0546                                " (also known as SMPTE 2048 curve). Recommended for HDR images where the absolute brightness is important."),
0547                               i18nc("@tooltip", "The image will be converted to Rec 2020 linear first, and then encoded with a Hybrid Log Gamma curve."
0548                                " Recommended for HDR images where the display may not understand HDR.")};
0549     QStringList conversionOptionName = {"Rec2100PQ", "Rec2100HLG"};
0550     
0551     if (cfg->getString(KisImportExportFilter::ColorModelIDTag) == "RGBA") {
0552         if (cicpPrimaries != PRIMARIES_UNSPECIFIED) {
0553             conversionOptionsList << i18nc("Color space option plus transfer function name", "Keep colorants, encode PQ");
0554             toolTipList << i18nc("@tooltip", "The image will be linearized first, and then encoded with a perceptual quantizer curve"
0555                                             " (also known as the SMPTE 2048 curve). Recommended for images where the absolute brightness is important.");
0556             conversionOptionName << "ApplyPQ";
0557             
0558             conversionOptionsList << i18nc("Color space option plus transfer function name", "Keep colorants, encode HLG");
0559             toolTipList << i18nc("@tooltip", "The image will be linearized first, and then encoded with a Hybrid Log Gamma curve."
0560                                             " Recommended for images intended for screens which cannot understand PQ");
0561             conversionOptionName << "ApplyHLG";
0562             
0563             conversionOptionsList << i18nc("Color space option plus transfer function name", "Keep colorants, encode SMPTE ST 428");
0564             toolTipList << i18nc("@tooltip", "The image will be linearized first, and then encoded with SMPTE ST 428."
0565                                             " Krita always opens images like these as linear floating point, this option is there to reverse that");
0566             conversionOptionName << "ApplySMPTE428";
0567         }
0568 
0569         conversionOptionsList << i18nc("Color space option", "No changes, clip");
0570         toolTipList << i18nc("@tooltip", "The image will be converted plainly to 12bit integer, and values that are out of bounds are clipped, the icc profile will be embedded.");
0571         conversionOptionName << "KeepSame";
0572     }
0573     cmbConversionPolicy->addItems(conversionOptionsList);
0574     for (int i=0; i< toolTipList.size(); i++) {
0575         cmbConversionPolicy->setItemData(i, toolTipList.at(i), Qt::ToolTipRole);
0576         cmbConversionPolicy->setItemData(i, conversionOptionName.at(i), Qt::UserRole+1);
0577     }
0578     QString optionName =
0579         cfg->getString("floatingPointConversionOption", "KeepSame");
0580     if (conversionOptionName.contains(optionName)) {
0581         cmbConversionPolicy->setCurrentIndex(
0582             conversionOptionName.indexOf(optionName));
0583     }
0584     chkHLGOOTF->setChecked(cfg->getBool("removeHGLOOTF", true));
0585     spnNits->setValue(cfg->getDouble("HLGnominalPeak", 1000.0));
0586     spnGamma->setValue(cfg->getDouble("HLGgamma", 1.2));
0587 }
0588 
0589 KisPropertiesConfigurationSP KisWdgOptionsHeif::configuration() const
0590 {
0591     KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
0592     cfg->setProperty("lossless", chkLossless->isChecked());
0593     cfg->setProperty("quality", int(sliderQuality->value()));
0594     cfg->setProperty("chroma", cmbChroma->currentText());
0595     cfg->setProperty("floatingPointConversionOption", cmbConversionPolicy->currentData(Qt::UserRole+1).toString());
0596     cfg->setProperty("monochromeToSRGB", chkMonochromesRGB->isChecked());
0597     cfg->setProperty("HLGnominalPeak", spnNits->value());
0598     cfg->setProperty("HLGgamma", spnGamma->value());
0599     cfg->setProperty("removeHGLOOTF", chkHLGOOTF->isChecked());
0600     cfg->setProperty(KisImportExportFilter::ImageContainsTransparencyTag, m_hasAlpha);
0601     return cfg;
0602 }
0603 
0604 void KisWdgOptionsHeif::toggleQualitySlider(bool toggle)
0605 {
0606     // Disable the quality slider if lossless is true
0607     lossySettings->setEnabled(!toggle);
0608 }
0609 
0610 void KisWdgOptionsHeif::toggleHLGOptions(bool toggle)
0611 {
0612     spnNits->setEnabled(toggle);
0613     spnGamma->setEnabled(toggle);
0614 }
0615 
0616 void KisWdgOptionsHeif::toggleExtraHDROptions(int index) {
0617     Q_UNUSED(index)
0618     bool toggle = cmbConversionPolicy->currentData(Qt::UserRole+1).toString().contains("HLG");
0619     chkHLGOOTF->setEnabled(toggle);
0620     spnNits->setEnabled(toggle);
0621     spnGamma->setEnabled(toggle);
0622 }
0623 #include <HeifExport.moc>