diff --git a/core/app/utils/digikam_globals.cpp b/core/app/utils/digikam_globals.cpp index 6da268fd4c..e1402f17f9 100644 --- a/core/app/utils/digikam_globals.cpp +++ b/core/app/utils/digikam_globals.cpp @@ -1,314 +1,287 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-09-08 * Description : global macros, variables and flags * * Copyright (C) 2009-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 "digikam_globals.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_config.h" #include "digikam_debug.h" #include "drawdecoder.h" #include "rawcameradlg.h" // Windows includes #ifdef HAVE_DRMINGW # include #endif namespace Digikam { QShortcut* defineShortcut(QWidget* const w, const QKeySequence& key, const QObject* receiver, const char* slot) { QShortcut* const s = new QShortcut(w); s->setKey(key); s->setContext(Qt::WidgetWithChildrenShortcut); QObject::connect(s, SIGNAL(activated()), receiver, slot); return s; } QStringList supportedImageMimeTypes(QIODevice::OpenModeFlag mode, QString& allTypes) { QStringList formats; QList supported; switch(mode) { case QIODevice::ReadOnly: supported = QImageReader::supportedImageFormats(); break; case QIODevice::WriteOnly: supported = QImageWriter::supportedImageFormats(); break; case QIODevice::ReadWrite: supported = QImageWriter::supportedImageFormats() + QImageReader::supportedImageFormats(); break; default: qCDebug(DIGIKAM_GENERAL_LOG) << "Unsupported mode!"; break; } - bool tiff = false; - bool jpeg = false; -#ifdef HAVE_JASPER - bool jp2k = false; -#endif // HAVE_JASPER - -#ifdef HAVE_X265 - bool heif = false; -#endif // HAVE_X265 - foreach (const QByteArray& frm, supported) { if (QString::fromLatin1(frm).contains(QLatin1String("tif"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("tiff"), Qt::CaseInsensitive)) { - tiff = true; continue; } if (QString::fromLatin1(frm).contains(QLatin1String("jpg"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("jpeg"), Qt::CaseInsensitive)) { - jpeg = true; continue; } #ifdef HAVE_JASPER if (QString::fromLatin1(frm).contains(QLatin1String("jp2"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("j2k"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("jpx"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("jpc"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("pgx"), Qt::CaseInsensitive)) { - jp2k = true; continue; } #endif // HAVE_JASPER #ifdef HAVE_X265 if (QString::fromLatin1(frm).contains(QLatin1String("heic"), Qt::CaseInsensitive)) { - heif = true; continue; } #endif // HAVE_X265 formats.append(i18n("%1 Image (%2)", QString::fromLatin1(frm).toUpper(), QLatin1String("*.") + QLatin1String(frm))); allTypes.append(QString::fromLatin1("*.%1 ").arg(QLatin1String(frm))); } - if (tiff) - { - formats.append(i18n("TIFF Image (*.tiff *.tif)")); - allTypes.append(QLatin1String("*.tiff *.tif ")); - } - - if (jpeg) - { - formats.append(i18n("JPEG Image (*.jpg *.jpeg *.jpe)")); - allTypes.append(QLatin1String("*.jpg *.jpeg *.jpe ")); - } + formats.append(i18n("TIFF Image (*.tiff *.tif)")); + allTypes.append(QLatin1String("*.tiff *.tif ")); + formats.append(i18n("JPEG Image (*.jpg *.jpeg *.jpe)")); + allTypes.append(QLatin1String("*.jpg *.jpeg *.jpe ")); #ifdef HAVE_JASPER - if (jp2k) - { - formats.append(i18n("JPEG2000 Image (*.jp2 *.j2k *.jpx *.pgx)")); - allTypes.append(QLatin1String("*.jp2 *.j2k *.jpx *.pgx ")); - } + formats.append(i18n("JPEG2000 Image (*.jp2 *.j2k *.jpx *.pgx)")); + allTypes.append(QLatin1String("*.jp2 *.j2k *.jpx *.pgx ")); #endif // HAVE_JASPER formats << i18n("Progressive Graphics file (*.pgf)"); allTypes.append(QLatin1String("*.pgf ")); #ifdef HAVE_X265 - if (heif) - { - formats << i18n("High Efficiency Image Coding (*.heic)"); - allTypes.append(QLatin1String("*.heic ")); - } + formats << i18n("High Efficiency Image Coding (*.heic)"); + allTypes.append(QLatin1String("*.heic ")); #endif // HAVE_X265 if (mode != QIODevice::WriteOnly) { formats << i18n("Raw Images (%1)", DRawDecoder::rawFiles()); allTypes.append(DRawDecoder::rawFiles()); formats << i18n("All supported files (%1)", allTypes); } return formats; } void showRawCameraList() { RawCameraDlg* const dlg = new RawCameraDlg(qApp->activeWindow()); dlg->show(); } QProcessEnvironment adjustedEnvironmentForAppImage() { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); // If we are running into AppImage bundle, switch env var to the right values. if (env.contains(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")) && env.contains(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")) && env.contains(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")) && env.contains(QLatin1String("APPIMAGE_ORIGINAL_PATH"))) { qCDebug(DIGIKAM_GENERAL_LOG) << "Adjusting environment variables for AppImage bundle"; if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")).isEmpty()) { env.insert(QLatin1String("LD_LIBRARY_PATH"), env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH"))); } else { env.remove(QLatin1String("LD_LIBRARY_PATH")); } if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")).isEmpty()) { env.insert(QLatin1String("QT_PLUGIN_PATH"), env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH"))); } else { env.remove(QLatin1String("QT_PLUGIN_PATH")); } if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")).isEmpty()) { env.insert(QLatin1String("XDG_DATA_DIRS"), env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS"))); } else { env.remove(QLatin1String("XDG_DATA_DIRS")); } if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH")).isEmpty()) { env.insert(QLatin1String("PATH"), env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH"))); } else { env.remove(QLatin1String("PATH")); } } return env; } void tryInitDrMingw() { #ifdef HAVE_DRMINGW qCDebug(DIGIKAM_GENERAL_LOG) << "Loading DrMinGw run-time..."; wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QLatin1String("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot find crash handler dll."; return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init crash handler dll."; return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init customized crash file."; return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QLatin1String("\\digikam_crash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit().data()); qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw run-time loaded."; qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw crash-file will be located at: " << logFile; #endif // HAVE_DRMINGW } QString toolButtonStyleSheet() { return QLatin1String("QToolButton { padding: 1px; background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(100, 100, 100, 50%), " " stop: 1 rgba(170, 170, 170, 50%)); " "border: 1px solid rgba(170, 170, 170, 10%); } " "QToolButton:hover { border-color: white; } " "QToolButton:pressed { background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(40, 40, 40, 50%), " " stop: 1 rgba(90, 90, 90, 50%)); " "border-color: white; } " "QToolButton:checked { background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(40, 40, 40, 50%), " " stop: 1 rgba(90, 90, 90, 50%)); } " "QToolButton:disabled { background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(40, 40, 40, 50%), " " stop: 1 rgba(50, 50, 50, 50%)); }"); } } // namespace Digikam diff --git a/core/dplugins/dimg/heif/CMakeLists.txt b/core/dplugins/dimg/heif/CMakeLists.txt index a35fcd990e..7d4d9b0b7d 100644 --- a/core/dplugins/dimg/heif/CMakeLists.txt +++ b/core/dplugins/dimg/heif/CMakeLists.txt @@ -1,191 +1,192 @@ # # Copyright (c) 2015-2019 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include(MacroDPlugins) include_directories($ $ $ $ $ ) include(CheckFunctionExists) include(CheckIncludeFile) CHECK_FUNCTION_EXISTS(posix_memalign HAVE_POSIX_MEMALIGN) CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H) CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H) CHECK_INCLUDE_FILE(stdbool.h HAVE_STDBOOL_H) CHECK_INCLUDE_FILE(inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILE(stddef.h HAVE_STDDEF_H) CHECK_INCLUDE_FILE(strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILE(unistd.h HAVE_UNISTD_H) if(HAVE_INTTYPES_H) add_definitions(-DHAVE_INTTYPES_H) endif() if(HAVE_STDDEF_H) add_definitions(-DHAVE_STDDEF_H) endif() if(HAVE_STRINGS_H) add_definitions(-DHAVE_STRINGS_H) endif() if(HAVE_UNISTD_H) add_definitions(-DHAVE_UNISTD_H) endif() if(HAVE_MALLOC_H) add_definitions(-DHAVE_MALLOC_H) endif() if(HAVE_STDINT_H) add_definitions(-DHAVE_STDINT_H) endif() if(HAVE_STDBOOL_H) add_definitions(-DHAVE_STDBOOL_H) endif() if(HAVE_POSIX_MEMALIGN) add_definitions(-DHAVE_POSIX_MEMALIGN) endif() add_definitions(-DHAVE_LIBDE265=1) set(NUMERIC_VERSION 0x01050100) set(PACKAGE_VERSION 1.5.1) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/libheif/heif_version.h" ) set(NUMERIC_VERSION 0x01000000) set(PACKAGE_VERSION 1.0.0) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libde265/de265-version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/libde265/de265-version.h" ) set(libheif_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/libheif/bitstream.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/box.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/error.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_context.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_file.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_image.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_hevc.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_colorconversion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_plugin_registry.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_plugin.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_decoder_libde265.cc ) if(X265_FOUND) + add_definitions(-DHAVE_X265=1) include_directories(${X265_INCLUDE_DIRS}) set(libheif_SRCS ${libheif_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_encoder_x265.cc ) else() set(X265_LIBRARIES "") endif() foreach(_currentfile ${libheif_SRCS}) if(NOT MSVC) set_source_files_properties(${_currentfile} PROPERTIES COMPILE_FLAGS "-w") endif() endforeach() set(libde265_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/libde265/bitstream.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/cabac.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/de265.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/deblock.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/decctx.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/nal-parser.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/dpb.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/image.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/intrapred.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/md5.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/nal.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/pps.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/transform.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/refpic.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/sao.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/scan.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/sei.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/slice.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/sps.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/util.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/vps.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/vui.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/motion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/threads.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/visualize.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/fallback.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/fallback-motion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/fallback-dct.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/quality.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/configparam.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/image-io.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/alloc_pool.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/en265.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/contextmodel.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-core.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-types.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-params.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-context.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-syntax.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-intrapred.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-motion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encpicbuf.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/sop.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/algo.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/coding-options.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/ctb-qscale.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-split.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-intrapartmode.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-interpartmode.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-skip.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-intra-inter.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-mergeindex.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-split.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-transform.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-intrapredmode.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-rateestim.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/pb-mv.cc ) if(WIN32) set(libde265_SRCS ${libde265_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/libde265/extra/win32cond.cc ) endif() foreach(_currentfile ${libde265_SRCS}) if(NOT MSVC) set_source_files_properties(${_currentfile} PROPERTIES COMPILE_FLAGS "-w") endif() endforeach() set(dimgheifplugin_SRCS ${libheif_SRCS} ${libde265_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifplugin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifloader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifloader_load.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifloader_save.cpp ) DIGIKAM_ADD_DIMG_PLUGIN(NAME HEIF SOURCES ${dimgheifplugin_SRCS} DEPENDS ${X265_LIBRARIES} ) diff --git a/core/dplugins/dimg/heif/dimgheifloader_save.cpp b/core/dplugins/dimg/heif/dimgheifloader_save.cpp index a27369672f..616f901504 100644 --- a/core/dplugins/dimg/heif/dimgheifloader_save.cpp +++ b/core/dplugins/dimg/heif/dimgheifloader_save.cpp @@ -1,474 +1,507 @@ /* ============================================================ * * 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" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_config.h" #include "digikam_debug.h" #include "dimg.h" #include "dimgloaderobserver.h" #include "metaengine.h" // libx265 includes #ifdef HAVE_X265 # include #endif namespace Digikam { int DImgHEIFLoader::x265MaxBitsDepth() { 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 file = fopen(QFile::encodeName(filePath).constData(), "wb"); if (!file) { qWarning() << "Cannot open target image file."; return false; } fclose(file); QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 75; bool lossless = (quality == 0); // --- Determine libx265 encoder bits depth capability: 8=standard, 10, 12, or later 16. int maxOutputBitsDepth = x265MaxBitsDepth(); if (maxOutputBitsDepth == -1) { return false; } heif_chroma chroma; if (maxOutputBitsDepth > 8) // 16 bits image. { chroma = imageHasAlpha() ? heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RRGGBB_BE; } else { chroma = imageHasAlpha() ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB; } // --- use standard HEVC encoder qDebug() << "HEVC encoder setup..."; 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); 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..."; error = heif_image_add_plane(image, heif_channel_interleaved, imageWidth(), imageHeight(), 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 || stride <= 0) { qWarning() << "HEIC data pixels information not valid!"; heif_encoder_release(encoder); heif_context_free(ctx); return false; } qDebug() << "HEIC data container:" << data; qDebug() << "HEIC bytes per line:" << 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 short* src16 = nullptr; unsigned short* dst16 = nullptr; int div16 = 16 - maxOutputBitsDepth; int mul8 = maxOutputBitsDepth - 8; int nbOutputBytesPerColor = (maxOutputBitsDepth > 8) ? (imageHasAlpha() ? 4 * 2 : 3 * 2) // output data stored on 16 bits : (imageHasAlpha() ? 4 : 3 ); // output data stored on 8 bits qDebug() << "HEIC output bytes per color:" << nbOutputBytesPerColor; qDebug() << "HEIC 16 to 8 bits coeff. :" << div16; qDebug() << "HEIC 8 to 16 bits coeff. :" << mul8; for (unsigned int y = 0 ; y < imageHeight() ; ++y) { src = &imageData()[(y * imageWidth()) * imageBytesDepth()]; src16 = reinterpret_cast(src); dst = reinterpret_cast(data + (y * stride)); dst16 = reinterpret_cast(dst); for (unsigned int x = 0 ; x < imageWidth() ; ++x) { if (imageSixteenBit()) // 16 bits source image. { 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[0] = (unsigned short)(r16 >> div16); dst16[1] = (unsigned short)(g16 >> div16); dst16[2] = (unsigned short)(b16 >> div16); if (imageHasAlpha()) { dst16[3] = (unsigned short)(a16 >> div16); dst16 += 4; } else { dst16 += 3; } } else // From 16 bits to 8 bits. { dst[0] = (unsigned char)(r16 >> div16); dst[1] = (unsigned char)(g16 >> div16); dst[2] = (unsigned char)(b16 >> div16); if (imageHasAlpha()) { dst[3] = (unsigned char)(a16 >> div16); dst += 4; } else { dst += 3; } } src16 += 4; } else // 8 bits source image. { b = src[0]; g = src[1]; r = src[2]; if (imageHasAlpha()) { a = src[3]; } if (maxOutputBitsDepth > 8) // From 8 bits to 10 bits or more. { dst16[0] = (unsigned short)(r << mul8); dst16[1] = (unsigned short)(g << mul8); dst16[2] = (unsigned short)(b << mul8); if (imageHasAlpha()) { dst16[3] = (unsigned short)(a << mul8); dst16 += 4; } else { dst16 += 3; } } else // From 8 bits to 8 bits. { dst[0] = r; dst[1] = g; dst[2] = b; if (imageHasAlpha()) { dst[3] = a; dst += 4; } else { dst += 3; } } src += 4; } } 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())))); } } - qDebug() << "HEIC image encoding..."; + qDebug() << "HEIC master image encoding..."; - // --- encode and write image + // --- encode and write master image struct heif_encoding_options* const options = heif_encoding_options_alloc(); options->save_alpha_channel = imageHasAlpha() ? 1 : 0; struct heif_image_handle* image_handle = nullptr; - error = heif_context_encode_image(ctx, image, encoder, options, &image_handle); + error = heif_context_encode_image(ctx, + image, + encoder, + options, + &image_handle); if (!isHeifSuccess(&error)) { heif_encoding_options_free(options); heif_image_handle_release(image_handle); heif_encoder_release(encoder); heif_context_free(ctx); return false; } + // --- encode thumbnail + + // Note: Only encode preview for large image. + // We will use the same preview size than DImg::prepareMetadataToSave() + const int previewSize = 1280; + + if (qMin(imageWidth(), imageHeight()) > previewSize) + { + qDebug() << "HEIC preview storage..."; + + struct heif_image_handle* thumbnail_handle = nullptr; + + error = heif_context_encode_thumbnail(ctx, + image, + image_handle, + encoder, + options, + previewSize, + &thumbnail_handle); + if (!isHeifSuccess(&error)) + { + heif_encoding_options_free(options); + heif_image_handle_release(image_handle); + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + heif_image_handle_release(thumbnail_handle); + } + heif_encoding_options_free(options); heif_encoder_release(encoder); // --- Add Exif and XMP metadata qDebug() << "HEIC metadata storage..."; saveHEICMetadata(ctx, image_handle); heif_image_handle_release(image_handle); // --- 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("HEIF")); saveMetadata(filePath); 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; } qDebug() << "Stored HEIC color profile size:" << profile.size(); } #else Q_UNUSED(image_handle); #endif 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; } QByteArray exif = meta.getExifEncoded(); QByteArray xmp = meta.getXmp(); struct heif_error error; if (!exif.isEmpty()) { error = heif_context_add_exif_metadata(heif_context, image_handle, exif.data(), exif.size()); if (error.code != 0) { qWarning() << "Cannot store HEIC Exif metadata!"; return false; } qDebug() << "Stored HEIC Exif data size:" << exif.size(); } if (!xmp.isEmpty()) { error = heif_context_add_XMP_metadata(heif_context, image_handle, xmp.data(), xmp.size()); if (error.code != 0) { qWarning() << "Cannot store HEIC Xmp metadata!"; return false; } qDebug() << "Stored HEIC Xmp data size:" << xmp.size(); } return true; } } // namespace Digikam diff --git a/core/libs/dimg/dimg.cpp b/core/libs/dimg/dimg.cpp index fbe6ca60fd..a407134ee5 100644 --- a/core/libs/dimg/dimg.cpp +++ b/core/libs/dimg/dimg.cpp @@ -1,192 +1,192 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : digiKam 8/16 bits image management API. * Contructors and destructor. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2019 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * * 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 "dimg_p.h" namespace Digikam { /** - * - * DImg is a framework to support 16bits color depth image. it doesn't aim - * to be a complete imaging library; it uses QImage/ImageMagick for - * load/save files which are not supported natively by it. + * + * DImg is a framework to support 16bits color depth image. it doesn't aim + * to be a complete imaging library; it uses QImage/ImageMagick for + * load/save files which are not supported natively by it. * some of the features: - * - * - Native Image Loaders, for some imageformats which are of interest to - * us: JPEG (complete), TIFF (mostly complete), PNG (complete), JPEG2000 + * + * - Native Image Loaders, for some imageformats which are of interest to + * us: JPEG (complete), TIFF (mostly complete), PNG (complete), JPEG2000 * (complete), RAW (complete through libraw), PGF (complete). * For the rest ImageMAgick codecs or qimageloader are used. - * - * - Metadata preservation: when a file is loaded, its metadata like XMP, - * IPTC, EXIF, JFIF are read and held in memory. now when you save back the - * file to the original file or to a different file, the metadata is + * + * - Metadata preservation: when a file is loaded, its metadata like XMP, + * IPTC, EXIF, JFIF are read and held in memory. now when you save back the + * file to the original file or to a different file, the metadata is * automatically written. All is delegate to Exiv2 library. - * - * - Explicitly Shared Container format (see qt docs): this is necessary for + * + * - Explicitly Shared Container format (see qt docs): this is necessary for * performance reasons. - * - * - 8 bits and 16 bits support: if the file format is 16 bits, it will load up - * the image in 16bits format (TIFF/PNG/JPEG2000/RAW/PGF support) and all - * operations are done in 16 bits format, except when the rendering to screen - * is done, when its converted on the fly to a temporary 8 bits image and then + * + * - 8 bits and 16 bits support: if the file format is 16 bits, it will load up + * the image in 16bits format (TIFF/PNG/JPEG2000/RAW/PGF support) and all + * operations are done in 16 bits format, except when the rendering to screen + * is done, when its converted on the fly to a temporary 8 bits image and then * rendered. - * - * - Basic image manipulation: rotate, flip, color modifications, crop, + * + * - Basic image manipulation: rotate, flip, color modifications, crop, * scale. This has been ported from Imlib2 with 16 bits scaling support * and support for scaling of only a section of the image. - * - * - Rendering to Pixmap: using QImage/QPixmap. (see above for rendering of + * + * - Rendering to Pixmap: using QImage/QPixmap. (see above for rendering of * 16 bits images). - * - * - Pixel format: the pixel format is different from QImage pixel - * format. In QImage the pixel data is stored as unsigned ints and to - * access the individual colors you need to use bit-shifting to ensure - * endian correctness. in DImg, the pixel data is stored as unsigned char. + * + * - Pixel format: the pixel format is different from QImage pixel + * format. In QImage the pixel data is stored as unsigned ints and to + * access the individual colors you need to use bit-shifting to ensure + * endian correctness. in DImg, the pixel data is stored as unsigned char. * the color layout is B,G,R,A (blue, green, red, alpha) - * + * * for 8 bits images: you can access individual color components like this: - * + * * uchar* const pixels = image.bits(); - * + * * for (int i = 0 ; i < image.width() * image.height() ; ++i) * { * pixel[0] // blue * pixel[1] // green * pixel[2] // red * pixel[3] // alpha - * + * * pixel += 4; // go to next pixel * } - * + * * and for 16 bits images: - * + * * ushort* const pixels = (ushort*)image.bits(); - * + * * for (int i = 0 ; i < image.width() * image.height() ; ++i) * { * pixel[0] // blue * pixel[1] // green * pixel[2] // red * pixel[3] // alpha - * + * * pixel += 4; // go to next pixel * } - * + * * The above is true for both big and little endian platforms. What this also - * means is that the pixel format is different from that of QImage for big - * endian machines. Functions are provided if you want to get a copy of the + * means is that the pixel format is different from that of QImage for big + * endian machines. Functions are provided if you want to get a copy of the * DImg as a QImage. - * + * */ DImg::DImg() : m_priv(new Private) { } DImg::DImg(const QByteArray& filePath, DImgLoaderObserver* const observer, const DRawDecoding& rawDecodingSettings) : m_priv(new Private) { load(QString::fromUtf8(filePath), observer, rawDecodingSettings); } DImg::DImg(const QString& filePath, DImgLoaderObserver* const observer, const DRawDecoding& rawDecodingSettings) : m_priv(new Private) { load(filePath, observer, rawDecodingSettings); } DImg::DImg(const DImg& image) : m_priv(image.m_priv) { } DImg::DImg(uint width, uint height, bool sixteenBit, bool alpha, uchar* const data, bool copyData) : m_priv(new Private) { putImageData(width, height, sixteenBit, alpha, data, copyData); } DImg::DImg(const DImg& image, int w, int h) : m_priv(new Private) { // This private constructor creates a copy of everything except the data. // The image size is set to the given values and a buffer corresponding to these values is allocated. // This is used by copy and scale. copyImageData(image.m_priv); copyMetaData(image.m_priv); setImageDimension(w, h); allocateData(); } DImg::DImg(const QImage& image) : m_priv(new Private) { if (!image.isNull()) { QImage target; if (image.format() == QImage::Format_RGB32 || image.format() == QImage::Format_ARGB32) { target = image; } else { target = image.convertToFormat(QImage::Format_ARGB32); } setImageData(true, image.width(), image.height(), false, image.hasAlphaChannel()); if (allocateData()) { uint* sptr = reinterpret_cast(target.bits()); uchar* dptr = m_priv->data; const uint pixels = numPixels(); for (uint i = 0 ; i < pixels ; ++i) { dptr[0] = qBlue(*sptr); dptr[1] = qGreen(*sptr); dptr[2] = qRed(*sptr); dptr[3] = qAlpha(*sptr); dptr += 4; ++sptr; } } } } DImg::~DImg() { } } // namespace Digikam diff --git a/core/libs/metadataengine/engine/metaengine_previews.cpp b/core/libs/metadataengine/engine/metaengine_previews.cpp index 2b1bd2aede..a487f17eee 100644 --- a/core/libs/metadataengine/engine/metaengine_previews.cpp +++ b/core/libs/metadataengine/engine/metaengine_previews.cpp @@ -1,242 +1,242 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-09-15 * Description : Exiv2 library interface. * Embedded preview loading. * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * * 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. * * ============================================================ */ // Local includes #include "metaengine_previews.h" #include "metaengine_p.h" #include "metaengine.h" #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN MetaEnginePreviews::Private { public: explicit Private() { manager = nullptr; } ~Private() { delete manager; } void load(Exiv2::Image::AutoPtr image_) { QMutexLocker lock(&s_metaEngineMutex); try { #if EXIV2_TEST_VERSION(0,27,99) image = std::move(image_); #else image = image_; #endif image->readMetadata(); manager = new Exiv2::PreviewManager(*image); Exiv2::PreviewPropertiesList props = manager->getPreviewProperties(); // reverse order of list, which is smallest-first Exiv2::PreviewPropertiesList::reverse_iterator it; for (it = props.rbegin() ; it != props.rend() ; ++it) { properties << *it; } } catch(Exiv2::AnyError& e) { MetaEngine::Private::printExiv2ExceptionError(QLatin1String("Cannot load preview data using Exiv2 "), e); } catch(...) { qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; } } public: Exiv2::Image::AutoPtr image; Exiv2::PreviewManager* manager; QList properties; }; MetaEnginePreviews::MetaEnginePreviews(const QString& filePath) : d(new Private) { QMutexLocker lock(&s_metaEngineMutex); try { Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((const char*)(QFile::encodeName(filePath).constData())); #if EXIV2_TEST_VERSION(0,27,99) d->load(std::move(image)); #else d->load(image); #endif } catch(Exiv2::AnyError& e) { MetaEngine::Private::printExiv2ExceptionError(QLatin1String("Cannot load metadata using Exiv2 "), e); } catch(...) { qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; } } MetaEnginePreviews::MetaEnginePreviews(const QByteArray& imgData) : d(new Private) { QMutexLocker lock(&s_metaEngineMutex); try { Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((Exiv2::byte*)imgData.data(), imgData.size()); #if EXIV2_TEST_VERSION(0,27,99) d->load(std::move(image)); #else d->load(image); #endif } catch(Exiv2::AnyError& e) { MetaEngine::Private::printExiv2ExceptionError(QLatin1String("Cannot load metadata using Exiv2 "), e); } catch(...) { qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; } } MetaEnginePreviews::~MetaEnginePreviews() { delete d; } bool MetaEnginePreviews::isEmpty() { return d->properties.isEmpty(); } QSize MetaEnginePreviews::originalSize() const { if (d->image.get()) return QSize(d->image->pixelWidth(), d->image->pixelHeight()); return QSize(); } QString MetaEnginePreviews::originalMimeType() const { if (d->image.get()) return QLatin1String(d->image->mimeType().c_str()); return QString(); } int MetaEnginePreviews::count() { return d->properties.size(); } int MetaEnginePreviews::dataSize(int index) { if (index < 0 || index >= size()) return 0; return d->properties[index].size_; } int MetaEnginePreviews::width(int index) { if (index < 0 || index >= size()) return 0; return d->properties[index].width_; } int MetaEnginePreviews::height(int index) { if (index < 0 || index >= size()) return 0; return d->properties[index].height_; } QString MetaEnginePreviews::mimeType(int index) { if (index < 0 || index >= size()) return QString(); return QLatin1String(d->properties[index].mimeType_.c_str()); } QString MetaEnginePreviews::fileExtension(int index) { if (index < 0 || index >= size()) return QString(); return QLatin1String(d->properties[index].extension_.c_str()); } QByteArray MetaEnginePreviews::data(int index) { if (index < 0 || index >= size()) return QByteArray(); - qCDebug(DIGIKAM_METAENGINE_LOG) << "index: " << index; - qCDebug(DIGIKAM_METAENGINE_LOG) << "d->properties: " << count(); + qCDebug(DIGIKAM_METAENGINE_LOG) << "index : " << index; + qCDebug(DIGIKAM_METAENGINE_LOG) << "properties: " << count(); QMutexLocker lock(&s_metaEngineMutex); try { Exiv2::PreviewImage image = d->manager->getPreviewImage(d->properties[index]); return QByteArray((const char*)image.pData(), image.size()); } catch(Exiv2::AnyError& e) { MetaEngine::Private::printExiv2ExceptionError(QLatin1String("Cannot load metadata using Exiv2 "), e); return QByteArray(); } catch(...) { qCCritical(DIGIKAM_METAENGINE_LOG) << "Default exception from Exiv2"; return QByteArray(); } } QImage MetaEnginePreviews::image(int index) { QByteArray previewData = data(index); QImage image; if (!image.loadFromData(previewData)) return QImage(); return image; } } // namespace Digikam