diff --git a/core/dplugins/dimg/heif/dimgheifloader.cpp b/core/dplugins/dimg/heif/dimgheifloader.cpp index 9e6b3b9061..653b4fe41b 100644 --- a/core/dplugins/dimg/heif/dimgheifloader.cpp +++ b/core/dplugins/dimg/heif/dimgheifloader.cpp @@ -1,69 +1,76 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : A HEIF IO file for DImg framework * * Copyright (C) 2019 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimgheifloader.h" // Local includes #include "digikam_debug.h" #include "dimg.h" #include "dimgloaderobserver.h" namespace Digikam { DImgHEIFLoader::DImgHEIFLoader(DImg* const image) : DImgLoader(image) { - m_hasAlpha = false; - m_sixteenBit = false; - m_observer = nullptr; + m_hasAlpha = false; + m_sixteenBit = false; + m_observer = nullptr; + m_maxOutputBitsDepth = -1; + m_quality = 50; + m_lossless = false; } bool DImgHEIFLoader::hasAlpha() const { return m_hasAlpha; } bool DImgHEIFLoader::sixteenBit() const { return m_sixteenBit; } bool DImgHEIFLoader::isReadOnly() const { +#ifdef HAVE_X265 return false; +#else + return true; +#endif } bool DImgHEIFLoader::isHeifSuccess(struct heif_error* const error) { if (error->code == 0) { return true; } qWarning() << "Error while processing HEIC image:" << error->message; return false; } } // namespace Digikam diff --git a/core/dplugins/dimg/heif/dimgheifloader.h b/core/dplugins/dimg/heif/dimgheifloader.h index 16f7ee6668..4692893886 100644 --- a/core/dplugins/dimg/heif/dimgheifloader.h +++ b/core/dplugins/dimg/heif/dimgheifloader.h @@ -1,83 +1,96 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : A HEIF IO file for DImg framework * * Copyright (C) 2019 by Gilles Caulier * * Other HEIF loader implementions: * https://github.com/KDE/krita/tree/master/plugins/impex/heif * https://github.com/jakar/qt-heif-image-plugin * https://github.com/ImageMagick/ImageMagick/blob/master/coders/heic.c * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_HEIF_LOADER_H #define DIGIKAM_DIMG_HEIF_LOADER_H // Local includes #include "dimg.h" #include "dimgloader.h" #include "digikam_export.h" #include "heif.h" using namespace Digikam; namespace Digikam { class DIGIKAM_EXPORT DImgHEIFLoader : public DImgLoader { public: explicit DImgHEIFLoader(DImg* const image); bool load(const QString& filePath, DImgLoaderObserver* const observer) override; bool save(const QString& filePath, DImgLoaderObserver* const observer) override; virtual bool hasAlpha() const override; virtual bool sixteenBit() const override; virtual bool isReadOnly() const override; private: bool isHeifSuccess(struct heif_error* const error); // Read operations bool readHEICColorProfile(struct heif_image_handle* const image_handle); bool readHEICMetadata(struct heif_image_handle* const image_handle); bool readHEICImageByID(struct heif_context* const heif_context, heif_item_id image_id); // Save operations bool saveHEICColorProfile(struct heif_image* const image); bool saveHEICMetadata(struct heif_context* const heif_context, struct heif_image_handle* const image_handle); + bool saveHEIC8bits(const QString& filePath); + bool saveHEICHdr(const QString& filePath); + + /** + * Determine libx265 encoder bits depth capability: 8=standard, 10, 12, or more. + * Return -1 if encoder instance is not found. + */ + int x265MaxBitsDepth() const; + private: + int m_quality; + bool m_lossless; + + int m_maxOutputBitsDepth; bool m_sixteenBit; bool m_hasAlpha; DImgLoaderObserver* m_observer; }; } // namespace Digikam #endif // DIGIKAM_DIMG_HEIF_LOADER_H diff --git a/core/dplugins/dimg/heif/dimgheifloader_save.cpp b/core/dplugins/dimg/heif/dimgheifloader_save.cpp index f98321a196..f353666cc4 100644 --- a/core/dplugins/dimg/heif/dimgheifloader_save.cpp +++ b/core/dplugins/dimg/heif/dimgheifloader_save.cpp @@ -1,437 +1,643 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : A HEIF IO file for DImg framework - save operations * * Copyright (C) 2019 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimgheifloader.h" // C includes #include // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "dimg.h" #include "dimgloaderobserver.h" #include "metaengine.h" // libx265 includes #ifdef HAVE_X265 # include #endif namespace Digikam { -bool DImgHEIFLoader::save(const QString& filePath, DImgLoaderObserver* const observer) +int DImgHEIFLoader::x265MaxBitsDepth() const { -#ifdef HAVE_X265 - - m_observer = observer; - - // ------------------------------------------------------------------- - // Open the file - - FILE* const f = fopen(QFile::encodeName(filePath).constData(), "wb"); - - if (!f) - { - qWarning() << "Cannot open target image file."; - return false; - } - - QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); - int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 50; - QVariant losslessAttr = imageGetAttribute(QLatin1String("lossless")); - bool lossless = losslessAttr.isValid() ? qualityAttr.toBool() : false; - - // --- Determine libx265 encoder bits depth capability: 8=standard, 10, 12, or later 16. - int maxOutputBitsDepth = -1; +#ifdef HAVE_X265 + for (int i = 16 ; i >= 8 ; i-=2) { qDebug() << "Check HEVC encoder for" << i << "bits encoding..."; const x265_api* const api = x265_api_get(i); if (api) { maxOutputBitsDepth = i; break; } } qDebug() << "HEVC encoder max bits depth:" << maxOutputBitsDepth; +#endif + if (maxOutputBitsDepth == -1) { qWarning() << "Cannot get max supported HEVC encoder bits depth!"; + } + + return maxOutputBitsDepth; +} + +bool DImgHEIFLoader::save(const QString& filePath, DImgLoaderObserver* const observer) +{ + m_observer = observer; + + // ------------------------------------------------------------------- + // Open the file + + FILE* const f = fopen(QFile::encodeName(filePath).constData(), "wb"); + + if (!f) + { + qWarning() << "Cannot open target image file."; return false; } - heif_chroma chroma; + fclose(f); + + QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); + m_quality = qualityAttr.isValid() ? qualityAttr.toInt() : 50; + QVariant losslessAttr = imageGetAttribute(QLatin1String("lossless")); + m_lossless = losslessAttr.isValid() ? qualityAttr.toBool() : false; + m_maxOutputBitsDepth = x265MaxBitsDepth(); - if (maxOutputBitsDepth > 8) // 16 bits image. + if (m_maxOutputBitsDepth == -1) { - chroma = imageHasAlpha() ? heif_chroma_interleaved_RRGGBBAA_BE - : heif_chroma_interleaved_RRGGBB_BE; + return false; + } + + bool ret = false; + + if (m_maxOutputBitsDepth > 8) // HDR encoder is available + { + qDebug() << "HEIC export as HDR image..."; + ret = saveHEICHdr(filePath); } else { - chroma = imageHasAlpha() ? heif_chroma_interleaved_RGBA - : heif_chroma_interleaved_RGB; + qDebug() << "HEIC export as 8 bits image..."; + ret = saveHEIC8bits(filePath); } + + return ret; +} - // --- use standard HEVC encoder - - qDebug() << "HEVC encoder setup..."; +bool DImgHEIFLoader::saveHEIC8bits(const QString& filePath) +{ + heif_chroma chroma = imageHasAlpha() ? heif_chroma_interleaved_RGBA + : heif_chroma_interleaved_RGB; + + qDebug() << "HEVC encoder setup with chroma profile" << chroma; struct heif_context* const ctx = heif_context_alloc(); if (!ctx) { qWarning() << "Cannot create HEIC context!"; return false; } struct heif_encoder* encoder = nullptr; struct heif_error error = heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder); if (!isHeifSuccess(&error)) { heif_context_free(ctx); return false; } - heif_encoder_set_lossy_quality(encoder, quality); - heif_encoder_set_lossless(encoder, lossless); + heif_encoder_set_lossy_quality(encoder, m_quality); + heif_encoder_set_lossless(encoder, m_lossless); struct heif_image* image = nullptr; error = heif_image_create(imageWidth(), imageHeight(), heif_colorspace_RGB, chroma, &image); if (!isHeifSuccess(&error)) { heif_encoder_release(encoder); heif_context_free(ctx); return false; } // --- Save color profile before to create image data, as converting to color space can be processed at this stage. qDebug() << "HEIC set color profile..."; saveHEICColorProfile(image); // --- Add image data - qDebug() << "HEIC setup data plane..."; + qDebug() << "HEIC setup data plane with size (" << imageWidth() << "x" << imageHeight() << ")"; error = heif_image_add_plane(image, heif_channel_interleaved, imageWidth(), imageHeight(), - maxOutputBitsDepth); + m_maxOutputBitsDepth); if (!isHeifSuccess(&error)) { heif_encoder_release(encoder); heif_context_free(ctx); return false; } int stride = 0; uint8_t* const data = heif_image_get_plane(image, heif_channel_interleaved, &stride); if (!data) { qWarning() << "Cannot get HEIC RGB plane!"; heif_encoder_release(encoder); heif_context_free(ctx); return false; } qDebug() << "HEIC plane details: ptr=" << data << "stride=" << stride; uint checkpoint = 0; unsigned char r = 0; unsigned char g = 0; unsigned char b = 0; unsigned char a = 0; - unsigned char* src = nullptr; - unsigned char* dst = nullptr; unsigned short r16 = 0; unsigned short g16 = 0; unsigned short b16 = 0; unsigned short a16 = 0; + unsigned char* src = nullptr; + unsigned char* dst = nullptr; unsigned short* src16 = nullptr; - unsigned short* dst16 = nullptr; - float div16 = (16 - maxOutputBitsDepth) > 0 ? 16.0 - maxOutputBitsDepth : 1.0; - float mul8 = (maxOutputBitsDepth - 8) > 0 ? maxOutputBitsDepth - 8.0 : 1.0; - int nbOutputBytesPerColor = (maxOutputBitsDepth > 8) ? (imageHasAlpha() ? 4 * 2 : 3 * 2) // output data stored on 16 bits - : (imageHasAlpha() ? 4 : 3 ); // output data stored on 8 bits + float mul8 = (m_maxOutputBitsDepth - 8) > 0 ? m_maxOutputBitsDepth - 8.0 : 1.0; + int nbOutputBytesPerColor = imageHasAlpha() ? 4 : 3; qDebug() << "HEIC output bytes per color:" << nbOutputBytesPerColor; - qDebug() << "HEIC 16 to 8 bits coeff. :" << div16; - qDebug() << "HEIC 8 to 16 bits coeff. :" << mul8; + qDebug() << "HEIC bits conversion coeff.:" << mul8; - for (unsigned int y = 0 ; y < imageHeight() ; ++y) + for (unsigned int y = 0 ; y < 20/*imageHeight()*/ ; ++y) { if (m_observer && y == (long)checkpoint) { checkpoint += granularity(m_observer, imageHeight(), 0.8F); if (!m_observer->continueQuery(m_image)) { heif_encoder_release(encoder); heif_context_free(ctx); return false; } m_observer->progressInfo(m_image, 0.1 + (0.8 * (((float)y) / ((float)imageHeight())))); } for (unsigned int x = 0 ; x < imageWidth() ; ++x) { - if (imageSixteenBit()) // 16 bits image. + if (imageSixteenBit()) { + // From 16 bits to 8 bits. + src16 = reinterpret_cast(&imageData()[((y * imageWidth()) + x) * imageBytesDepth()]); b16 = src16[0]; g16 = src16[1]; r16 = src16[2]; if (imageHasAlpha()) { a16 = src16[3]; } - if (maxOutputBitsDepth > 8) // From 16 bits to 10 bits or more. - { - dst16 = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); - dst16[0] = (unsigned short)floor(r16 / div16); - dst16[1] = (unsigned short)floor(g16 / div16); - dst16[2] = (unsigned short)floor(b16 / div16); - - if (imageHasAlpha()) - { - dst16[3] = (unsigned short)floor(a16 / div16); - } - } - else // From 16 bits to 8 bits. + dst = reinterpret_cast(data + (y * stride) + (x * nbOutputBytesPerColor)); + dst[0] = (unsigned char)floor(r16 / mul8); + dst[1] = (unsigned char)floor(g16 / mul8); + dst[2] = (unsigned char)floor(b16 / mul8); + + if (imageHasAlpha()) { - dst = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); - dst[0] = (unsigned char)floor(r16 / div16); - dst[1] = (unsigned char)floor(g16 / div16); - dst[2] = (unsigned char)floor(b16 / div16); - - if (imageHasAlpha()) - { - dst[3] = (unsigned char)floor(a16 / div16); - } + dst[3] = (unsigned char)floor(a16 / mul8); } } - else // 8 bits image. + else { + // From 8 bits to 8 bits. + src = &imageData()[((y * imageWidth()) + x) * imageBytesDepth()]; - b = src[0]; - g = src[1]; - r = src[2]; + b = 128;//src[0]; + g = 128;//src[1]; + r = 128;//src[2]; if (imageHasAlpha()) { a = src[3]; } - if (maxOutputBitsDepth > 8) // From 8 bits to 10 bits or more. - { - dst16 = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); - dst16[0] = (unsigned short)floor(r * mul8); - dst16[1] = (unsigned short)floor(g * mul8); - dst16[2] = (unsigned short)floor(b * mul8); - - if (imageHasAlpha()) - { - dst16[3] = (unsigned short)floor(a * mul8); - } - } - else // From 8 bits to 8 bits. + dst = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); + dst[0] = r; + dst[1] = g; + dst[2] = b; + + if (imageHasAlpha()) { - dst = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); - dst[0] = r; - dst[1] = g; - dst[2] = b; - - if (imageHasAlpha()) - { - dst[3] = a; - } + dst[3] = a; } } } } qDebug() << "HEIC image encoding..."; // --- encode and write image struct heif_encoding_options* options = heif_encoding_options_alloc(); options->save_alpha_channel = imageHasAlpha() ? 1 : 0; struct heif_image_handle* hdl = nullptr; error = heif_context_encode_image(ctx, image, encoder, options, &hdl); if (!isHeifSuccess(&error)) { heif_encoding_options_free(options); heif_image_handle_release(hdl); heif_encoder_release(encoder); heif_context_free(ctx); return false; } heif_encoding_options_free(options); heif_encoder_release(encoder); // --- Add Exif and XMP metadata qDebug() << "HEIC metadata embeding..."; saveHEICMetadata(ctx, hdl); heif_image_handle_release(hdl); // --- TODO: Add thumnail image. // --- write HEIF file qDebug() << "HEIC flush to file..."; error = heif_context_write_to_file(ctx, QFile::encodeName(filePath).constData()); if (!isHeifSuccess(&error)) { heif_context_free(ctx); return false; } heif_context_free(ctx); imageSetAttribute(QLatin1String("savedFormat"), QLatin1String("HEIC")); saveMetadata(filePath); -#endif - return true; } -bool DImgHEIFLoader::saveHEICColorProfile(struct heif_image* const image) +bool DImgHEIFLoader::saveHEICHdr(const QString& filePath) { -#if LIBHEIF_NUMERIC_VERSION >= 0x01040000 + heif_chroma chroma = imageHasAlpha() ? heif_chroma_interleaved_RRGGBBAA_BE + : heif_chroma_interleaved_RRGGBB_BE; - QByteArray profile = m_image->getIccProfile().data(); + qDebug() << "HEVC encoder setup with chroma profile" << chroma; - if (!profile.isEmpty()) + struct heif_context* const ctx = heif_context_alloc(); + + if (!ctx) { - // Save color profile. + qWarning() << "Cannot create HEIC context!"; + return false; + } - struct heif_error error = heif_image_set_raw_color_profile(image, - "prof", // FIXME: detect string in profile data - profile.data(), - profile.size()); + struct heif_encoder* encoder = nullptr; + struct heif_error error = heif_context_get_encoder_for_format(ctx, + heif_compression_HEVC, + &encoder); - if (error.code != 0) + if (!isHeifSuccess(&error)) + { + heif_context_free(ctx); + return false; + } + + heif_encoder_set_lossy_quality(encoder, m_quality); + heif_encoder_set_lossless(encoder, m_lossless); + + struct heif_image* image = nullptr; + error = heif_image_create(imageWidth(), + imageHeight(), + heif_colorspace_RGB, + chroma, + &image); + + if (!isHeifSuccess(&error)) + { + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + // --- Save color profile before to create image data, as converting to color space can be processed at this stage. + + qDebug() << "HEIC set color profile..."; + + saveHEICColorProfile(image); + + // --- Add image data + + qDebug() << "HEIC setup data plane with size (" << imageWidth() << "x" << imageHeight() << ")"; + + error = heif_image_add_plane(image, + heif_channel_interleaved, + imageWidth(), + imageHeight(), + m_maxOutputBitsDepth); + + if (!isHeifSuccess(&error)) + { + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + int stride = 0; + uint8_t* const data = heif_image_get_plane(image, + heif_channel_interleaved, + &stride); + + if (!data) + { + qWarning() << "Cannot get HEIC RGB plane!"; + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + qDebug() << "HEIC plane details: ptr=" << data << "stride=" << stride; + + uint checkpoint = 0; + unsigned char r = 0; + unsigned char g = 0; + unsigned char b = 0; + unsigned char a = 0; + unsigned short r16 = 0; + unsigned short g16 = 0; + unsigned short b16 = 0; + unsigned short a16 = 0; + unsigned char* src = nullptr; + unsigned short* src16 = nullptr; + unsigned short* dst16 = nullptr; + float div16 = (16 - m_maxOutputBitsDepth) > 0 ? 16.0 - m_maxOutputBitsDepth : 1.0; + int nbOutputBytesPerColor = imageHasAlpha() ? 8 : 6; + + qDebug() << "HEIC output bytes per color:" << nbOutputBytesPerColor; + qDebug() << "HEIC bits conversion coeff.:" << div16; + + for (unsigned int y = 0 ; y < imageHeight() ; ++y) + { + if (m_observer && y == (long)checkpoint) { - qWarning() << "Cannot set HEIC color profile!"; - return false; + checkpoint += granularity(m_observer, imageHeight(), 0.8F); + + if (!m_observer->continueQuery(m_image)) + { + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + m_observer->progressInfo(m_image, 0.1 + (0.8 * (((float)y) / ((float)imageHeight())))); + } + + for (unsigned int x = 0 ; x < imageWidth() ; ++x) + { + if (imageSixteenBit()) + { + src16 = reinterpret_cast(&imageData()[((y * imageWidth()) + x) * imageBytesDepth()]); + + b16 = src16[0]; + g16 = src16[1]; + r16 = src16[2]; + + if (imageHasAlpha()) + { + a16 = src16[3]; + } + + // From 16 bits to 10 bits or more. + dst16 = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); + dst16[0] = (unsigned short)floor(r16 / div16); + dst16[1] = (unsigned short)floor(g16 / div16); + dst16[2] = (unsigned short)floor(b16 / div16); + + if (imageHasAlpha()) + { + dst16[3] = (unsigned short)floor(a16 / div16); + } + } + else + { + src = &imageData()[((y * imageWidth()) + x) * imageBytesDepth()]; + + b = src[0]; + g = src[1]; + r = src[2]; + + if (imageHasAlpha()) + { + a = src[3]; + } + + // From 8 bits to 10 bits or more. + dst16 = reinterpret_cast(data) + (y * stride) + (x * nbOutputBytesPerColor); + dst16[0] = (unsigned short)floor(r * div16); + dst16[1] = (unsigned short)floor(g * div16); + dst16[2] = (unsigned short)floor(b * div16); + + if (imageHasAlpha()) + { + dst16[3] = (unsigned short)floor(a * div16); + } + } } } -#else - Q_UNUSED(image_handle); -#endif + + qDebug() << "HEIC image encoding..."; + + // --- encode and write image + + struct heif_encoding_options* options = heif_encoding_options_alloc(); + options->save_alpha_channel = imageHasAlpha() ? 1 : 0; + struct heif_image_handle* hdl = nullptr; + error = heif_context_encode_image(ctx, image, encoder, options, &hdl); + + if (!isHeifSuccess(&error)) + { + heif_encoding_options_free(options); + heif_image_handle_release(hdl); + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + heif_encoding_options_free(options); + heif_encoder_release(encoder); + + // --- Add Exif and XMP metadata + + qDebug() << "HEIC metadata embeding..."; + + saveHEICMetadata(ctx, hdl); + + heif_image_handle_release(hdl); + + // --- TODO: Add thumnail image. + + // --- write HEIF file + + qDebug() << "HEIC flush to file..."; + + error = heif_context_write_to_file(ctx, QFile::encodeName(filePath).constData()); + + if (!isHeifSuccess(&error)) + { + heif_context_free(ctx); + return false; + } + + heif_context_free(ctx); + + imageSetAttribute(QLatin1String("savedFormat"), QLatin1String("HEIC")); + saveMetadata(filePath); return true; } bool DImgHEIFLoader::saveHEICMetadata(struct heif_context* const heif_context, struct heif_image_handle* const image_handle) { MetaEngine meta(m_image->getMetadata()); if (!meta.hasExif() && !meta.hasXmp()) { return false; } struct heif_error error; if (meta.hasExif()) { QByteArray exif = meta.getExifEncoded(true); error = heif_context_add_exif_metadata(heif_context, image_handle, exif.data(), exif.size()); if (error.code != 0) { qWarning() << "Cannot set HEIC Exif metadata!"; return false; } } if (meta.hasXmp()) { QByteArray xmp = meta.getExifEncoded(); error = heif_context_add_XMP_metadata(heif_context, image_handle, xmp.data(), xmp.size()); if (error.code != 0) { qWarning() << "Cannot set HEIC Xmp metadata!"; return false; } } return true; } +bool DImgHEIFLoader::saveHEICColorProfile(struct heif_image* const image) +{ +#if LIBHEIF_NUMERIC_VERSION >= 0x01040000 + + QByteArray profile = m_image->getIccProfile().data(); + + if (!profile.isEmpty()) + { + // Save color profile. + + struct heif_error error = heif_image_set_raw_color_profile(image, + "prof", // FIXME: detect string in profile data + profile.data(), + profile.size()); + + if (error.code != 0) + { + qWarning() << "Cannot set HEIC color profile!"; + return false; + } + } +#else + Q_UNUSED(image_handle); +#endif + + return true; +} + + } // namespace Digikam