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

#include <HeifImport.h>

+ Inheritance diagram for HeifImport:

Public Member Functions

KisImportExportErrorCode convert (KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration=0) override
 
 HeifImport (QObject *parent, const QVariantList &)
 
bool supportsIO () const override
 Override and return false for the filters that use a library that cannot handle file handles, only file names.
 
 ~HeifImport () override
 
- Public Member Functions inherited from KisImportExportFilter
virtual KisConfigWidgetcreateConfigurationWidget (QWidget *parent, const QByteArray &from="", const QByteArray &to="") const
 createConfigurationWidget creates a widget that can be used to define the settings for a given import/export filter
 
virtual KisPropertiesConfigurationSP defaultConfiguration (const QByteArray &from="", const QByteArray &to="") const
 defaultConfiguration defines the default settings for the given import export filter
 
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)
 
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
 
virtual void initializeCapabilities ()
 
 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 17 of file HeifImport.h.

Constructor & Destructor Documentation

◆ HeifImport()

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

Definition at line 45 of file HeifImport.cpp.

45 : KisImportExportFilter(parent)
46{
47}
KisImportExportFilter(QObject *parent=0)

◆ ~HeifImport()

HeifImport::~HeifImport ( )
override

Definition at line 49 of file HeifImport.cpp.

50{
51}

Member Function Documentation

◆ convert()

KisImportExportErrorCode HeifImport::convert ( KisDocument * document,
QIODevice * io,
KisPropertiesConfigurationSP configuration = 0 )
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 210 of file HeifImport.cpp.

211{
212#if LIBHEIF_HAVE_VERSION(1, 13, 0)
213 HeifLock lock;
214#endif
215
216#if LIBHEIF_HAVE_VERSION(1, 20, 2)
217 using HeifStrideType = size_t;
218 auto heifGetPlaneMethod = std::mem_fn(qNonConstOverload<heif_channel, HeifStrideType*>(&heif::Image::get_plane2));
219#elif LIBHEIF_HAVE_VERSION(1, 20, 0)
220 using HeifStrideType = size_t;
221 auto heifGetPlaneMethod = std::mem_fn(qNonConstOverload<heif_channel, HeifStrideType*>(&heif::Image::get_plane));
222#else
223 using HeifStrideType = int;
224 auto heifGetPlaneMethod = std::mem_fn(qNonConstOverload<heif_channel, HeifStrideType*>(&heif::Image::get_plane));
225#endif
226
227 // Wrap input stream into heif Reader object
228 Reader_QIODevice reader(io);
229
230 try {
231 heif::Context ctx;
232 ctx.read_from_reader(reader);
233
234 // decode primary image
235
236 heif::ImageHandle handle = ctx.get_primary_image_handle();
237
238 heif_color_profile_type profileType = heif_image_handle_get_color_profile_type(handle.get_raw_image_handle());
239
240
241 heif::Image heifimage = handle.decode_image(heif_colorspace_undefined, heif_chroma_undefined);
242 heif_colorspace heifModel = heifimage.get_colorspace();
243 heif_chroma heifChroma = heifimage.get_chroma_format();
244 int luma = handle.get_luma_bits_per_pixel();
245 bool hasAlpha = handle.has_alpha_channel();
246
247 dbgFile << "loading heif" << heifModel << heifChroma << luma;
248
250 bool applyOOTF = true;
251 float displayGamma = 1.2f;
252 float displayNits = 1000.0;
253
254 struct heif_error err {
255 };
256 const KoColorProfile *profile = nullptr;
257 KoID colorDepth = Integer8BitsColorDepthID;
258 QString colorModel = RGBAColorModelID.id();
259
260 if (luma > 8) {
261 colorDepth = Integer16BitsColorDepthID;
262 }
263 // First, get the profile, because sometimes the image may be encoded in YCbCr and the embedded icc profile is graya.
264
265 if (profileType == heif_color_profile_type_prof || profileType == heif_color_profile_type_rICC) {
266 dbgFile << "profile type is icc profile";
267 // rICC are 'restricted' icc profiles, and are matrix shaper profiles
268 // that are either RGB or Grayscale, and are of the input or display types.
269 // They are from the JPEG2000 spec.
270
271 int rawProfileSize = (int) heif_image_handle_get_raw_color_profile_size(handle.get_raw_image_handle());
272 if (rawProfileSize > 0) {
273 QByteArray ba(rawProfileSize, 0);
274 err = heif_image_handle_get_raw_color_profile(handle.get_raw_image_handle(), ba.data());
275 if (err.code) {
276 dbgFile << "icc profile loading failed:" << err.message;
277 } else {
278 profile = KoColorSpaceRegistry::instance()->createColorProfile(colorModel, colorDepth.id(), ba);
280 colorModel = profile->colorModelID();
281 }
282 } else {
283 dbgFile << "icc profile is empty";
284 }
285 } else if (profileType == heif_color_profile_type_nclx) {
286 dbgFile << "profile type is nclx coding independent code points";
287 // NCLX parameters is a colorspace description used for videofiles.
288 // We generate a color profile for most entries, except that we cannot handle
289 // PQ, HLG or 428 in an icc profile, so we convert those on the fly to linear.
290
291 struct heif_color_profile_nclx *nclx = nullptr;
292 err = heif_image_handle_get_nclx_color_profile(handle.get_raw_image_handle(), &nclx);
293 if (err.code || !nclx) {
294 dbgFile << "nclx profile loading failed" << err.message;
295 } else {
296 TransferCharacteristics transferCharacteristic = TransferCharacteristics(nclx->transfer_characteristics);
297 ColorPrimaries primaries = ColorPrimaries(nclx->color_primaries);
298 if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ) {
299 dbgFile << "linearizing from PQ";
300 linearizePolicy = LinearizePolicy::LinearFromPQ;
301 transferCharacteristic = TRC_LINEAR;
302 }
303 if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_HLG) {
304 dbgFile << "linearizing from HLG";
305 if (!document->fileBatchMode()) {
306 KisDlgHLGImport dlg(applyOOTF, displayGamma, displayNits);
307 dlg.exec();
308 applyOOTF = dlg.applyOOTF();
309 displayGamma = dlg.gamma();
310 displayNits = dlg.nominalPeakBrightness();
311 }
312 linearizePolicy = LinearizePolicy::LinearFromHLG;
313 transferCharacteristic = TRC_LINEAR;
314 }
315 if (nclx->transfer_characteristics == heif_transfer_characteristic_SMPTE_ST_428_1) {
316 dbgFile << "linearizing from SMPTE 428";
317 linearizePolicy = LinearizePolicy::LinearFromSMPTE428;
318 transferCharacteristic = TRC_LINEAR;
319 }
320
321 if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_4 ||
322 nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_1361) {
323 transferCharacteristic = TRC_ITU_R_BT_709_5;
324 }
325
326 const QVector<double> colorants = [&]() -> QVector<double> {
327 if (primaries == PRIMARIES_UNSPECIFIED) {
328 return {};
329 } else {
330 return {
331 static_cast<double>(nclx->color_primary_white_x),
332 static_cast<double>(nclx->color_primary_white_y),
333 static_cast<double>(nclx->color_primary_red_x),
334 static_cast<double>(nclx->color_primary_red_y),
335 static_cast<double>(nclx->color_primary_green_x),
336 static_cast<double>(nclx->color_primary_green_y),
337 static_cast<double>(nclx->color_primary_blue_x),
338 static_cast<double>(nclx->color_primary_blue_y)};
339 }
340 }();
341
342 profile = KoColorSpaceRegistry::instance()->profileFor(colorants,
343 primaries,
344 transferCharacteristic);
345
346 if (linearizePolicy != LinearizePolicy::KeepTheSame) {
347 colorDepth = Float32BitsColorDepthID;
348 }
349
350 heif_nclx_color_profile_free(nclx);
351 dbgFile << "nclx profile found" << profile->name();
352 }
353 } else {
354 dbgFile << "no profile found";
355 }
356
357 // Now, to figure out the correct chroma and color model.
358 if (heifModel == heif_colorspace_monochrome || colorModel == GrayAColorModelID.id()) {
359 // Grayscale image.
360 if (heifChroma != heif_chroma_monochrome && colorModel == GrayAColorModelID.id()) {
361 heifimage = handle.decode_image(heif_colorspace_YCbCr, heif_chroma_monochrome);
362 }
363 colorModel = GrayAColorModelID.id();
364 heifChroma = heif_chroma_monochrome;
365 } else if (heifModel == heif_colorspace_YCbCr) {
366 // we do not support importing YCrBr spaces directly
367 heifimage = handle.decode_image(heif_colorspace_RGB, heif_chroma_undefined);
368 colorModel = RGBAColorModelID.id();
369 heifModel = heifimage.get_colorspace();
370 heifChroma = heifimage.get_chroma_format();
371 }
372
373 // Get the default profile if we haven't found one up till now.
374 if (!profile) {
375 if (colorModel == RGBAColorModelID.id()) {
379 } else {
380 const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(colorModel, colorDepth.id());
381 QString profileName = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId);
382 profile = KoColorSpaceRegistry::instance()->profileByName(profileName);
383 }
384 }
385
386 const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth.id(), profile);
387
388 int width = handle.get_width();
389
390 int height = handle.get_height();
391
392 // convert HEIF image to Krita KisDocument
393
394 KisImageSP image = new KisImage(document->createUndoStore(), width, height, colorSpace,
395 "HEIF image");
396
397 KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8);
398
399 if (luma != 8 && luma != 10 && luma != 12) {
400 dbgFile << "unknown bitdepth" << luma;
401 }
402
403 if (heifChroma == heif_chroma_monochrome) {
404 dbgFile << "monochrome heif file, bits:" << luma;
405 HeifStrideType strideG = 0;
406 HeifStrideType strideA = 0;
407 const uint8_t *imgG = heifGetPlaneMethod(heifimage, heif_channel_Y, &strideG);
408 const uint8_t *imgA =
409 heifGetPlaneMethod(heifimage, heif_channel_Alpha, &strideA);
410 const int width = heifimage.get_width(heif_channel_Y);
411 const int height = heifimage.get_height(heif_channel_Y);
413 layer->paintDevice()->createHLineIteratorNG(0, 0, width);
414
416 hasAlpha,
417 width,
418 height,
419 it,
420 imgG,
421 imgA,
422 strideG,
423 strideA);
424 } else if (heifChroma == heif_chroma_444) {
425 dbgFile << "planar heif file, bits:" << luma;
426
427 HeifStrideType strideR = 0;
428 HeifStrideType strideG = 0;
429 HeifStrideType strideB = 0;
430 HeifStrideType strideA = 0;
431 const uint8_t* imgR = heifGetPlaneMethod(heifimage, heif_channel_R, &strideR);
432 const uint8_t* imgG = heifGetPlaneMethod(heifimage, heif_channel_G, &strideG);
433 const uint8_t* imgB = heifGetPlaneMethod(heifimage, heif_channel_B, &strideB);
434 const uint8_t *imgA =
435 heifGetPlaneMethod(heifimage, heif_channel_Alpha, &strideA);
436 KisHLineIteratorSP it = layer->paintDevice()->createHLineIteratorNG(0, 0, width);
437
439 linearizePolicy,
440 applyOOTF,
441 hasAlpha,
442 width,
443 height,
444 imgR,
445 strideR,
446 imgG,
447 strideG,
448 imgB,
449 strideB,
450 imgA,
451 strideA,
452 it,
453 displayGamma,
454 displayNits,
455 colorSpace);
456 } else if (heifChroma == heif_chroma_interleaved_RGB || heifChroma == heif_chroma_interleaved_RGBA) {
457 HeifStrideType stride = 0;
458 dbgFile << "interleaved SDR heif file, bits:" << luma;
459
460 const uint8_t *img = heifGetPlaneMethod(heifimage, heif_channel_interleaved, &stride);
461 width = heifimage.get_width(heif_channel_interleaved);
462 height = heifimage.get_height(heif_channel_interleaved);
464 layer->paintDevice()->createHLineIteratorNG(0, 0, width);
465
466 SDR::readInterleavedLayer(linearizePolicy,
467 applyOOTF,
468 hasAlpha,
469 width,
470 height,
471 img,
472 stride,
473 it,
474 displayGamma,
475 displayNits,
476 colorSpace);
477
478 } else if (heifChroma == heif_chroma_interleaved_RRGGBB_LE || heifChroma == heif_chroma_interleaved_RRGGBBAA_LE || heifChroma == heif_chroma_interleaved_RRGGBB_BE || heifChroma == heif_chroma_interleaved_RRGGBB_BE) {
479 HeifStrideType stride = 0;
480 dbgFile << "interleaved HDR heif file, bits:" << luma;
481
482 const uint8_t *img =
483 heifGetPlaneMethod(heifimage, heif_channel_interleaved, &stride);
485 layer->paintDevice()->createHLineIteratorNG(0, 0, width);
486
488 linearizePolicy,
489 applyOOTF,
490 hasAlpha,
491 width,
492 height,
493 img,
494 stride,
495 it,
496 displayGamma,
497 displayNits,
498 colorSpace);
499 }
500
501 image->addNode(layer.data(), image->rootLayer().data());
502 image->cropImage(QRect(0, 0, width, height));
503
504 // --- Iterate through all metadata blocks and extract Exif and XMP metadata ---
505
506 std::vector<heif_item_id> metadata_IDs = handle.get_list_of_metadata_block_IDs();
507
508 for (heif_item_id id : metadata_IDs) {
509
510 if (handle.get_metadata_type(id) == "Exif") {
511 // Read exif information
512
513 std::vector<uint8_t> exif_data = handle.get_metadata(id);
514
515 if (exif_data.size()>4) {
516 size_t skip = ((quint32(exif_data[0]) << 24U)
517 | (quint32(exif_data[1])) << 16U
518 | (quint32(exif_data[2]) << 8U) | exif_data[3])
519 + 4u;
520
521 if (exif_data.size() > skip) {
522 KisMetaData::IOBackend *exifIO =
524
525 // Copy the exif data into the byte array
526 QByteArray ba(
527 reinterpret_cast<char *>(exif_data.data() + skip),
528 static_cast<int>(exif_data.size() - skip));
529 QBuffer buf(&ba);
530 exifIO->loadFrom(layer->metaData(), &buf);
531 }
532 }
533 }
534
535 if (handle.get_metadata_type(id) == "mime" &&
536 handle.get_metadata_content_type(id) == "application/rdf+xml") {
537 // Read XMP information
538
539 std::vector<uint8_t> xmp_data = handle.get_metadata(id);
541
542 // Copy the xmp data into the byte array
543 QByteArray ba(reinterpret_cast<char *>(xmp_data.data()), static_cast<int>(xmp_data.size()));
544 QBuffer buf(&ba);
545 xmpIO->loadFrom(layer->metaData(), &buf);
546 }
547 }
548
549 document->setCurrentImage(image);
551 } catch (Error &err) {
552 return setHeifError(document, err);
553 }
554}
KisImportExportErrorCode setHeifError(KisDocument *document, heif::Error error)
Definition HeifError.cpp:10
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
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_UNSPECIFIED
@ 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_1
@ TRC_ITU_R_BT_709_5
const quint8 OPACITY_OPAQUE_U8
LinearizePolicy
The KoColorTransferFunctions class.
KisGroupLayerSP rootLayer() const
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
void cropImage(const QRect &newRect)
start asynchronous operation on cropping the image
Definition kis_image.cc:870
virtual bool loadFrom(Store *store, QIODevice *ioDevice) const =0
static KisMetadataBackendRegistry * instance()
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
const T value(const QString &id) const
Definition KoID.h:30
QString id() const
Definition KoID.cpp:63
#define dbgFile
Definition kis_debug.h:53
auto readPlanarLayer(const int luma, Args &&...args)
auto readInterleavedLayer(const int luma, LinearizePolicy linearizePolicy, bool applyOOTF, const int channels, const int width, const int height, const uint8_t *img, const int stride, KisHLineIteratorSP it, float displayGamma, float displayNits, const KoColorSpace *colorSpace)
auto readPlanarLayer(const int luma, LinearizePolicy policy, bool applyOOTF, bool hasAlpha, const int width, const int height, const uint8_t *imgR, const int strideR, const uint8_t *imgG, const int strideG, const uint8_t *imgB, const int strideB, const uint8_t *imgA, const int strideA, KisHLineIteratorSP it, float displayGamma, float displayNits, const KoColorSpace *colorSpace)
auto readInterleavedLayer(LinearizePolicy linearizePolicy, bool applyOOTF, const int channels, const int width, const int height, const uint8_t *img, const int stride, KisHLineIteratorSP it, float displayGamma, float displayNits, const KoColorSpace *colorSpace)
KisMetaData::Store * metaData()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
KisPaintDeviceSP paintDevice
virtual QString colorModelID() const
const KoColorProfile * profileByName(const QString &name) const
QString colorSpaceId(const QString &colorModelId, const QString &colorDepthId) const
const KoColorProfile * profileFor(const QVector< double > &colorants, ColorPrimaries colorPrimaries, TransferCharacteristics transferFunction) const
profileFor tries to find the profile that matches these characteristics, if no such profile is found,...
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
QString defaultProfileForColorSpace(const QString &colorSpaceId) const
const KoColorProfile * createColorProfile(const QString &colorModelId, const QString &colorDepthId, const QByteArray &rawData)
void addProfile(KoColorProfile *profile)

References KisNodeFacade::addNode(), KoColorSpaceRegistry::addProfile(), KisDlgHLGImport::applyOOTF(), KoColorProfile::colorModelID(), KoColorSpaceRegistry::colorSpace(), KoColorSpaceRegistry::colorSpaceId(), KoColorSpaceRegistry::createColorProfile(), KisPaintDevice::createHLineIteratorNG(), KisImage::cropImage(), KisSharedPtr< T >::data(), dbgFile, KoColorSpaceRegistry::defaultProfileForColorSpace(), Float32BitsColorDepthID, KisDlgHLGImport::gamma(), GrayAColorModelID, KoID::id(), KisMetadataBackendRegistry::instance(), KoColorSpaceRegistry::instance(), Integer16BitsColorDepthID, Integer8BitsColorDepthID, KeepTheSame, LinearFromHLG, LinearFromPQ, LinearFromSMPTE428, KisMetaData::IOBackend::loadFrom(), KisLayer::metaData(), KoColorProfile::name, KisImage::nextLayerName(), KisDlgHLGImport::nominalPeakBrightness(), ImportExportCodes::OK, OPACITY_OPAQUE_U8, KisPaintLayer::paintDevice, PRIMARIES_ITU_R_BT_709_5, PRIMARIES_UNSPECIFIED, KoColorSpaceRegistry::profileByName(), KoColorSpaceRegistry::profileFor(), HDR::readInterleavedLayer(), SDR::readInterleavedLayer(), Gray::readPlanarLayer(), Planar::readPlanarLayer(), RGBAColorModelID, KisImage::rootLayer(), setHeifError(), TRC_IEC_61966_2_1, TRC_ITU_R_BT_709_5, TRC_LINEAR, and KoGenericRegistry< T >::value().

◆ supportsIO()

bool HeifImport::supportsIO ( ) const
inlineoverridevirtual

Override and return false for the filters that use a library that cannot handle file handles, only file names.

Reimplemented from KisImportExportFilter.

Definition at line 23 of file HeifImport.h.

23{ return true; }

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