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>