Krita Source Code Documentation
Loading...
Searching...
No Matches
JPEGXLExport Class Reference

#include <JPEGXLExport.h>

+ Inheritance diagram for JPEGXLExport:

Public Member Functions

KisImportExportErrorCode convert (KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP cfg=nullptr) override
 
KisConfigWidgetcreateConfigurationWidget (QWidget *parent, const QByteArray &from="", const QByteArray &to="") const override
 createConfigurationWidget creates a widget that can be used to define the settings for a given import/export filter
 
KisPropertiesConfigurationSP defaultConfiguration (const QByteArray &from="", const QByteArray &to="") const override
 defaultConfiguration defines the default settings for the given import export filter
 
void initializeCapabilities () override
 
 JPEGXLExport (QObject *parent, const QVariantList &)
 
 ~JPEGXLExport () override=default
 
- Public Member Functions inherited from KisImportExportFilter
virtual QMap< QString, KisExportCheckBase * > exportChecks ()
 generate and return the list of capabilities of this export filter. The list
 
virtual bool exportSupportsGuides () const
 exportSupportsGuides Because guides are in the document and not the image, checking for guides cannot be made an exportCheck.
 
KisPropertiesConfigurationSP lastSavedConfiguration (const QByteArray &from="", const QByteArray &to="") const
 lastSavedConfiguration return the last saved configuration for this filter
 
 Private ()
 
void setBatchMode (bool batchmode)
 
void setFilename (const QString &filename)
 
void setImportUserFeedBackInterface (KisImportUserFeedbackInterface *interface)
 
void setMimeType (const QString &mime)
 
void setRealFilename (const QString &filename)
 
void setUpdater (QPointer< KoUpdater > updater)
 
virtual bool supportsIO () const
 Override and return false for the filters that use a library that cannot handle file handles, only file names.
 
QPointer< KoUpdaterupdater ()
 
virtual QString verify (const QString &fileName) const
 Verify whether the given file is correct and readable.
 
 ~KisImportExportFilter () override
 
 ~Private ()
 

Additional Inherited Members

- Public Attributes inherited from KisImportExportFilter
bool batchmode
 
QMap< QString, KisExportCheckBase * > capabilities
 
QString filename
 
KisImportUserFeedbackInterfaceimportUserFeedBackInterface {nullptr}
 
QByteArray mime
 
QString realFilename
 
QPointer< KoUpdaterupdater
 
- Static Public Attributes inherited from KisImportExportFilter
static const QString CICPPrimariesTag = "CICPCompatiblePrimaries"
 
static const QString CICPTransferCharacteristicsTag = "CICPCompatibleTransferFunction"
 
static const QString ColorDepthIDTag = "ColorDepthID"
 
static const QString ColorModelIDTag = "ColorModelID"
 
static const QString HDRTag = "HDRSupported"
 
static const QString ImageContainsTransparencyTag = "ImageContainsTransparency"
 
static const QString sRGBTag = "sRGB"
 
- Protected Member Functions inherited from KisImportExportFilter
void addCapability (KisExportCheckBase *capability)
 
void addSupportedColorModels (QList< QPair< KoID, KoID > > supportedColorModels, const QString &name, KisExportCheckBase::Level level=KisExportCheckBase::PARTIALLY)
 
bool batchMode () const
 
QString filename () const
 
KisImportUserFeedbackInterfaceimportUserFeedBackInterface () const
 
 KisImportExportFilter (QObject *parent=0)
 
QByteArray mimeType () const
 
QString realFilename () const
 
void setProgress (int value)
 
QString verifyZiPBasedFiles (const QString &fileName, const QStringList &filesToCheck) const
 

Detailed Description

Definition at line 12 of file JPEGXLExport.h.

Constructor & Destructor Documentation

◆ JPEGXLExport()

JPEGXLExport::JPEGXLExport ( QObject * parent,
const QVariantList &  )

Definition at line 63 of file JPEGXLExport.cpp.

64 : KisImportExportFilter(parent)
65{
66}
KisImportExportFilter(QObject *parent=0)

◆ ~JPEGXLExport()

JPEGXLExport::~JPEGXLExport ( )
overridedefault

Member Function Documentation

◆ convert()

KisImportExportErrorCode JPEGXLExport::convert ( KisDocument * document,
QIODevice * io,
KisPropertiesConfigurationSP configuration = nullptr )
overridevirtual

The filter chain calls this method to perform the actual conversion. The passed mimetypes should be a pair of those you specified in your .desktop file. You have to implement this method to make the filter work.

Returns
The error status, see the #ConversionStatus enum. KisImportExportFilter::OK means that everything is alright.

Implements KisImportExportFilter.

Definition at line 68 of file JPEGXLExport.cpp.

69{
71
72 dbgFile << QString("libjxl version: %1.%2.%3")
73 .arg(JPEGXL_MAJOR_VERSION)
74 .arg(JPEGXL_MINOR_VERSION)
75 .arg(JPEGXL_PATCH_VERSION);
76
77 KisImageSP image = document->savingImage();
78 const QRect bounds = image->bounds();
79
80 const bool cfgFlattenLayer = cfg->getBool("flattenLayers", true);
81 const bool cfgHaveAnimation = cfg->getBool("haveAnimation", false);
82 const bool cfgMultiLayer = cfg->getBool("multiLayer", false);
83 const bool cfgMultiPage = cfg->getBool("multiPage", false);
84
85 auto enc = JxlEncoderMake(nullptr);
86 auto runner = JxlResizableParallelRunnerMake(nullptr);
87 if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(), JxlResizableParallelRunner, runner.get())) {
88 errFile << "JxlEncoderSetParallelRunner failed";
90 }
91
92 JxlResizableParallelRunnerSetThreads(runner.get(),
93 JxlResizableParallelRunnerSuggestThreads(static_cast<uint64_t>(bounds.width()), static_cast<uint64_t>(bounds.height())));
94
95 const KoColorSpace *cs = image->colorSpace();
97 bool convertToRec2020 = false;
98
100 const QString conversionOption = (cfg->getString("floatingPointConversionOption", "Rec2100PQ"));
101 if (conversionOption == "Rec2100PQ") {
102 convertToRec2020 = true;
103 conversionPolicy = ConversionPolicy::ApplyPQ;
104 } else if (conversionOption == "Rec2100HLG") {
105 convertToRec2020 = true;
106 conversionPolicy = ConversionPolicy::ApplyHLG;
107 } else if (conversionOption == "ApplyPQ") {
108 conversionPolicy = ConversionPolicy::ApplyPQ;
109 } else if (conversionOption == "ApplyHLG") {
110 conversionPolicy = ConversionPolicy::ApplyHLG;
111 } else if (conversionOption == "ApplySMPTE428") {
112 conversionPolicy = ConversionPolicy::ApplySMPTE428;
113 }
114 }
115
116 if (cs->hasHighDynamicRange() && convertToRec2020) {
117 const KoColorProfile *linear =
119 KIS_ASSERT_RECOVER(linear)
120 {
121 errFile << "Unable to find a working profile for Rec. 2020";
123 }
124 const KoColorSpace *linearRec2020 =
126 image->convertImageColorSpace(linearRec2020,
129
130 image->waitForDone();
131 cs = image->colorSpace();
132 }
133
134 const float hlgGamma = cfg->getFloat("HLGgamma", 1.2f);
135 const float hlgNominalPeak = cfg->getFloat("HLGnominalPeak", 1000.0f);
136 const bool removeHGLOOTF = cfg->getBool("removeHGLOOTF", true);
137
138 const bool hasPrimaries = cs->profile()->hasColorants();
140 static constexpr std::array<TransferCharacteristics, 14> supportedTRC = {TRC_LINEAR,
153 TRC_A98};
154 const bool isSupportedTRC = std::find(supportedTRC.begin(), supportedTRC.end(), gamma) != supportedTRC.end();
155
156#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 10, 1)
157 JXLExpTool::JxlOutputProcessor processor(io);
158 if (JXL_ENC_SUCCESS != JxlEncoderSetOutputProcessor(enc.get(), processor.getOutputProcessor())) {
159 errFile << "JxlEncoderSetOutputProcessor failed";
161 }
162#endif
163
164 const JxlPixelFormat pixelFormat = [&]() {
165 JxlPixelFormat pixelFormat{};
167 pixelFormat.data_type = JXL_TYPE_UINT8;
168 } else if (conversionPolicy != ConversionPolicy::KeepTheSame
170 pixelFormat.data_type = JXL_TYPE_UINT16;
171#ifdef HAVE_OPENEXR
172 } else if (cs->colorDepthId() == Float16BitsColorDepthID) {
173 pixelFormat.data_type = JXL_TYPE_FLOAT16;
174#endif
175 } else if (cs->colorDepthId() == Float32BitsColorDepthID) {
176 pixelFormat.data_type = JXL_TYPE_FLOAT;
177 }
178 if (cs->colorModelId() == RGBAColorModelID) {
179 pixelFormat.num_channels = 4;
180 } else if (cs->colorModelId() == GrayAColorModelID) {
181 pixelFormat.num_channels = 2;
182 } else if (cs->colorModelId() == CMYKAColorModelID) {
183 pixelFormat.num_channels = 3;
184 }
185 return pixelFormat;
186 }();
187
188 if (JXL_ENC_SUCCESS != JxlEncoderUseBoxes(enc.get())) {
189 errFile << "JxlEncoderUseBoxes failed";
191 }
192
193 const auto basicInfo = [&]() {
194 auto info{std::make_unique<JxlBasicInfo>()};
195 JxlEncoderInitBasicInfo(info.get());
196 info->xsize = static_cast<uint32_t>(bounds.width());
197 info->ysize = static_cast<uint32_t>(bounds.height());
198 {
199 if (pixelFormat.data_type == JXL_TYPE_UINT8) {
200 info->bits_per_sample = 8;
201 info->exponent_bits_per_sample = 0;
202 info->alpha_bits = 8;
203 info->alpha_exponent_bits = 0;
204 } else if (pixelFormat.data_type == JXL_TYPE_UINT16) {
205 info->bits_per_sample = 16;
206 info->exponent_bits_per_sample = 0;
207 info->alpha_bits = 16;
208 info->alpha_exponent_bits = 0;
209#ifdef HAVE_OPENEXR
210 } else if (pixelFormat.data_type == JXL_TYPE_FLOAT16) {
211 info->bits_per_sample = 16;
212 info->exponent_bits_per_sample = 5;
213 info->alpha_bits = 16;
214 info->alpha_exponent_bits = 5;
215#endif
216 } else if (pixelFormat.data_type == JXL_TYPE_FLOAT) {
217 info->bits_per_sample = 32;
218 info->exponent_bits_per_sample = 8;
219 info->alpha_bits = 32;
220 info->alpha_exponent_bits = 8;
221 }
222 }
223 if (cs->colorModelId() == RGBAColorModelID) {
224 info->num_color_channels = 3;
225 info->num_extra_channels = 1;
226 } else if (cs->colorModelId() == GrayAColorModelID) {
227 info->num_color_channels = 1;
228 info->num_extra_channels = 1;
229 } else if (cs->colorModelId() == CMYKAColorModelID) {
230 info->num_color_channels = 3;
231 info->num_extra_channels = 2;
232 }
233 // Use original profile on lossless, non-matrix profile or unsupported transfer curve.
234 if (cfg->getBool("lossless") || (!hasPrimaries && !(cs->colorModelId() == GrayAColorModelID))
235 || !isSupportedTRC) {
236 info->uses_original_profile = JXL_TRUE;
237 dbgFile << "JXL use original profile";
238 } else {
239 info->uses_original_profile = JXL_FALSE;
240 dbgFile << "JXL use internal XYB profile";
241 }
242 if (image->animationInterface()->hasAnimation() && cfgHaveAnimation) {
243 info->have_animation = JXL_TRUE;
244 info->animation.have_timecodes = JXL_FALSE;
245 info->animation.num_loops = 0;
246 // Unlike WebP, JXL does allow for setting proper frame rates.
247 info->animation.tps_numerator =
248 static_cast<uint32_t>(image->animationInterface()->framerate());
249 info->animation.tps_denominator = 1;
250 } else if (cfgMultiPage) {
251 info->have_animation = JXL_TRUE;
252 info->animation.have_timecodes = JXL_FALSE;
253 info->animation.num_loops = 0;
254 info->animation.tps_numerator = 1;
255 info->animation.tps_denominator = 1;
256 }
257 return info;
258 }();
259
260 if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), basicInfo.get())) {
261 errFile << "JxlEncoderSetBasicInfo failed";
263 }
264
265 // CMYKA extra channel info
266 if (cs->colorModelId() == CMYKAColorModelID) {
267 const auto blackInfo = [&]() {
268 auto black{std::make_unique<JxlExtraChannelInfo>()};
269 JxlEncoderInitExtraChannelInfo(JXL_CHANNEL_BLACK, black.get());
270 black->bits_per_sample = basicInfo->bits_per_sample;
271 black->exponent_bits_per_sample = basicInfo->exponent_bits_per_sample;
272 return black;
273 }();
274 const auto alphaInfo = [&]() {
275 auto alpha{std::make_unique<JxlExtraChannelInfo>()};
276 JxlEncoderInitExtraChannelInfo(JXL_CHANNEL_ALPHA, alpha.get());
277 alpha->bits_per_sample = basicInfo->bits_per_sample;
278 alpha->exponent_bits_per_sample = basicInfo->exponent_bits_per_sample;
279 return alpha;
280 }();
281
282 if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelInfo(enc.get(), 0, blackInfo.get())) {
283 errFile << "JxlEncoderSetBasicInfo Key failed";
285 }
286 if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelInfo(enc.get(), 1, alphaInfo.get())) {
287 errFile << "JxlEncoderSetBasicInfo Alpha failed";
289 }
290 }
291
292 {
293 JxlColorEncoding cicpDescription{};
294
295 switch (conversionPolicy) {
297 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
298 break;
300 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
301 break;
303 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_DCI;
304 break;
306 default: {
307 switch (gamma) {
308 case TRC_LINEAR:
309 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
310 break;
314 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_709;
315 break;
317 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
318 cicpDescription.gamma = 1.0 / 2.2;
319 break;
321 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
322 cicpDescription.gamma = 1.0 / 2.8;
323 break;
325 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
326 break;
328 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
329 break;
331 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_DCI;
332 break;
334 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
335 break;
336 case TRC_GAMMA_1_8:
337 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
338 cicpDescription.gamma = 1.0 / 1.8;
339 break;
340 case TRC_GAMMA_2_4:
341 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
342 cicpDescription.gamma = 1.0 / 2.4;
343 break;
344 case TRC_PROPHOTO:
345 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
346 cicpDescription.gamma = 1.0 / 1.8;
347 break;
348 case TRC_A98:
349 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
350 cicpDescription.gamma = 256.0 / 563.0;
351 break;
354 case TRC_SMPTE_240M:
358 case TRC_LAB_L:
359 case TRC_UNSPECIFIED:
360 if (cs->profile()->isLinear()) {
361 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
362 } else {
363 dbgFile << "JXL CICP cannot describe the current transfer function" << gamma
364 << ", falling back to ICC";
365 cicpDescription.transfer_function = JXL_TRANSFER_FUNCTION_UNKNOWN;
366 }
367 break;
368 }
369 } break;
370 }
371
372 const ColorPrimaries primaries = cs->profile()->getColorPrimaries();
373
374 if ((cfg->getBool("lossless") && conversionPolicy == ConversionPolicy::KeepTheSame
375 && !cfg->getBool("forceCicpLossless"))
376 || (!hasPrimaries && !(cs->colorModelId() == GrayAColorModelID)) || !isSupportedTRC) {
377 const QByteArray profile = cs->profile()->rawData();
378
379 dbgFile << "Saving with ICC profile";
380
381 if (JXL_ENC_SUCCESS
382 != JxlEncoderSetICCProfile(enc.get(), reinterpret_cast<const uint8_t *>(profile.constData()), static_cast<size_t>(profile.size()))) {
383 errFile << "JxlEncoderSetICCProfile failed";
385 }
386 } else {
387 dbgFile << "Saving with CICP profile";
388
389 if (cs->colorModelId() == GrayAColorModelID) {
390 // XXX: JXL can't parse custom white point for grayscale (yet) and returned as linear on roundtrip so
391 // let's use default D65 as whitepoint instead...
392 //
393 // See: https://github.com/libjxl/libjxl/issues/1933
394 warnFile << "Using workaround for libjxl grayscale whitepoint";
395 cicpDescription.white_point = JXL_WHITE_POINT_D65;
396 cicpDescription.color_space = JXL_COLOR_SPACE_GRAY;
397 } else {
398 switch (primaries) {
400 cicpDescription.primaries = JXL_PRIMARIES_SRGB;
401 break;
403 cicpDescription.primaries = JXL_PRIMARIES_2100;
404 break;
406 cicpDescription.primaries = JXL_PRIMARIES_P3;
407 break;
408 default:
409 warnFile << "Writing possibly non-roundtrip primaries!";
411 cicpDescription.primaries = JXL_PRIMARIES_CUSTOM;
412 cicpDescription.primaries_red_xy[0] = colorants[0].x;
413 cicpDescription.primaries_red_xy[1] = colorants[0].y;
414 cicpDescription.primaries_green_xy[0] = colorants[1].x;
415 cicpDescription.primaries_green_xy[1] = colorants[1].y;
416 cicpDescription.primaries_blue_xy[0] = colorants[2].x;
417 cicpDescription.primaries_blue_xy[1] = colorants[2].y;
418 break;
419 }
420
421 // Unfortunately, Wolthera never wrote an enum for white points...
422 const KoColorimetryUtils::xyY whitePoint = image->colorSpace()->profile()->getWhitePointxyY();
423 cicpDescription.white_point = JXL_WHITE_POINT_CUSTOM;
424 cicpDescription.white_point_xy[0] = whitePoint.x;
425 cicpDescription.white_point_xy[1] = whitePoint.y;
426 }
427
428 if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &cicpDescription)) {
429 errFile << "JxlEncoderSetColorEncoding failed";
431 }
432 }
433 }
434
435 if (cfg->getBool("storeMetaData", false)) {
436 auto metaDataStore = [&]() -> std::unique_ptr<KisMetaData::Store> {
437 KisExifInfoVisitor exivInfoVisitor;
438 exivInfoVisitor.visit(image->rootLayer().data());
439 if (exivInfoVisitor.metaDataCount() == 1) {
440 return std::make_unique<KisMetaData::Store>(*exivInfoVisitor.exifInfo());
441 } else if (cfg->getBool("storeAuthor", true)) {
442 return std::make_unique<KisMetaData::Store>();
443 } else {
444 return {};
445 }
446 }();
447
448 if (metaDataStore && !metaDataStore->isEmpty()) {
450 model.setEnabledFilters(cfg->getString("filters").split(","));
451 metaDataStore->applyFilters(model.enabledFilters());
452 }
453
454 const KisMetaData::Schema *dcSchema =
456 Q_ASSERT(dcSchema);
457
458 if (cfg->getBool("storeAuthor", true)) {
459 QString author = document->documentInfo()->authorInfo("creator");
460 if (!author.isEmpty()) {
461 if (!document->documentInfo()->authorContactInfo().isEmpty()) {
462 QString contact = document->documentInfo()->authorContactInfo().at(0);
463 if (!contact.isEmpty()) {
464 author = author + "(" + contact + ")";
465 }
466 }
467 if (metaDataStore->containsEntry("creator")) {
468 metaDataStore->removeEntry("creator");
469 }
470 metaDataStore->addEntry(KisMetaData::Entry(dcSchema, "creator", KisMetaData::Value(QVariant(author))));
471 }
472 }
473
474 if (metaDataStore && cfg->getBool("exif", true)) {
476
477 QBuffer ioDevice;
478
479 // Inject the data as any other IOBackend
480 io->saveTo(metaDataStore.get(), &ioDevice);
481
482 if (JXL_ENC_SUCCESS
483 != JxlEncoderAddBox(enc.get(),
484 "Exif",
485 reinterpret_cast<const uint8_t *>(ioDevice.data().constData()),
486 static_cast<size_t>(ioDevice.size()),
487 cfg->getBool("lossless") ? JXL_FALSE : JXL_TRUE)) {
488 errFile << "JxlEncoderAddBox for EXIF failed";
490 }
491 }
492
493 if (metaDataStore && cfg->getBool("xmp", true)) {
495
496 QBuffer ioDevice;
497
498 // Inject the data as any other IOBackend
499 io->saveTo(metaDataStore.get(), &ioDevice);
500
501 if (JXL_ENC_SUCCESS
502 != JxlEncoderAddBox(enc.get(),
503 "xml ",
504 reinterpret_cast<const uint8_t *>(ioDevice.data().constData()),
505 static_cast<size_t>(ioDevice.size()),
506 cfg->getBool("lossless") ? JXL_FALSE : JXL_TRUE)) {
507 errFile << "JxlEncoderAddBox for XMP failed";
509 }
510 }
511
512 if (metaDataStore && cfg->getBool("iptc", true)) {
514
515 QBuffer ioDevice;
516
517 // Inject the data as any other IOBackend
518 io->saveTo(metaDataStore.get(), &ioDevice);
519
520 if (JXL_ENC_SUCCESS
521 != JxlEncoderAddBox(enc.get(),
522 "xml ",
523 reinterpret_cast<const uint8_t *>(ioDevice.data().constData()),
524 static_cast<size_t>(ioDevice.size()),
525 cfg->getBool("lossless") ? JXL_FALSE : JXL_TRUE)) {
526 errFile << "JxlEncoderAddBox for IPTC failed";
528 }
529 }
530 }
531
532 auto *frameSettings = JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
533 {
534 const auto setFrameLossless = [&](bool v) {
535 if (JxlEncoderSetFrameLossless(frameSettings, v ? JXL_TRUE : JXL_FALSE) != JXL_ENC_SUCCESS) {
536 errFile << "JxlEncoderSetFrameLossless failed";
537 return false;
538 }
539 return true;
540 };
541
542 const auto setSetting = [&](JxlEncoderFrameSettingId id, int v) {
543 // https://github.com/libjxl/libjxl/issues/1210
544 if (id == JXL_ENC_FRAME_SETTING_RESAMPLING && v == -1)
545 return true;
546 if (JxlEncoderFrameSettingsSetOption(frameSettings, id, v) != JXL_ENC_SUCCESS) {
547 errFile << "JxlEncoderFrameSettingsSetOption failed";
548 return false;
549 }
550 return true;
551 };
552
553 // Using cjxl quality mapping that translates from arbitrary quality value to JPEG-XL distance
554 const auto setDistance = [&](float v) {
555 const float distance = cfg->getBool("lossless") ? 0.0
556 : v >= 30 ? 0.1 + (100 - v) * 0.09
557 : 53.0 / 3000.0 * v * v - 23.0 / 20.0 * v + 25.0;
558 dbgFile << "libjxl distance equivalent: " << distance;
559 if (JxlEncoderSetFrameDistance(frameSettings, distance) != JXL_ENC_SUCCESS) {
560 errFile << "JxlEncoderSetFrameDistance failed";
561 return false;
562 }
563#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
564 // Lossless alpha (extra channel), only available on libjxl 0.9.0+
565 if (!cfg->getBool("lossless")) {
566 if (cfg->getBool("losslessAlpha")) {
567 if (JxlEncoderSetExtraChannelDistance(frameSettings,
568 (cs->colorModelId() == CMYKAColorModelID) ? 1 : 0,
569 0)
570 != JXL_ENC_SUCCESS) {
571 errFile << "JxlEncoderSetExtraChannelDistance failed";
572 return false;
573 }
574 } else {
575 if (JxlEncoderSetExtraChannelDistance(frameSettings,
576 (cs->colorModelId() == CMYKAColorModelID) ? 1 : 0,
577 distance)
578 != JXL_ENC_SUCCESS) {
579 errFile << "JxlEncoderSetExtraChannelDistance failed";
580 return false;
581 }
582 }
583 }
584#endif
585 return true;
586 };
587
588 // XXX: Workaround for a buggy lossy F32.
589 //
590 // See: https://github.com/libjxl/libjxl/issues/2064
591 //
592 // Update: It's not the modular mode that caused the bug, but the progressive/responsive setting
593 // that didn't work well with F32. So let's disable it on F32 instead.
594 const int setResponsive = [&]() -> int {
595 if (pixelFormat.data_type == JXL_TYPE_FLOAT && !cfg->getBool("lossless")) {
596 warnFile << "Using workaround for lossy 32-bit float, disabling progressive option";
597 return 0;
598 }
599 return cfg->getInt("responsive", -1);
600 }();
601
602 // XXX: Workaround for a buggy lossless patches. Set to disable instead.
603 // Patch only for libjxl under v0.9.0
604 //
605 // See: https://github.com/libjxl/libjxl/issues/2463
606 const int setPatches = [&]() -> int {
607#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
608 if ((cfg->getInt("effort", 7) > 4) && cfgFlattenLayer) {
609 warnFile << "Using workaround for layer exports, disabling patches option on effort > 4";
610 return 0;
611 }
612#endif
613 return cfg->getInt("patches", -1);
614 }();
615
616 if (!setFrameLossless(cfg->getBool("lossless"))
617 || !setSetting(JXL_ENC_FRAME_SETTING_EFFORT, cfg->getInt("effort", 7))
618 || !setSetting(JXL_ENC_FRAME_SETTING_DECODING_SPEED, cfg->getInt("decodingSpeed", 0))
619 || !setSetting(JXL_ENC_FRAME_SETTING_RESAMPLING, cfg->getInt("resampling", -1))
620 || !setSetting(JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING, cfg->getInt("extraChannelResampling", -1))
621 || !setSetting(JXL_ENC_FRAME_SETTING_DOTS, cfg->getInt("dots", -1))
622 || !setSetting(JXL_ENC_FRAME_SETTING_PATCHES, setPatches)
623 || !setSetting(JXL_ENC_FRAME_SETTING_EPF, cfg->getInt("epf", -1))
624 || !setSetting(JXL_ENC_FRAME_SETTING_GABORISH, cfg->getInt("gaborish", -1))
625 || !setSetting(JXL_ENC_FRAME_SETTING_MODULAR, cfg->getInt("modular", -1))
626 || !setSetting(JXL_ENC_FRAME_SETTING_KEEP_INVISIBLE, cfg->getInt("keepInvisible", -1))
627 || !setSetting(JXL_ENC_FRAME_SETTING_GROUP_ORDER, cfg->getInt("groupOrder", -1))
628 || !setSetting(JXL_ENC_FRAME_SETTING_RESPONSIVE, setResponsive)
629 || !setSetting(JXL_ENC_FRAME_SETTING_PROGRESSIVE_AC, cfg->getInt("progressiveAC", -1))
630 || !setSetting(JXL_ENC_FRAME_SETTING_QPROGRESSIVE_AC, cfg->getInt("qProgressiveAC", -1))
631 || !setSetting(JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, cfg->getInt("progressiveDC", -1))
632 || !setSetting(JXL_ENC_FRAME_SETTING_PALETTE_COLORS, cfg->getInt("paletteColors", -1))
633 || !setSetting(JXL_ENC_FRAME_SETTING_LOSSY_PALETTE, cfg->getInt("lossyPalette", -1))
634 || !setSetting(JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, cfg->getInt("modularGroupSize", -1))
635 || !setSetting(JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR, cfg->getInt("modularPredictor", -1))
636 || !setSetting(JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL, cfg->getInt("jpegReconCFL", -1))
637 || !setDistance(cfg->getInt("lossyQuality", 100))) {
639 }
640 }
641
642 {
643 const auto setSettingFloat = [&](JxlEncoderFrameSettingId id, float v) {
644 if (JxlEncoderFrameSettingsSetFloatOption(frameSettings, id, v) != JXL_ENC_SUCCESS) {
645 errFile << "JxlEncoderFrameSettingsSetFloatOption failed";
646 return false;
647 }
648 return true;
649 };
650
651 if (!setSettingFloat(JXL_ENC_FRAME_SETTING_PHOTON_NOISE, cfg->getFloat("photonNoise", 0))
652 || !setSettingFloat(JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT,
653 cfg->getFloat("channelColorsGlobalPercent", -1))
654 || !setSettingFloat(JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT,
655 cfg->getFloat("channelColorsGroupPercent", -1))
656 || !setSettingFloat(JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT,
657 cfg->getFloat("modularMATreeLearningPercent", -1))) {
659 }
660 }
661
662 {
663 const bool isAnimated = [&]() {
664 if (image->animationInterface()->hasAnimation() && cfgHaveAnimation) {
665 KisLayerUtils::flattenImage(image, nullptr);
666 image->waitForDone();
667
668 const KisNodeSP projection = image->rootLayer()->firstChild();
669 return projection->isAnimated() && projection->hasEditablePaintDevice();
670 }
671 return false;
672 }();
673
674 if (isAnimated) {
675 // Flatten the image, projections don't have keyframes.
676 KisLayerUtils::flattenImage(image, nullptr);
677 image->waitForDone();
678
679 const KisNodeSP projection = image->rootLayer()->firstChild();
680 KIS_ASSERT(projection->isAnimated());
681 KIS_ASSERT(projection->hasEditablePaintDevice());
682
683 const auto *frames = projection->paintDevice()->keyframeChannel();
684 const auto times = [&]() {
685 QList<int> t;
686 QSet<int> s = frames->allKeyframeTimes();
687 t = QList<int>(s.begin(), s.end());
688 std::sort(t.begin(), t.end());
689 return t;
690 }();
691
692 auto frameHeader = []() {
693 auto header = std::make_unique<JxlFrameHeader>();
694 JxlEncoderInitFrameHeader(header.get());
695 return header;
696 }();
697
698 int frameNum = 0;
699 for (const auto i : times) {
700 frameHeader->duration = [&]() {
701 const auto nextKeyframe = frames->nextKeyframeTime(i);
702 if (nextKeyframe == -1) {
703 return static_cast<uint32_t>(
705 - i + 1);
706 } else {
707 return static_cast<uint32_t>(frames->nextKeyframeTime(i) - i);
708 }
709 }();
710 frameHeader->is_last = 0;
711
712 if (JxlEncoderSetFrameHeader(frameSettings, frameHeader.get()) != JXL_ENC_SUCCESS) {
713 errFile << "JxlEncoderSetFrameHeader failed";
715 }
716
717 const QByteArray pixels = [&]() {
718 const auto frameData = frames->keyframeAt<KisRasterKeyframe>(i);
719 KisPaintDeviceSP dev =
721 frameData->writeFrameToDevice(dev);
722
723 const KoID colorModel = cs->colorModelId();
724
725 if (colorModel != RGBAColorModelID) {
726 // blast it wholesale
727 QByteArray p;
728 p.resize(bounds.width() * bounds.height() * static_cast<int>(cs->pixelSize()));
729 dev->readBytes(reinterpret_cast<quint8 *>(p.data()), bounds);
730 return p;
731 } else {
732 KisHLineConstIteratorSP it = dev->createHLineConstIteratorNG(0, 0, bounds.width());
733
734 // detect traits based on depth
735 // if u8 or u16, also trigger swap
736 return HDR::writeLayer(cs->colorDepthId(),
737 convertToRec2020,
738 cs->profile()->isLinear(),
739 conversionPolicy,
740 removeHGLOOTF,
741 bounds.width(),
742 bounds.height(),
743 it,
744 hlgGamma,
745 hlgNominalPeak,
746 cs);
747 }
748 }();
749
750 if (JxlEncoderAddImageFrame(frameSettings,
751 &pixelFormat,
752 pixels.data(),
753 static_cast<size_t>(pixels.size()))
754 != JXL_ENC_SUCCESS) {
755 errFile << "JxlEncoderAddImageFrame @" << i << "failed";
757 }
758
759#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 10, 1)
760 if (updater() && updater()->interrupted()) {
761 warnFile << "Save cancelled";
763 }
764 const int progress = (frameNum * 100) / times.size();
765 setProgress(progress);
766 frameNum++;
767 if (frames->nextKeyframeTime(i) == -1) {
768 JxlEncoderCloseInput(enc.get());
769 }
770 if (JxlEncoderFlushInput(enc.get()) != JXL_ENC_SUCCESS) {
771 errFile << "JxlEncoderFlushInput failed";
773 }
774#endif
775 }
776 } else {
777 auto frameHeader = std::make_unique<JxlFrameHeader>();
778
779 // (On layered export) Convert group layer to paint layer to preserve
780 // out-of-bound pixels so that it won't get clipped to canvas size
781 quint32 lastValidLayer = 0;
782 if (cfgMultiLayer || cfgMultiPage) {
783 for (quint32 pos = 0; pos < image->root()->childCount(); pos++) {
784 KisNodeSP node = image->root()->at(pos);
785 KisLayer *layer = qobject_cast<KisLayer *>(node.data());
786 if (layer && (layer->inherits("KisGroupLayer") || layer->childCount() > 0 || layer->layerStyle())
787 && layer->visible()) {
788 dbgFile << "Flattening layer" << node->name();
789 KisLayerUtils::flattenLayer(image, layer);
790 }
791 if (node && node->visible() && !node->isFakeNode()) {
792 lastValidLayer = pos;
793 }
794 }
795 image->waitForDone();
796 }
797
798 // Iterate through the layers (non-recursively)
799 for (quint32 pos = 0; pos < image->root()->childCount(); pos++) {
800 KisNodeSP node = image->root()->at(pos);
801 // Skip invalid and invisible layers
802 if ((cfgMultiLayer || cfgMultiPage) && (!node || !node->visible() || node->isFakeNode())) {
803 dbgFile << "Skipping hidden layer" << node->name();
804 continue;
805 }
806 const bool isFirstLayer = (node == image->root()->firstChild());
807
808 if (cfgMultiLayer || cfgMultiPage) {
809 dbgFile << "Visiting on layer" << node->name();
810
811 if (!node->inherits("KisPaintLayer")) {
812 std::future<KisNodeSP> convertedNode = KisLayerUtils::convertToPaintLayer(image, node);
813 node = convertedNode.get();
814 }
815
816 const KoColorSpace *lcs = node->colorSpace();
817 if (lcs && (lcs != cs)) {
818 node->paintDevice()->convertTo(cs);
819 // Kampidh: Do I also need to call waitForDone() here?
820 }
821 } else {
822 dbgFile << "Saving flattened image";
823 }
824
825 const QRect layerBounds = [&]() {
826 if (node->exactBounds().isEmpty() || cfgFlattenLayer) {
827 return image->bounds();
828 }
829 return node->exactBounds();
830 }();
831
833 if (cfgFlattenLayer) {
834 dev = image->projection();
835 } else {
836 dev = node->projection();
837 }
838
839 if (cs->colorModelId() == CMYKAColorModelID) {
840 // Inverting colors for CMYK
841 const KisFilterSP f = KisFilterRegistry::instance()->value("invert");
842 KIS_ASSERT(f);
843 const KisFilterConfigurationSP kfc =
844 f->defaultConfiguration(KisGlobalResourcesInterface::instance());
845 KIS_ASSERT(kfc);
846 f->process(dev, layerBounds, kfc->cloneWithResourcesSnapshot());
847 }
848
849 const QByteArray pixels = [&]() {
850 const KoID colorModel = cs->colorModelId();
851 const KoID colorDepth = cs->colorDepthId();
852
853 if (colorModel != RGBAColorModelID
854 || (colorDepth != Integer8BitsColorDepthID && colorDepth != Integer16BitsColorDepthID
855 && conversionPolicy == ConversionPolicy::KeepTheSame)) {
856 // CMYK
857 if (colorModel == CMYKAColorModelID) {
859 dev->createHLineConstIteratorNG(layerBounds.x(), layerBounds.y(), layerBounds.width());
860
861 // interleaved CMY buffer
863 true,
864 0,
865 layerBounds.width(),
866 layerBounds.height(),
867 it);
868 }
869 // blast it wholesale
870 QByteArray p;
871 p.resize(layerBounds.width() * layerBounds.height() * static_cast<int>(cs->pixelSize()));
872 dev->readBytes(reinterpret_cast<quint8 *>(p.data()), layerBounds);
873 return p;
874 } else {
876 dev->createHLineConstIteratorNG(layerBounds.x(), layerBounds.y(), layerBounds.width());
877
878 // detect traits based on depth
879 // if u8 or u16, also trigger swap
880 return HDR::writeLayer(cs->colorDepthId(),
881 convertToRec2020,
882 cs->profile()->isLinear(),
883 conversionPolicy,
884 removeHGLOOTF,
885 layerBounds.width(),
886 layerBounds.height(),
887 it,
888 hlgGamma,
889 hlgNominalPeak,
890 cs);
891 }
892 }();
893
894 if (cfgMultiLayer || cfgMultiPage) {
895 JxlEncoderInitFrameHeader(frameHeader.get());
896
897 // Set frame duration to 0 to indicate a multi-layered image
898 if (cfgMultiLayer) {
899 frameHeader->duration = 0;
900 } else if (cfgMultiPage) {
901 frameHeader->duration = 0xFFFFFFFF;
902 }
903
904 // Enable crop info if layer dimension is different than main
905 // This also enables out-of-bound pixels to be preserved
906 if (node->exactBounds() == image->bounds()) {
907 frameHeader->layer_info.have_crop = false;
908 } else {
909 frameHeader->layer_info.have_crop = true;
910 }
911 frameHeader->layer_info.crop_x0 = layerBounds.x();
912 frameHeader->layer_info.crop_y0 = layerBounds.y();
913 frameHeader->layer_info.xsize = layerBounds.width();
914 frameHeader->layer_info.ysize = layerBounds.height();
915
916 if (cs->colorModelId() == CMYKAColorModelID) {
917 frameHeader->layer_info.blend_info.alpha = 1;
918 } else {
919 frameHeader->layer_info.blend_info.alpha = 0;
920 }
921
922 // EXPERIMENTAL! Additive blending mode on JPEG-XL produces
923 // slightly different result than Krita.
924 const QString frameName = node->name();
925 if (!isFirstLayer && cfgMultiLayer) {
926 if (node->compositeOpId() == QString("add")) {
927 frameHeader->layer_info.blend_info.blendmode = JXL_BLEND_MULADD;
928 } else {
929 frameHeader->layer_info.blend_info.blendmode = JXL_BLEND_BLEND;
930 }
931 }
932
933 if (JxlEncoderSetFrameHeader(frameSettings, frameHeader.get()) != JXL_ENC_SUCCESS) {
934 errFile << "JxlEncoderSetFrameHeader failed";
936 }
937 if (JxlEncoderSetFrameName(frameSettings, frameName.toLocal8Bit()) != JXL_ENC_SUCCESS) {
938 errFile << "JxlEncoderSetFrameName failed";
940 }
941 }
942
943 if (JxlEncoderAddImageFrame(frameSettings,
944 &pixelFormat,
945 pixels.data(),
946 static_cast<size_t>(pixels.size()))
947 != JXL_ENC_SUCCESS) {
948 errFile << "JxlEncoderAddImageFrame failed";
950 }
951
952 // CMYKA separate planar buffer for Key and Alpha
953 if (cs->colorModelId() == CMYKAColorModelID) {
955 dev->createHLineConstIteratorNG(layerBounds.x(), layerBounds.y(), layerBounds.width());
956
957 const QByteArray chaK = JXLExpTool::writeCMYKLayer(cs->colorDepthId(),
958 false,
959 3,
960 layerBounds.width(),
961 layerBounds.height(),
962 it);
963 it->resetRowPos();
964 const QByteArray chaA = JXLExpTool::writeCMYKLayer(cs->colorDepthId(),
965 false,
966 4,
967 layerBounds.width(),
968 layerBounds.height(),
969 it);
970
971 if (JxlEncoderSetExtraChannelBuffer(frameSettings,
972 &pixelFormat,
973 chaK,
974 static_cast<size_t>(chaK.size()),
975 0)
976 != JXL_ENC_SUCCESS) {
977 errFile << "JxlEncoderSetExtraChannelBuffer Key failed";
979 }
980 if (JxlEncoderSetExtraChannelBuffer(frameSettings,
981 &pixelFormat,
982 chaA,
983 static_cast<size_t>(chaA.size()),
984 1)
985 != JXL_ENC_SUCCESS) {
986 errFile << "JxlEncoderSetExtraChannelBuffer Alpha failed";
988 }
989 }
990
991#if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 10, 1)
992 if (updater() && updater()->interrupted()) {
993 warnFile << "Save cancelled";
995 }
996 const int progress = (pos * 100) / image->root()->childCount();
997 setProgress(progress);
998 if ((pos == lastValidLayer && (cfgMultiLayer || cfgMultiPage)) || cfgFlattenLayer) {
999 JxlEncoderCloseInput(enc.get());
1000 }
1001 if (JxlEncoderFlushInput(enc.get()) != JXL_ENC_SUCCESS) {
1002 errFile << "JxlEncoderFlushInput failed";
1004 }
1005 if (cfgFlattenLayer) {
1006 break;
1007 }
1008 }
1009 }
1010#else
1011 // Quit loop if flatten is active
1012 if (cfgFlattenLayer) {
1013 break;
1014 }
1015 }
1016 }
1017 JxlEncoderCloseInput(enc.get());
1018
1019 QByteArray compressed(16384, 0x0);
1020 auto *nextOut = reinterpret_cast<uint8_t *>(compressed.data());
1021 auto availOut = static_cast<size_t>(compressed.size());
1022 auto result = JXL_ENC_NEED_MORE_OUTPUT;
1023 while (result == JXL_ENC_NEED_MORE_OUTPUT) {
1024 result = JxlEncoderProcessOutput(enc.get(), &nextOut, &availOut);
1025 if (result != JXL_ENC_ERROR) {
1026 io->write(compressed.data(), compressed.size() - static_cast<int>(availOut));
1027 }
1028 if (result == JXL_ENC_NEED_MORE_OUTPUT) {
1029 compressed.resize(compressed.size() * 2);
1030 nextOut = reinterpret_cast<uint8_t *>(compressed.data());
1031 availOut = static_cast<size_t>(compressed.size());
1032 }
1033 }
1034 if (JXL_ENC_SUCCESS != result) {
1035 errFile << "JxlEncoderProcessOutput failed";
1037 }
1038#endif
1039 }
1040
1041 return ImportExportCodes::OK;
1042}
const Params2D p
qreal v
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
ColorPrimaries
The colorPrimaries enum Enum of colorants, follows ITU H.273 for values 0 to 255, and has extra known...
@ PRIMARIES_ITU_R_BT_2020_2_AND_2100_0
@ PRIMARIES_SMPTE_RP_431_2
@ PRIMARIES_ITU_R_BT_709_5
TransferCharacteristics
The transferCharacteristics enum Enum of transfer characteristics, follows ITU H.273 for values 0 to ...
@ TRC_IEC_61966_2_4
@ TRC_ITU_R_BT_2020_2_10bit
@ TRC_LOGARITHMIC_100
@ TRC_ITU_R_BT_470_6_SYSTEM_M
@ TRC_ITU_R_BT_470_6_SYSTEM_B_G
@ TRC_ITU_R_BT_1361
@ TRC_ITU_R_BT_2100_0_HLG
@ TRC_ITU_R_BT_2100_0_PQ
@ TRC_ITU_R_BT_601_6
@ TRC_IEC_61966_2_1
@ TRC_ITU_R_BT_709_5
@ TRC_SMPTE_ST_428_1
@ TRC_LOGARITHMIC_100_sqrt10
@ TRC_ITU_R_BT_2020_2_12bit
qreal distance(const QPointF &p1, const QPointF &p2)
The KisExifInfoVisitor class looks for a layer with metadata.
KisMetaData::Store * exifInfo()
bool visit(KisNode *) override
static KisFilterRegistry * instance()
static KisResourcesInterfaceSP instance()
virtual void resetRowPos()=0
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
void waitForDone()
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
void convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
KisPaintDeviceSP projection() const
QRect bounds() const override
QPointer< KoUpdater > updater
virtual void setEnabledFilters(const QStringList &enabledFilters)
enable the filters in the given list; others will be disabled.
virtual bool saveTo(const Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const =0
static KisMetaData::SchemaRegistry * instance()
const Schema * schemaFromUri(const QString &uri) const
static const QString DublinCoreSchemaUri
static KisMetadataBackendRegistry * instance()
KisRasterKeyframeChannel * keyframeChannel() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
void readBytes(quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) const
KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
The KisRasterKeyframe class is a concrete subclass of KisKeyframe that wraps a physical raster image ...
int end() const
virtual quint32 pixelSize() const =0
virtual bool hasHighDynamicRange() const =0
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
virtual const KoColorProfile * profile() const =0
const T value(const QString &id) const
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define bounds(x, a, b)
#define warnFile
Definition kis_debug.h:95
#define errFile
Definition kis_debug.h:115
#define dbgFile
Definition kis_debug.h:53
QByteArray writeLayer(const int width, const int height, KisHLineConstIteratorSP it, float hlgGamma, float hlgNominalPeak, const KoColorSpace *cs)
QByteArray writeCMYKLayer(const KoID &id, Args &&...args)
void flattenImage(KisImageSP image, KisNodeSP activeNode, MergeFlags flags)
void flattenLayer(KisImageSP image, KisLayerSP layer, MergeFlags flags)
std::future< KisNodeSP > convertToPaintLayer(KisImageSP image, KisNodeSP src)
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
virtual QRect exactBounds() const
virtual const KoColorSpace * colorSpace() const =0
bool isAnimated() const
virtual KisPaintDeviceSP paintDevice() const =0
QString name() const
virtual bool isFakeNode() const
virtual bool visible(bool recursive=false) const
bool hasEditablePaintDevice() const
KisPSDLayerStyleSP layerStyle
Definition kis_layer.cc:171
KisNodeSP firstChild() const
Definition kis_node.cpp:361
quint32 childCount() const
Definition kis_node.cpp:414
KisNodeSP at(quint32 index) const
Definition kis_node.cpp:421
The KoColorProfileQuery struct.
virtual QByteArray rawData() const
virtual KoColorimetryUtils::xyY getWhitePointxyY() const =0
virtual ColorPrimaries getColorPrimaries() const
getColorPrimaries
virtual bool hasColorants() const =0
virtual bool isLinear() const =0
virtual QVector< KoColorimetryUtils::xyY > getColorantsxyY() const =0
virtual TransferCharacteristics getTransferCharacteristics() const
getTransferCharacteristics This function should be subclassed at some point so we can get the value f...
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorProfile * profileFor(const KoColorProfileQuery &query, const bool generate=true) const
profileFor tries to find the profile that matches these characteristics, if no such profile is found,...

References KisImage::animationInterface(), ApplyHLG, ApplyPQ, ApplySMPTE428, KisNode::at(), KisImage::bounds(), bounds, ImportExportCodes::Cancelled, KisNode::childCount(), CMYKAColorModelID, KoColorSpace::colorDepthId(), KoColorSpace::colorModelId(), KisImage::colorSpace(), KisBaseNode::colorSpace(), KoColorSpaceRegistry::colorSpace(), KisBaseNode::compositeOpId(), KisImage::convertImageColorSpace(), KisPaintDevice::convertTo(), KisLayerUtils::convertToPaintLayer(), KritaUtils::CopySnapshot, KisPaintDevice::createHLineConstIteratorNG(), KisSharedPtr< T >::data(), dbgFile, distance(), KisImageAnimationInterface::documentPlaybackRange(), KisMetaData::Schema::DublinCoreSchemaUri, KisMetaData::FilterRegistryModel::enabledFilters(), KisTimeSpan::end(), errFile, KisBaseNode::exactBounds(), KisExifInfoVisitor::exifInfo(), KisNode::firstChild(), KisLayerUtils::flattenImage(), KisLayerUtils::flattenLayer(), Float16BitsColorDepthID, Float32BitsColorDepthID, ImportExportCodes::FormatColorSpaceUnsupported, KisImageAnimationInterface::framerate(), KoColorProfile::getColorantsxyY(), KoColorProfile::getColorPrimaries(), JXLExpTool::JxlOutputProcessor::getOutputProcessor(), KoColorProfile::getTransferCharacteristics(), KoColorProfile::getWhitePointxyY(), GrayAColorModelID, KisImageAnimationInterface::hasAnimation(), KoColorProfile::hasColorants(), KisBaseNode::hasEditablePaintDevice(), KoColorSpace::hasHighDynamicRange(), KoID::id(), KisFilterRegistry::instance(), KisMetadataBackendRegistry::instance(), KisMetaData::SchemaRegistry::instance(), KoColorSpaceRegistry::instance(), KisGlobalResourcesInterface::instance(), Integer16BitsColorDepthID, Integer8BitsColorDepthID, KoColorConversionTransformation::internalConversionFlags(), ImportExportCodes::InternalError, KoColorConversionTransformation::internalRenderingIntent(), KisBaseNode::isAnimated(), KisBaseNode::isFakeNode(), KoColorProfile::isLinear(), KeepTheSame, KisPaintDevice::keyframeChannel(), KIS_ASSERT, KIS_ASSERT_RECOVER, KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE, KisLayer::layerStyle, KisExifInfoVisitor::metaDataCount(), KisBaseNode::name(), ImportExportCodes::NoAccessToWrite, p, KisBaseNode::paintDevice(), KoColorSpace::pixelSize(), PRIMARIES_ITU_R_BT_2020_2_AND_2100_0, PRIMARIES_ITU_R_BT_709_5, PRIMARIES_SMPTE_RP_431_2, KoColorSpace::profile(), KoColorSpaceRegistry::profileFor(), KisImage::projection(), KisBaseNode::projection(), KoColorProfile::rawData(), KisPaintDevice::readBytes(), KisHLineConstIteratorNG::resetRowPos(), RGBAColorModelID, KisNodeFacade::root, KisImage::rootLayer(), KisMetaData::IOBackend::saveTo(), KisMetaData::SchemaRegistry::schemaFromUri(), KisMetaData::FilterRegistryModel::setEnabledFilters(), KisImportExportFilter::setProgress(), TRC_A98, TRC_GAMMA_1_8, TRC_GAMMA_2_4, TRC_IEC_61966_2_1, TRC_IEC_61966_2_4, TRC_ITU_R_BT_1361, TRC_ITU_R_BT_2020_2_10bit, TRC_ITU_R_BT_2020_2_12bit, TRC_ITU_R_BT_2100_0_HLG, TRC_ITU_R_BT_2100_0_PQ, TRC_ITU_R_BT_470_6_SYSTEM_B_G, TRC_ITU_R_BT_470_6_SYSTEM_M, TRC_ITU_R_BT_601_6, TRC_ITU_R_BT_709_5, TRC_LAB_L, TRC_LINEAR, TRC_LOGARITHMIC_100, TRC_LOGARITHMIC_100_sqrt10, TRC_PROPHOTO, TRC_SMPTE_240M, TRC_SMPTE_ST_428_1, TRC_UNSPECIFIED, KisImportExportFilter::updater, v, KoGenericRegistry< T >::value(), KisBaseNode::visible(), KisExifInfoVisitor::visit(), KisImage::waitForDone(), warnFile, JXLExpTool::writeCMYKLayer(), HDR::writeLayer(), KoColorimetryUtils::xyY::x, and KoColorimetryUtils::xyY::y.

◆ createConfigurationWidget()

KisConfigWidget * JPEGXLExport::createConfigurationWidget ( QWidget * parent,
const QByteArray & from = "",
const QByteArray & to = "" ) const
overridevirtual

createConfigurationWidget creates a widget that can be used to define the settings for a given import/export filter

Parameters
parentthe owner of the widget; the caller is responsible for deleting
fromThe mimetype of the source file/document
toThe mimetype of the destination file/document
Returns
the widget

Reimplemented from KisImportExportFilter.

Definition at line 1085 of file JPEGXLExport.cpp.

1086{
1087 return new KisWdgOptionsJPEGXL(parent);
1088}

◆ defaultConfiguration()

KisPropertiesConfigurationSP JPEGXLExport::defaultConfiguration ( const QByteArray & from = "",
const QByteArray & to = "" ) const
overridevirtual

defaultConfiguration defines the default settings for the given import export filter

Parameters
fromThe mimetype of the source file/document
toThe mimetype of the destination file/document
Returns
a serializable KisPropertiesConfiguration object

Reimplemented from KisImportExportFilter.

Definition at line 1090 of file JPEGXLExport.cpp.

1091{
1093
1094 // WARNING: libjxl only allows setting encoding properties,
1095 // so I hardcoded values from https://libjxl.readthedocs.io/en/latest/api_encoder.html
1096 // https://readthedocs.org/projects/libjxl/builds/16271112/
1097
1098 // Options for the following were not added because they rely
1099 // on the image's specific color space, or can introduce more
1100 // trouble than help:
1101 // JXL_ENC_FRAME_SETTING_GROUP_ORDER_CENTER_X
1102 // JXL_ENC_FRAME_SETTING_GROUP_ORDER_CENTER_Y
1103 // JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE
1104 // JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS
1105 // These are directly incompatible with the export logic:
1106 // JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED
1107 // JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM
1108
1109 cfg->setProperty("haveAnimation", false);
1110 cfg->setProperty("flattenLayers", true);
1111 cfg->setProperty("multiLayer", false);
1112 cfg->setProperty("multiPage", false);
1113 cfg->setProperty("lossless", true);
1114 cfg->setProperty("effort", 7);
1115 cfg->setProperty("decodingSpeed", 0);
1116 cfg->setProperty("lossyQuality", 100);
1117 cfg->setProperty("forceModular", false);
1118 cfg->setProperty("modularSetVal", -1);
1119 cfg->setProperty("losslessAlpha", false);
1120
1121 cfg->setProperty("forceCicpLossless", false);
1122 cfg->setProperty("floatingPointConversionOption", "KeepSame");
1123 cfg->setProperty("HLGnominalPeak", 1000.0);
1124 cfg->setProperty("HLGgamma", 1.2);
1125 cfg->setProperty("removeHGLOOTF", true);
1126
1127 cfg->setProperty("resampling", -1);
1128 cfg->setProperty("extraChannelResampling", -1);
1129 cfg->setProperty("photonNoise", 0);
1130 cfg->setProperty("dots", -1);
1131 cfg->setProperty("patches", -1);
1132 cfg->setProperty("epf", -1);
1133 cfg->setProperty("gaborish", -1);
1134 cfg->setProperty("modular", -1);
1135 cfg->setProperty("keepInvisible", -1);
1136 cfg->setProperty("groupOrder", -1);
1137 cfg->setProperty("responsive", -1);
1138 cfg->setProperty("progressiveAC", -1);
1139 cfg->setProperty("qProgressiveAC", -1);
1140 cfg->setProperty("progressiveDC", -1);
1141 cfg->setProperty("channelColorsGlobalPercent", -1);
1142 cfg->setProperty("channelColorsGroupPercent", -1);
1143 cfg->setProperty("paletteColors", -1);
1144 cfg->setProperty("lossyPalette", -1);
1145 cfg->setProperty("modularGroupSize", -1);
1146 cfg->setProperty("modularPredictor", -1);
1147 cfg->setProperty("modularMATreeLearningPercent", -1);
1148 cfg->setProperty("jpegReconCFL", -1);
1149
1150 cfg->setProperty("storeAuthor", false);
1151 cfg->setProperty("exif", true);
1152 cfg->setProperty("xmp", true);
1153 cfg->setProperty("iptc", true);
1154 cfg->setProperty("storeMetaData", false);
1155 cfg->setProperty("filters", "");
1156 return cfg;
1157}

◆ initializeCapabilities()

void JPEGXLExport::initializeCapabilities ( )
overridevirtual

Reimplemented from KisImportExportFilter.

Definition at line 1044 of file JPEGXLExport.cpp.

1045{
1046 // This checks before saving for what the file format supports: anything that is supported needs to be mentioned
1047 // here
1048
1049 QList<QPair<KoID, KoID>> supportedColorModels;
1051 ->get("AnimationCheck")
1057 supportedColorModels << QPair<KoID, KoID>() << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID)
1058 << QPair<KoID, KoID>(GrayAColorModelID, Integer8BitsColorDepthID)
1059 << QPair<KoID, KoID>(CMYKAColorModelID, Integer8BitsColorDepthID)
1060 << QPair<KoID, KoID>(RGBAColorModelID, Integer16BitsColorDepthID)
1061 << QPair<KoID, KoID>(GrayAColorModelID, Integer16BitsColorDepthID)
1062 << QPair<KoID, KoID>(CMYKAColorModelID, Integer16BitsColorDepthID)
1063#ifdef HAVE_OPENEXR
1064 << QPair<KoID, KoID>(RGBAColorModelID, Float16BitsColorDepthID)
1065 << QPair<KoID, KoID>(GrayAColorModelID, Float16BitsColorDepthID)
1066 << QPair<KoID, KoID>(CMYKAColorModelID, Float16BitsColorDepthID)
1067#endif
1068 << QPair<KoID, KoID>(RGBAColorModelID, Float32BitsColorDepthID)
1069 << QPair<KoID, KoID>(GrayAColorModelID, Float32BitsColorDepthID)
1070 << QPair<KoID, KoID>(CMYKAColorModelID, Float32BitsColorDepthID);
1071 addSupportedColorModels(supportedColorModels, "JPEG-XL");
1072
1082}
VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v)
static KisExportCheckRegistry * instance()
void addSupportedColorModels(QList< QPair< KoID, KoID > > supportedColorModels, const QString &name, KisExportCheckBase::Level level=KisExportCheckBase::PARTIALLY)
void addCapability(KisExportCheckBase *capability)
QPainterPath create(const char32_t codepoint, double height)
Creates a tofu missing glyph indicator representing the provided Unicode codepoint.

References KisImportExportFilter::addCapability(), KisImportExportFilter::addSupportedColorModels(), CMYKAColorModelID, Float16BitsColorDepthID, Float32BitsColorDepthID, get(), GrayAColorModelID, KisExportCheckRegistry::instance(), Integer16BitsColorDepthID, Integer8BitsColorDepthID, KisExportCheckBase::PARTIALLY, RGBAColorModelID, and KisExportCheckBase::SUPPORTED.


The documentation for this class was generated from the following files: