diff --git a/core/libs/dimg/filters/greycstoration/greycstorationfilter.cpp b/core/libs/dimg/filters/greycstoration/greycstorationfilter.cpp index 627ff16c3d..4c276936d3 100644 --- a/core/libs/dimg/filters/greycstoration/greycstorationfilter.cpp +++ b/core/libs/dimg/filters/greycstoration/greycstorationfilter.cpp @@ -1,573 +1,579 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-12-03 * Description : Greycstoration interface. * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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. * * ============================================================ */ // Don't use CImg interface (keyboard/mouse interaction). #define cimg_display 0 // Only print debug information on the console. #define cimg_debug 1 #include "greycstorationfilter.h" // C++ includes #include // Qt includes #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "dynamicthread.h" #define cimg_plugin "cimg/greycstoration.h" // Uncomment this line if you use future GreycStoration implementation with GFact parameter. #define GREYSTORATION_USING_GFACT 1 // Pragma directives to reduce warnings from CImg header files. #if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-local-typedefs" # pragma GCC diagnostic ignored "-Wunused-but-set-variable" # pragma GCC diagnostic ignored "-Wshift-negative-value" # pragma GCC diagnostic ignored "-Wmisleading-indentation" # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" # pragma GCC diagnostic ignored "-Wdate-time" #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-parameter" # pragma clang diagnostic ignored "-Wcast-align" # pragma clang diagnostic ignored "-Wshift-negative-value" # pragma clang diagnostic ignored "-Wunused-local-typedef" # pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" # pragma clang diagnostic ignored "-Wdate-time" #endif // CImg includes #include "cimg/CImg.h" // Restore warnings #if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) # pragma GCC diagnostic pop #endif #if defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif extern "C" { #include } namespace Digikam { using namespace cimg_library; class Q_DECL_HIDDEN GreycstorationFilter::Private { public: explicit Private() : gfact(1.0), computationThreads(2), mode(GreycstorationFilter::Restore), threadManager(new CImg<>::GreycstorationThreadManager) { } ~Private() { delete threadManager; } public: float gfact; int computationThreads; // Number of threads used by CImg during computation. int mode; // The interface running mode. QSize newSize; QImage inPaintingMask; // Mask for inpainting. GreycstorationContainer settings; // Current Greycstoraion algorithm settings. CImg<> img; // Main image. CImg mask; // The mask used with inpaint or resize mode CImg<>::GreycstorationThreadManager* threadManager; }; GreycstorationFilter::GreycstorationFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { setOriginalImage(DImg()); setSettings(GreycstorationContainer()); setMode(Restore); setInPaintingMask(QImage()); } GreycstorationFilter::GreycstorationFilter(DImg* const orgImage, const GreycstorationContainer& settings, int mode, int newWidth, int newHeight, const QImage& inPaintingMask, QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { setOriginalImage(orgImage->copyImageData()); setSettings(settings); setMode(mode, newWidth, newHeight); setInPaintingMask(inPaintingMask); setup(); } GreycstorationFilter::~GreycstorationFilter() { // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. this->cancelFilter(); delete d; } QString GreycstorationFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Greycstoration Filter")); } void GreycstorationFilter::setSettings(const GreycstorationContainer& settings) { d->settings = settings; } void GreycstorationFilter::setMode(int mode, int newWidth, int newHeight) { d->mode = mode; d->newSize = QSize(newWidth, newHeight); } void GreycstorationFilter::setInPaintingMask(const QImage& inPaintingMask) { d->inPaintingMask = inPaintingMask; } void GreycstorationFilter::computeChildrenThreads() { const int numProcs = qMax(QThread::idealThreadCount(), 1); const int maxThreads = 16; d->computationThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2)); qCDebug(DIGIKAM_DIMG_LOG) << "GreycstorationFilter::Computation threads: " << d->computationThreads; } void GreycstorationFilter::setup() { computeChildrenThreads(); if (m_orgImage.sixteenBit()) // 16 bits image. { d->gfact = 1.0 / 256.0; } if ((d->mode == Resize) || (d->mode == SimpleResize)) { m_destImage = DImg(d->newSize.width(), d->newSize.height(), m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); qCDebug(DIGIKAM_DIMG_LOG) << "GreycstorationFilter::Resize: new size: (" << d->newSize.width() << ", " << d->newSize.height() << ")"; } else { m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); } // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. + this->initFilter(); } QString GreycstorationFilter::cimgVersionString() { return QString::number(cimg_version); } // We need to re-implement this method from DImgThreadedFilter class because // target image size can be different from original if d->mode = Resize. void GreycstorationFilter::initFilter() { // (left out here: creation of m_destImage) if (m_master) { startFilterDirectly(); } } void GreycstorationFilter::cancelFilter() { // Because Greycstoration algorithm run in a child thread, we need // to stop it before to stop this thread. + qCDebug(DIGIKAM_DIMG_LOG) << "Stop Greycstoration computation..."; d->threadManager->stop(); // And now when stop main loop and clean up all + DImgThreadedFilter::cancelFilter(); } void GreycstorationFilter::filterImage() { int x, y; qCDebug(DIGIKAM_DIMG_LOG) << "Initialization..."; uchar* const data = m_orgImage.bits(); int width = m_orgImage.width(); int height = m_orgImage.height(); // convert DImg (interleaved RGBA) to CImg (planar RGBA) + if (!m_orgImage.sixteenBit()) // 8 bits image. { d->img = CImg(data, 4, width, height, 1, false). get_permute_axes("yzvx"); } else // 16 bits image. { d->img = CImg(reinterpret_cast(data), 4, width, height, 1, false). get_permute_axes("yzvx"); } qCDebug(DIGIKAM_DIMG_LOG) << "Process Computation..."; try { switch (d->mode) { case Restore: restoration(); break; case InPainting: inpainting(); break; case Resize: resize(); break; case SimpleResize: simpleResize(); break; } // harvest d->threadManager->finish(); } catch (...) // Everything went wrong. { qCDebug(DIGIKAM_DIMG_LOG) << "Error during Greycstoration filter computation!"; return; } if (!runningFlag()) { return; } // Copy CImg onto destination. qCDebug(DIGIKAM_DIMG_LOG) << "Finalization..."; uchar* const newData = m_destImage.bits(); int newWidth = m_destImage.width(); int newHeight = m_destImage.height(); if (!m_orgImage.sixteenBit()) // 8 bits image. { uchar* ptr = newData; for (y = 0 ; y < newHeight ; ++y) { for (x = 0 ; x < newWidth ; ++x) { // Overwrite RGB values to destination. + ptr[0] = static_cast(d->img(x, y, 0)); // Blue ptr[1] = static_cast(d->img(x, y, 1)); // Green ptr[2] = static_cast(d->img(x, y, 2)); // Red ptr[3] = static_cast(d->img(x, y, 3)); // Alpha ptr += 4; } } } else // 16 bits image. { unsigned short* ptr = reinterpret_cast(newData); for (y = 0 ; y < newHeight ; ++y) { for (x = 0 ; x < newWidth ; ++x) { // Overwrite RGB values to destination. + ptr[0] = static_cast(d->img(x, y, 0)); // Blue ptr[1] = static_cast(d->img(x, y, 1)); // Green ptr[2] = static_cast(d->img(x, y, 2)); // Red ptr[3] = static_cast(d->img(x, y, 3)); // Alpha ptr += 4; } } } } void GreycstorationFilter::restoration() { for (uint iter = 0 ; runningFlag() && (iter < d->settings.nbIter) ; ++iter) { // This function will start a thread running one iteration of the GREYCstoration filter. // It returns immediately, so you can do what you want after (update a progress bar for // instance). d->threadManager->start(d->img, d->settings.amplitude, d->settings.sharpness, d->settings.anisotropy, d->settings.alpha, d->settings.sigma, #ifdef GREYSTORATION_USING_GFACT d->gfact, #endif d->settings.dl, d->settings.da, d->settings.gaussPrec, d->settings.interp, d->settings.fastApprox, d->settings.tile, d->settings.btile, d->computationThreads); iterationLoop(iter); } } void GreycstorationFilter::inpainting() { if (!d->inPaintingMask.isNull()) { // Copy the inpainting image data into a CImg type image with three channels and no alpha. int x, y; d->mask = CImg(d->inPaintingMask.width(), d->inPaintingMask.height(), 1, 3); uchar* ptr = d->inPaintingMask.bits(); for (y = 0 ; y < d->inPaintingMask.height() ; ++y) { for (x = 0 ; x < d->inPaintingMask.width() ; ++x) { d->mask(x, y, 0) = ptr[2]; // blue. d->mask(x, y, 1) = ptr[1]; // green. d->mask(x, y, 2) = ptr[0]; // red. ptr += 4; } } } else { qCDebug(DIGIKAM_DIMG_LOG) << "Inpainting image: mask is null!"; stop(); return; } for (uint iter = 0 ; runningFlag() && (iter < d->settings.nbIter) ; ++iter) { // This function will start a thread running one iteration of the GREYCstoration filter. // It returns immediately, so you can do what you want after (update a progress bar for // instance). d->threadManager->start(d->img, &d->mask, d->settings.amplitude, d->settings.sharpness, d->settings.anisotropy, d->settings.alpha, d->settings.sigma, #ifdef GREYSTORATION_USING_GFACT d->gfact, #endif d->settings.dl, d->settings.da, d->settings.gaussPrec, d->settings.interp, d->settings.fastApprox, d->settings.tile, d->settings.btile, d->computationThreads); iterationLoop(iter); } } void GreycstorationFilter::resize() { const bool anchor = true; // Anchor original pixels. const unsigned int init = 5; // Initial estimate (1=block, 3=linear, 5=bicubic). int w = m_destImage.width(); int h = m_destImage.height(); d->mask.assign(d->img.dimx(), d->img.dimy(), 1, 1, 255); // cppcheck-suppress knownConditionTrueFalse if (!anchor) { d->mask.resize(w, h, 1, 1, 1); } else { d->mask = !d->mask.resize(w, h, 1, 1, 4); } d->img.resize(w, h, 1, -100, init); for (uint iter = 0 ; runningFlag() && (iter < d->settings.nbIter) ; ++iter) { // This function will start a thread running one iteration of the GREYCstoration filter. // It returns immediately, so you can do what you want after (update a progress bar for // instance). d->threadManager->start(d->img, &d->mask, d->settings.amplitude, d->settings.sharpness, d->settings.anisotropy, d->settings.alpha, d->settings.sigma, #ifdef GREYSTORATION_USING_GFACT d->gfact, #endif d->settings.dl, d->settings.da, d->settings.gaussPrec, d->settings.interp, d->settings.fastApprox, d->settings.tile, d->settings.btile, d->computationThreads); iterationLoop(iter); } } void GreycstorationFilter::simpleResize() { const unsigned int method = 3; // Initial estimate (0, none, 1=block, 3=linear, 4=grid, 5=bicubic). int w = m_destImage.width(); int h = m_destImage.height(); while ((d->img.dimx() > 2 * w) && (d->img.dimy() > 2 * h)) { d->img.resize_halfXY(); } d->img.resize(w, h, -100, -100, method); } void GreycstorationFilter::iterationLoop(uint iter) { uint mp = 0; uint p = 0; while (d->threadManager->isRunning()) { if (!runningFlag()) { d->threadManager->stop(); d->threadManager->wait(); } else { float progress = d->threadManager->waitABit(50); // Update the progress bar in dialog. We simply compute the global // progression index (including all iterations). p = (uint)((iter * 100 + progress) / d->settings.nbIter); if (p > mp) { postProgress(p); mp = p; } } } } FilterAction GreycstorationFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("alpha"), d->settings.alpha); action.addParameter(QLatin1String("amplitude"), d->settings.amplitude); action.addParameter(QLatin1String("anisotropy"), d->settings.anisotropy); action.addParameter(QLatin1String("btile"), d->settings.btile); action.addParameter(QLatin1String("da"), d->settings.da); action.addParameter(QLatin1String("dl"), d->settings.dl); action.addParameter(QLatin1String("fastApprox"), d->settings.fastApprox); action.addParameter(QLatin1String("gaussPrec"), d->settings.gaussPrec); action.addParameter(QLatin1String("interp"), d->settings.interp); action.addParameter(QLatin1String("nbIter"), d->settings.nbIter); action.addParameter(QLatin1String("sharpness"), d->settings.sharpness); action.addParameter(QLatin1String("sigma"), d->settings.sigma); action.addParameter(QLatin1String("tile"), d->settings.tile); return action; } void GreycstorationFilter::readParameters(const FilterAction& action) { d->settings.alpha = action.parameter(QLatin1String("alpha")).toFloat(); d->settings.amplitude = action.parameter(QLatin1String("amplitude")).toFloat(); d->settings.anisotropy = action.parameter(QLatin1String("anisotropy")).toFloat(); d->settings.btile = action.parameter(QLatin1String("btile")).toInt(); d->settings.da = action.parameter(QLatin1String("da")).toFloat(); d->settings.dl = action.parameter(QLatin1String("dl")).toFloat(); d->settings.fastApprox = action.parameter(QLatin1String("fastApprox")).toBool(); d->settings.gaussPrec = action.parameter(QLatin1String("gaussPrec")).toFloat(); d->settings.interp = action.parameter(QLatin1String("interp")).toFloat(); d->settings.nbIter = action.parameter(QLatin1String("nbIter")).toUInt(); d->settings.sharpness = action.parameter(QLatin1String("sharpness")).toFloat(); d->settings.sigma = action.parameter(QLatin1String("sigma")).toFloat(); d->settings.tile = action.parameter(QLatin1String("tile")).toInt(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/greycstoration/greycstorationfilter.h b/core/libs/dimg/filters/greycstoration/greycstorationfilter.h index 63a0b1a3e1..6096fee3cc 100644 --- a/core/libs/dimg/filters/greycstoration/greycstorationfilter.h +++ b/core/libs/dimg/filters/greycstoration/greycstorationfilter.h @@ -1,235 +1,237 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-12-03 * Description : Greycstoration interface. * * Copyright (C) 2007-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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_GREY_CSTORATION_FILTER_H #define DIGIKAM_GREY_CSTORATION_FILTER_H // Qt includes #include // Local includes #include "dimg.h" #include "dimgthreadedfilter.h" #include "digikam_export.h" class QObject; namespace Digikam { class DIGIKAM_EXPORT GreycstorationContainer { public: enum INTERPOLATION { NearestNeighbor = 0, Linear, RungeKutta }; public: explicit GreycstorationContainer() { setRestorationDefaultSettings(); }; ~GreycstorationContainer() { }; void setRestorationDefaultSettings() { fastApprox = true; tile = 256; btile = 4; nbIter = 1; interp = NearestNeighbor; amplitude = 60.0; sharpness = 0.7F; anisotropy = 0.3F; alpha = 0.6F; sigma = 1.1F; gaussPrec = 2.0; dl = 0.8F; da = 30.0; }; void setInpaintingDefaultSettings() { fastApprox = true; tile = 256; btile = 4; nbIter = 30; interp = NearestNeighbor; amplitude = 20.0; sharpness = 0.3F; anisotropy = 1.0; alpha = 0.8F; sigma = 2.0; gaussPrec = 2.0; dl = 0.8F; da = 30.0; }; void setResizeDefaultSettings() { fastApprox = true; tile = 256; btile = 4; nbIter = 3; interp = NearestNeighbor; amplitude = 20.0; sharpness = 0.2F; anisotropy = 0.9F; alpha = 0.1F; sigma = 1.5; gaussPrec = 2.0; dl = 0.8F; da = 30.0; }; public: bool fastApprox; int tile; int btile; uint nbIter; uint interp; float amplitude; float sharpness; float anisotropy; float alpha; float sigma; float gaussPrec; float dl; float da; }; // -------------------------------------------------------------------------- class DIGIKAM_EXPORT GreycstorationFilter : public DImgThreadedFilter { public: enum MODE { Restore = 0, InPainting, Resize, - SimpleResize // Mode to resize image without to use Greycstoration algorithm. + SimpleResize ///< Mode to resize image without to use Greycstoration algorithm. }; public: - /** Contructor without argument. Before to use it, - you need to call in order: setSettings(), setMode(), optionally setInPaintingMask(), - setOriginalImage(), and necessary setup() at end. + /** + * Contructor without argument. Before to use it, + * you need to call in order: setSettings(), setMode(), optionally setInPaintingMask(), + * setOriginalImage(), and necessary setup() at end. */ explicit GreycstorationFilter(QObject* const parent=nullptr); - /** Contructor with all arguments. Ready to use. + /** + * Contructor with all arguments. Ready to use. */ GreycstorationFilter(DImg* const orgImage, const GreycstorationContainer& settings, int mode=Restore, int newWidth=0, int newHeight=0, const QImage& inPaintingMask=QImage(), QObject* const parent=nullptr); ~GreycstorationFilter(); void setMode(int mode, int newWidth=0, int newHeight=0); void setSettings(const GreycstorationContainer& settings); void setInPaintingMask(const QImage& inPaintingMask); void setup(); virtual void cancelFilter() override; static QString cimgVersionString(); static QString FilterIdentifier() { return QLatin1String("digikam:GreycstorationFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: void computeChildrenThreads(); void restoration(); void inpainting(); void resize(); void simpleResize(); void iterationLoop(uint iter); - virtual void initFilter() override; - virtual void filterImage() override; + virtual void initFilter() override; + virtual void filterImage() override; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_GREY_CSTORATION_FILTER_H diff --git a/core/libs/dimg/filters/greycstoration/greycstorationsettings.cpp b/core/libs/dimg/filters/greycstoration/greycstorationsettings.cpp index f90c55f038..169a2d2f14 100644 --- a/core/libs/dimg/filters/greycstoration/greycstorationsettings.cpp +++ b/core/libs/dimg/filters/greycstoration/greycstorationsettings.cpp @@ -1,379 +1,380 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-09-13 * Description : Greycstoration settings widgets * * Copyright (C) 2007-2020 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 "greycstorationsettings.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "dnuminput.h" #include "dcombobox.h" namespace Digikam { class Q_DECL_HIDDEN GreycstorationSettings::Private { public: explicit Private() : alphaLabel(nullptr), amplitudeLabel(nullptr), anisotropyLabel(nullptr), btileLabel(nullptr), daLabel(nullptr), dlLabel(nullptr), gaussianPrecLabel(nullptr), interpolationLabel(nullptr), iterationLabel(nullptr), sharpnessLabel(nullptr), sigmaLabel(nullptr), tileLabel(nullptr), advancedPage(nullptr), generalPage(nullptr), fastApproxCBox(nullptr), parent(nullptr), interpolationBox(nullptr), alphaInput(nullptr), amplitudeInput(nullptr), anisotropyInput(nullptr), daInput(nullptr), dlInput(nullptr), gaussianPrecInput(nullptr), sharpnessInput(nullptr), sigmaInput(nullptr), btileInput(nullptr), iterationInput(nullptr), tileInput(nullptr) { } QLabel* alphaLabel; QLabel* amplitudeLabel; QLabel* anisotropyLabel; QLabel* btileLabel; QLabel* daLabel; QLabel* dlLabel; QLabel* gaussianPrecLabel; QLabel* interpolationLabel; QLabel* iterationLabel; QLabel* sharpnessLabel; QLabel* sigmaLabel; QLabel* tileLabel; QWidget* advancedPage; QWidget* generalPage; QCheckBox* fastApproxCBox; QTabWidget* parent; DComboBox* interpolationBox; DDoubleNumInput* alphaInput; DDoubleNumInput* amplitudeInput; DDoubleNumInput* anisotropyInput; DDoubleNumInput* daInput; DDoubleNumInput* dlInput; DDoubleNumInput* gaussianPrecInput; DDoubleNumInput* sharpnessInput; DDoubleNumInput* sigmaInput; DIntNumInput* btileInput; DIntNumInput* iterationInput; DIntNumInput* tileInput; }; GreycstorationSettings::GreycstorationSettings(QTabWidget* const parent) : QObject(static_cast(parent)), d(new Private) { d->parent = parent; const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); // ------------------------------------------------------------- d->generalPage = new QWidget(parent); QGridLayout* const grid1 = new QGridLayout(d->generalPage); parent->addTab(d->generalPage, i18n("General")); d->sharpnessLabel = new QLabel(i18n("Detail preservation:"), d->generalPage); d->sharpnessInput = new DDoubleNumInput(d->generalPage); d->sharpnessInput->setDecimals(2); d->sharpnessInput->setRange(0.01, 1.0, 0.1); d->sharpnessInput->setWhatsThis(i18n("Preservation of details to set the sharpening level " "of the small features in the target image. " "Higher values leave details sharp.")); d->anisotropyLabel = new QLabel(i18n("Anisotropy:"), d->generalPage); d->anisotropyInput = new DDoubleNumInput(d->generalPage); d->anisotropyInput->setDecimals(2); d->anisotropyInput->setRange(0.0, 1.0, 0.1); d->anisotropyInput->setWhatsThis(i18n("Anisotropic (directional) modifier of the details. " "Keep it small for Gaussian noise.")); d->amplitudeLabel = new QLabel(i18n("Smoothing:"), d->generalPage); d->amplitudeInput = new DDoubleNumInput(d->generalPage); d->amplitudeInput->setDecimals(2); d->amplitudeInput->setRange(0.01, 500.0, 0.1); d->amplitudeInput->setWhatsThis(i18n("Total smoothing power: if the Detail Factor sets the relative " "smoothing and the Anisotropy Factor the direction, " "the Smoothing Factor sets the overall effect.")); d->sigmaLabel = new QLabel(i18n("Regularity:"), d->generalPage); d->sigmaInput = new DDoubleNumInput(d->generalPage); d->sigmaInput->setDecimals(2); d->sigmaInput->setRange(0.0, 10.0, 0.1); d->sigmaInput->setWhatsThis(i18n("This value controls the evenness of smoothing to the image. " "Do not use a high value here, or the " "target image will be completely blurred.")); d->iterationLabel = new QLabel(i18n("Iterations:"), d->generalPage); d->iterationInput = new DIntNumInput(d->generalPage); d->iterationInput->setRange(1, 5000, 1); d->iterationInput->setWhatsThis(i18n("Sets the number of times the filter is applied to the image.")); d->alphaLabel = new QLabel(i18n("Noise:"), d->generalPage); d->alphaInput = new DDoubleNumInput(d->generalPage); d->alphaInput->setDecimals(2); d->alphaInput->setRange(0.01, 1.0, 0.1); d->alphaInput->setWhatsThis(i18n("Sets the noise scale.")); grid1->addWidget(d->sharpnessLabel, 0, 0, 1, 1); grid1->addWidget(d->sharpnessInput, 0, 1, 1, 1); grid1->addWidget(d->anisotropyLabel, 1, 0, 1, 1); grid1->addWidget(d->anisotropyInput, 1, 1, 1, 1); grid1->addWidget(d->amplitudeLabel, 2, 0, 1, 1); grid1->addWidget(d->amplitudeInput, 2, 1, 1, 1); grid1->addWidget(d->sigmaLabel, 3, 0, 1, 1); grid1->addWidget(d->sigmaInput, 3, 1, 1, 1); grid1->addWidget(d->iterationLabel, 4, 0, 1, 1); grid1->addWidget(d->iterationInput, 4, 1, 1, 1); grid1->addWidget(d->alphaLabel, 5, 0, 1, 1); grid1->addWidget(d->alphaInput, 5, 1, 1, 1); grid1->setRowStretch(6, 10); grid1->setContentsMargins(spacing, spacing, spacing, spacing); grid1->setSpacing(spacing); // ------------------------------------------------------------- d->advancedPage = new QWidget(parent); QGridLayout* const grid2 = new QGridLayout(d->advancedPage); parent->addTab(d->advancedPage, i18n("Advanced Settings")); d->daLabel = new QLabel(i18n("Angular step:"), d->advancedPage); d->daInput = new DDoubleNumInput(d->advancedPage); d->daInput->setDecimals(2); d->daInput->setRange(0.0, 90.0, 1.0); d->daInput->setWhatsThis(i18n("Set here the angular integration step (in degrees) " "analogous to anisotropy.")); d->dlLabel = new QLabel(i18n("Integral step:"), d->advancedPage); d->dlInput = new DDoubleNumInput(d->advancedPage); d->dlInput->setDecimals(2); d->dlInput->setRange(0.0, 1.0, 0.1); d->dlInput->setWhatsThis(i18n("Set here the spatial integral step.")); d->gaussianPrecLabel = new QLabel(i18n("Gaussian:"), d->advancedPage); d->gaussianPrecInput = new DDoubleNumInput(d->advancedPage); d->gaussianPrecInput->setDecimals(2); d->gaussianPrecInput->setRange(0.01, 20.0, 0.01); d->gaussianPrecInput->setWhatsThis(i18n("Set here the precision of the Gaussian function.")); d->tileLabel = new QLabel(i18n("Tile size:"), d->advancedPage); d->tileInput = new DIntNumInput(d->advancedPage); d->tileInput->setRange(0, 2000, 1); d->tileInput->setWhatsThis(i18n("Sets the tile size.")); d->btileLabel = new QLabel(i18n("Tile border:"), d->advancedPage); d->btileInput = new DIntNumInput(d->advancedPage); d->btileInput->setRange(1, 20, 1); d->btileInput->setWhatsThis(i18n("Sets the size of each tile border.")); d->interpolationLabel = new QLabel(i18n("Interpolation:"), d->advancedPage); d->interpolationBox = new DComboBox(d->advancedPage); d->interpolationBox->insertItem(GreycstorationContainer::NearestNeighbor, i18n("Nearest Neighbor")); d->interpolationBox->insertItem(GreycstorationContainer::Linear, i18n("Linear")); d->interpolationBox->insertItem(GreycstorationContainer::RungeKutta, i18n("Runge-Kutta")); d->interpolationBox->setWhatsThis(i18n("Select the right interpolation method for the " "desired image quality.")); d->fastApproxCBox = new QCheckBox(i18n("Fast approximation"), d->advancedPage); d->fastApproxCBox->setWhatsThis(i18n("Enable fast approximation when rendering images.")); grid2->addWidget(d->daLabel, 0, 0, 1, 1); grid2->addWidget(d->daInput, 0, 1, 1, 1); grid2->addWidget(d->dlLabel, 1, 0, 1, 1); grid2->addWidget(d->dlInput, 1, 1, 1, 1); grid2->addWidget(d->gaussianPrecLabel, 2, 0, 1, 1); grid2->addWidget(d->gaussianPrecInput, 2, 1, 1, 1); grid2->addWidget(d->tileLabel, 3, 0, 1, 1); grid2->addWidget(d->tileInput, 3, 1, 1, 1); grid2->addWidget(d->btileLabel, 4, 0, 1, 1); grid2->addWidget(d->btileInput, 4, 1, 1, 1); grid2->addWidget(d->interpolationLabel, 5, 0, 1, 1); grid2->addWidget(d->interpolationBox, 5, 1, 1, 1); grid2->addWidget(d->fastApproxCBox, 6, 0, 1, 2); grid2->setContentsMargins(spacing, spacing, spacing, spacing); grid2->setSpacing(spacing); } GreycstorationSettings::~GreycstorationSettings() { delete d; } void GreycstorationSettings::setEnabled(bool b) { d->generalPage->setEnabled(b); d->advancedPage->setEnabled(b); d->parent->setTabEnabled(d->parent->indexOf(d->generalPage), b); d->parent->setTabEnabled(d->parent->indexOf(d->advancedPage), b); } void GreycstorationSettings::setSettings(const GreycstorationContainer& settings) { blockSignals(true); d->alphaInput->setValue(settings.alpha); d->amplitudeInput->setValue(settings.amplitude); d->anisotropyInput->setValue(settings.anisotropy); d->btileInput->setValue(settings.btile); d->daInput->setValue(settings.da); d->dlInput->setValue(settings.dl); d->fastApproxCBox->setChecked(settings.fastApprox); d->gaussianPrecInput->setValue(settings.gaussPrec); d->interpolationBox->setCurrentIndex(settings.interp); d->iterationInput->setValue(settings.nbIter); d->sharpnessInput->setValue(settings.sharpness); d->sigmaInput->setValue(settings.sigma); d->tileInput->setValue(settings.tile); blockSignals(false); } void GreycstorationSettings::setDefaultSettings(const GreycstorationContainer& settings) { blockSignals(true); d->alphaInput->setDefaultValue(settings.alpha); d->amplitudeInput->setDefaultValue(settings.amplitude); d->anisotropyInput->setDefaultValue(settings.anisotropy); d->btileInput->setDefaultValue(settings.btile); d->daInput->setDefaultValue(settings.da); d->dlInput->setDefaultValue(settings.dl); d->fastApproxCBox->setChecked(settings.fastApprox); d->gaussianPrecInput->setDefaultValue(settings.gaussPrec); d->interpolationBox->setDefaultIndex(settings.interp); d->iterationInput->setDefaultValue(settings.nbIter); d->sharpnessInput->setDefaultValue(settings.sharpness); d->sigmaInput->setDefaultValue(settings.sigma); d->tileInput->setDefaultValue(settings.tile); blockSignals(false); } GreycstorationContainer GreycstorationSettings::settings() const { GreycstorationContainer settings; settings.fastApprox = d->fastApproxCBox->isChecked(); settings.interp = d->interpolationBox->currentIndex(); settings.amplitude = d->amplitudeInput->value(); settings.sharpness = d->sharpnessInput->value(); settings.anisotropy = d->anisotropyInput->value(); settings.alpha = d->alphaInput->value(); settings.sigma = d->sigmaInput->value(); settings.gaussPrec = d->gaussianPrecInput->value(); settings.dl = d->dlInput->value(); settings.da = d->daInput->value(); settings.nbIter = d->iterationInput->value(); settings.tile = d->tileInput->value(); settings.btile = d->btileInput->value(); return settings; } bool GreycstorationSettings::loadSettings(QFile& file, const QString& header) { QTextStream stream(&file); if (stream.readLine() != header) { return false; } blockSignals(true); GreycstorationContainer prm; prm.fastApprox = stream.readLine().toInt(); prm.interp = stream.readLine().toInt(); prm.amplitude = stream.readLine().toDouble(); prm.sharpness = stream.readLine().toDouble(); prm.anisotropy = stream.readLine().toDouble(); prm.alpha = stream.readLine().toDouble(); prm.sigma = stream.readLine().toDouble(); prm.gaussPrec = stream.readLine().toDouble(); prm.dl = stream.readLine().toDouble(); prm.da = stream.readLine().toDouble(); prm.nbIter = stream.readLine().toInt(); prm.tile = stream.readLine().toInt(); prm.btile = stream.readLine().toInt(); setSettings(prm); blockSignals(false); + return true; } void GreycstorationSettings::saveSettings(QFile& file, const QString& header) { GreycstorationContainer prm = settings(); QTextStream stream(&file); stream << header << "\n"; stream << prm.fastApprox << "\n"; stream << prm.interp << "\n"; stream << prm.amplitude << "\n"; stream << prm.sharpness << "\n"; stream << prm.anisotropy << "\n"; stream << prm.alpha << "\n"; stream << prm.sigma << "\n"; stream << prm.gaussPrec << "\n"; stream << prm.dl << "\n"; stream << prm.da << "\n"; stream << prm.nbIter << "\n"; stream << prm.tile << "\n"; stream << prm.btile << "\n"; } } // namespace Digikam diff --git a/core/libs/dimg/filters/hsl/hslfilter.cpp b/core/libs/dimg/filters/hsl/hslfilter.cpp index 100b10e089..62203ce501 100644 --- a/core/libs/dimg/filters/hsl/hslfilter.cpp +++ b/core/libs/dimg/filters/hsl/hslfilter.cpp @@ -1,335 +1,339 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-03-06 * Description : Hue/Saturation/Lightness image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Julien Narboux * Copyright (C) 2010 by Martin Klapetek * * 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 "hslfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" namespace Digikam { class Q_DECL_HIDDEN HSLFilter::Private { public: explicit Private() { memset(&htransfer, 0, sizeof(htransfer)); memset(<ransfer, 0, sizeof(ltransfer)); memset(&stransfer, 0, sizeof(stransfer)); memset(&htransfer16, 0, sizeof(htransfer16)); memset(<ransfer16, 0, sizeof(ltransfer16)); memset(&stransfer16, 0, sizeof(stransfer16)); } int htransfer[256]; int ltransfer[256]; int stransfer[256]; int htransfer16[65536]; int ltransfer16[65536]; int stransfer16[65536]; HSLContainer settings; }; HSLFilter::HSLFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { reset(); initFilter(); } HSLFilter::HSLFilter(DImg* const orgImage, QObject* const parent, const HSLContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("HSLFilter")), d(new Private) { d->settings = settings; reset(); initFilter(); } HSLFilter::~HSLFilter() { cancelFilter(); delete d; } QString HSLFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Hue / Saturation / Lightness Filter")); } void HSLFilter::filterImage() { setHue(d->settings.hue); setSaturation(d->settings.saturation); setLightness(d->settings.lightness); applyHSL(m_orgImage); m_destImage = m_orgImage; } void HSLFilter::reset() { // initialize to linear mapping - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->htransfer16[i] = i; d->ltransfer16[i] = i; d->stransfer16[i] = i; } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->htransfer[i] = i; d->ltransfer[i] = i; d->stransfer[i] = i; } } void HSLFilter::setHue(double val) { int value; - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { value = lround(val * 65535.0 / 360.0); - if ((i + value) < 0) + if ((i + value) < 0) { d->htransfer16[i] = 65535 + (i + value); } else if ((i + value) > 65535) { d->htransfer16[i] = i + value - 65535; } else { d->htransfer16[i] = i + value; } } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { value = lround(val * 255.0 / 360.0); - if ((i + value) < 0) + if ((i + value) < 0) { d->htransfer[i] = 255 + (i + value); } else if ((i + value) > 255) { d->htransfer[i] = i + value - 255; } else { d->htransfer[i] = i + value; } } } void HSLFilter::setSaturation(double val) { val = CLAMP(val, -100.0, 100.0); int value; - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { - value = lround((i * (100.0 + val)) / 100.0); + value = lround((i * (100.0 + val)) / 100.0); d->stransfer16[i] = CLAMP065535(value); } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { - value = lround((i * (100.0 + val)) / 100.0); + value = lround((i * (100.0 + val)) / 100.0); d->stransfer[i] = CLAMP0255(value); } } void HSLFilter::setLightness(double val) { // val needs to be in that range so that the result is in the range 0..65535 val = CLAMP(val, -100.0, 100.0); if (val < 0) { - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->ltransfer16[i] = lround((i * (val + 100.0)) / 100.0); } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->ltransfer[i] = lround((i * (val + 100.0)) / 100.0); } } else { - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->ltransfer16[i] = lround(i * (1.0 - val / 100.0) + 65535.0 / 100.0 * val); } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->ltransfer[i] = lround(i * (1.0 - val / 100.0) + 255.0 / 100.0 * val); } } } int HSLFilter::vibranceBias(double sat, double hue, double vib, bool sixteenbit) { double ratio; int localsat; double normalized_hue = hue / (sixteenbit ? 65535.0 : 255.0); - if (normalized_hue > 0.85 || normalized_hue < 0.2) + if ((normalized_hue > 0.85) || (normalized_hue < 0.2)) { ratio = 0.3; } else { ratio = 1.0; } localsat = lround((sat * (100.0 + vib * ratio)) / 100.0); if (sixteenbit) { - return(CLAMP065535(localsat)); + return (CLAMP065535(localsat)); } else { - return(CLAMP0255(localsat)); + return (CLAMP0255(localsat)); } } void HSLFilter::applyHSL(DImg& image) { if (image.isNull()) { return; } bool sixteenBit = image.sixteenBit(); uint numberOfPixels = image.numPixels(); int progress; int hue, sat, lig; double vib = d->settings.vibrance; DColor color; if (sixteenBit) // 16 bits image. { unsigned short* data = reinterpret_cast(image.bits()); - for (uint i = 0; runningFlag() && (i < numberOfPixels); ++i) + for (uint i = 0 ; runningFlag() && (i < numberOfPixels) ; ++i) { color = DColor(data[2], data[1], data[0], 0, sixteenBit); // convert RGB to HSL + color.getHSL(&hue, &sat, &lig); // convert HSL to RGB + color.setHSL(d->htransfer16[hue], vibranceBias(d->stransfer16[sat], hue, vib, sixteenBit), d->ltransfer16[lig], sixteenBit); data[2] = color.red(); data[1] = color.green(); data[0] = color.blue(); - data += 4; + data += 4; progress = (int)(((double)i * 100.0) / numberOfPixels); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 8 bits image. { uchar* data = image.bits(); - for (uint i = 0; runningFlag() && (i < numberOfPixels); ++i) + for (uint i = 0 ; runningFlag() && (i < numberOfPixels) ; ++i) { color = DColor(data[2], data[1], data[0], 0, sixteenBit); // convert RGB to HSL + color.getHSL(&hue, &sat, &lig); // convert HSL to RGB + color.setHSL(d->htransfer[hue], vibranceBias(d->stransfer[sat], hue, vib, sixteenBit), d->ltransfer[lig], sixteenBit); data[2] = color.red(); data[1] = color.green(); data[0] = color.blue(); - data += 4; + data += 4; progress = (int)(((double)i * 100.0) / numberOfPixels); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } FilterAction HSLFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("hue"), d->settings.hue); action.addParameter(QLatin1String("lightness"), d->settings.lightness); action.addParameter(QLatin1String("saturation"), d->settings.saturation); action.addParameter(QLatin1String("vibrance"), d->settings.vibrance); return action; } void HSLFilter::readParameters(const Digikam::FilterAction& action) { d->settings.hue = action.parameter(QLatin1String("hue")).toDouble(); d->settings.lightness = action.parameter(QLatin1String("lightness")).toDouble(); d->settings.saturation = action.parameter(QLatin1String("saturation")).toDouble(); d->settings.vibrance = action.parameter(QLatin1String("vibrance")).toDouble(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/hsl/hslfilter.h b/core/libs/dimg/filters/hsl/hslfilter.h index a5f44e1d72..e78b8c3477 100644 --- a/core/libs/dimg/filters/hsl/hslfilter.h +++ b/core/libs/dimg/filters/hsl/hslfilter.h @@ -1,119 +1,119 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-03-06 * Description : Hue/Saturation/Lightness image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * 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_HSL_FILTER_H #define DIGIKAM_HSL_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT HSLContainer { public: explicit HSLContainer() + : hue(0.0), + saturation(0.0), + vibrance(0.0), + lightness(0.0) { - hue = 0.0; - saturation = 0.0; - vibrance = 0.0; - lightness = 0.0; }; ~HSLContainer() { }; public: double hue; double saturation; double vibrance; double lightness; }; // ----------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT HSLFilter : public DImgThreadedFilter { public: explicit HSLFilter(QObject* const parent = nullptr); explicit HSLFilter(DImg* const orgImage, QObject* const parent=nullptr, const HSLContainer& settings=HSLContainer()); virtual ~HSLFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:HSLFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; void reset(); void setHue(double val); void setSaturation(double val); void setLightness(double val); void applyHSL(DImg& image); int vibranceBias(double sat, double hue, double vib, bool sixteenbit); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_HSL_FILTER_H diff --git a/core/libs/dimg/filters/hsl/hslsettings.cpp b/core/libs/dimg/filters/hsl/hslsettings.cpp index 07f70a7056..9bbd7305a8 100644 --- a/core/libs/dimg/filters/hsl/hslsettings.cpp +++ b/core/libs/dimg/filters/hsl/hslsettings.cpp @@ -1,292 +1,292 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-11 * Description : HSL settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * Copyright (C) 2010 by Julien Narboux * * 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 "hslsettings.h" // Qt includes #include #include #include #include #include // KDE includes #include #include // Local includes #include "dnuminput.h" #include "digikam_debug.h" #include "colorgradientwidget.h" #include "hspreviewwidget.h" #include "dhuesaturationselect.h" namespace Digikam { class Q_DECL_HIDDEN HSLSettings::Private { public: explicit Private() : HSSelector(nullptr), hInput(nullptr), sInput(nullptr), vInput(nullptr), lInput(nullptr), HSPreview(nullptr) { } static const QString configHueAdjustmentEntry; static const QString configSaturationAdjustmentEntry; static const QString configVibranceAdjustmentEntry; static const QString configLighnessAdjustmentEntry; DHueSaturationSelector* HSSelector; DDoubleNumInput* hInput; DDoubleNumInput* sInput; DDoubleNumInput* vInput; DDoubleNumInput* lInput; HSPreviewWidget* HSPreview; }; const QString HSLSettings::Private::configHueAdjustmentEntry(QLatin1String("HueAdjustment")); const QString HSLSettings::Private::configSaturationAdjustmentEntry(QLatin1String("SaturationAdjustment")); const QString HSLSettings::Private::configVibranceAdjustmentEntry(QLatin1String("VibranceAdjustment")); const QString HSLSettings::Private::configLighnessAdjustmentEntry(QLatin1String("LighnessAdjustment")); // -------------------------------------------------------- HSLSettings::HSLSettings(QWidget* const parent) : QWidget(parent), d(new Private) { const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(parent); d->HSSelector = new DHueSaturationSelector(); d->HSSelector->setWhatsThis(i18n("Select the hue and saturation adjustments of the image.")); d->HSSelector->setMinimumSize(256, 142); d->HSPreview = new HSPreviewWidget(); d->HSPreview->setWhatsThis(i18n("You can see here a color preview of the hue and " "saturation adjustments.")); d->HSPreview->setMinimumSize(256, 15); QLabel* const label2 = new QLabel(i18n("Hue:")); d->hInput = new DDoubleNumInput(); d->hInput->setDecimals(0); d->hInput->setRange(-180.0, 180.0, 1.0); d->hInput->setDefaultValue(0.0); d->hInput->setWhatsThis(i18n("Set here the hue adjustment of the image.")); QLabel* const label3 = new QLabel(i18n("Saturation:")); d->sInput = new DDoubleNumInput(); d->sInput->setDecimals(2); d->sInput->setRange(-100.0, 100.0, 0.01); d->sInput->setDefaultValue(0.0); d->sInput->setWhatsThis(i18n("Set here the saturation adjustment of the image.")); QLabel* const label4 = new QLabel(i18n("Vibrance:")); d->vInput = new DDoubleNumInput(); d->vInput->setDecimals(2); d->vInput->setRange(-100.0, 100.0, 0.01); d->vInput->setDefaultValue(0.0); d->vInput->setWhatsThis(i18n("Set here the vibrance adjustment of the image." "Vibrance performs selective saturation on less saturated colors and avoiding skin tones.")); QLabel* const label5 = new QLabel(i18n("Lightness:")); d->lInput = new DDoubleNumInput(); d->lInput->setDecimals(2); d->lInput->setRange(-100.0, 100.0, 0.01); d->lInput->setDefaultValue(0.0); d->lInput->setWhatsThis(i18n("Set here the lightness adjustment of the image.")); // ------------------------------------------------------------- grid->addWidget(d->HSSelector, 0, 0, 1, 5); grid->addWidget(d->HSPreview, 1, 0, 1, 5); grid->addWidget(label2, 2, 0, 1, 1); grid->addWidget(d->hInput, 2, 1, 1, 4); grid->addWidget(label3, 3, 0, 1, 1); grid->addWidget(d->sInput, 3, 1, 1, 4); grid->addWidget(label4, 4, 0, 1, 1); grid->addWidget(d->vInput, 4, 1, 1, 4); grid->addWidget(label5, 5, 0, 1, 1); grid->addWidget(d->lInput, 5, 1, 1, 4); grid->setRowStretch(6, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->HSSelector, SIGNAL(valueChanged(int,int)), this, SLOT(slotHSChanged(int,int))); connect(d->hInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->hInput, SIGNAL(valueChanged(double)), this, SLOT(slotHChanged(double))); connect(d->sInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->vInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); connect(d->lInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); } HSLSettings::~HSLSettings() { delete d; } void HSLSettings::slotHSChanged(int h, int s) { double hue = (double)(h); - if (h >= 180 && h <= 359) + if ((h >= 180) && (h <= 359)) { hue = (double)(h) - 359.0; } double sat = ((double)s * (200.0 / 255.0)) - 100.0; d->hInput->blockSignals(true); d->sInput->blockSignals(true); d->hInput->setValue(hue); d->sInput->setValue(sat); d->HSPreview->setHS(hue, sat); d->hInput->blockSignals(false); d->sInput->blockSignals(false); emit signalSettingsChanged(); } void HSLSettings::slotHChanged(double h) { int hue = (int)(h); - if (h >= -180 && h < 0) + if ((h >= -180) && (h < 0)) { hue = (int)(h) + 359; } d->HSSelector->blockSignals(true); d->HSSelector->setXValue(hue); d->HSSelector->blockSignals(false); d->HSPreview->setHS(settings().hue, settings().saturation); } void HSLSettings::slotSChanged(double s) { int sat = (int)((s + 100.0) * (255.0 / 200.0)); d->HSSelector->blockSignals(true); d->HSSelector->setYValue(sat); d->HSSelector->blockSignals(false); d->HSPreview->setHS(settings().hue, settings().saturation); } HSLContainer HSLSettings::settings() const { HSLContainer prm; prm.hue = d->hInput->value(); prm.saturation = d->sInput->value(); prm.vibrance = d->vInput->value(); prm.lightness = d->lInput->value(); return prm; } void HSLSettings::setSettings(const HSLContainer& settings) { blockSignals(true); d->hInput->setValue(settings.hue); d->sInput->setValue(settings.saturation); d->vInput->setValue(settings.vibrance); d->lInput->setValue(settings.lightness); slotHChanged(settings.hue); slotSChanged(settings.saturation); blockSignals(false); } void HSLSettings::resetToDefault() { blockSignals(true); d->hInput->slotReset(); d->sInput->slotReset(); d->vInput->slotReset(); d->lInput->slotReset(); blockSignals(false); } HSLContainer HSLSettings::defaultSettings() const { HSLContainer prm; prm.hue = d->hInput->defaultValue(); prm.saturation = d->sInput->defaultValue(); prm.vibrance = d->vInput->defaultValue(); prm.lightness = d->lInput->defaultValue(); return prm; } void HSLSettings::readSettings(KConfigGroup& group) { HSLContainer prm; HSLContainer defaultPrm = defaultSettings(); prm.hue = group.readEntry(d->configHueAdjustmentEntry, defaultPrm.hue); prm.saturation = group.readEntry(d->configSaturationAdjustmentEntry, defaultPrm.saturation); prm.vibrance = group.readEntry(d->configVibranceAdjustmentEntry, defaultPrm.vibrance); prm.lightness = group.readEntry(d->configLighnessAdjustmentEntry, defaultPrm.lightness); setSettings(prm); } void HSLSettings::writeSettings(KConfigGroup& group) { HSLContainer prm = settings(); group.writeEntry(d->configHueAdjustmentEntry, prm.hue); group.writeEntry(d->configSaturationAdjustmentEntry, prm.saturation); group.writeEntry(d->configVibranceAdjustmentEntry, prm.vibrance); group.writeEntry(d->configLighnessAdjustmentEntry, prm.lightness); } } // namespace Digikam diff --git a/core/libs/dimg/filters/hsl/hslsettings.h b/core/libs/dimg/filters/hsl/hslsettings.h index 46125259b0..68c7ae73b2 100644 --- a/core/libs/dimg/filters/hsl/hslsettings.h +++ b/core/libs/dimg/filters/hsl/hslsettings.h @@ -1,77 +1,77 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-11 * Description : HSL settings view. * * Copyright (C) 2010-2020 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. * * ============================================================ */ #ifndef DIGIKAM_HSL_SETTINGS_H #define DIGIKAM_HSL_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "hslfilter.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT HSLSettings : public QWidget { Q_OBJECT public: explicit HSLSettings(QWidget* const parent); ~HSLSettings(); - HSLContainer defaultSettings() const; + HSLContainer defaultSettings() const; void resetToDefault(); - HSLContainer settings() const; + HSLContainer settings() const; void setSettings(const HSLContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); Q_SIGNALS: void signalSettingsChanged(); private Q_SLOTS: void slotHSChanged(int h, int s); void slotHChanged(double h); void slotSChanged(double s); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_HSL_SETTINGS_H diff --git a/core/libs/dimg/filters/hsl/hspreviewwidget.h b/core/libs/dimg/filters/hsl/hspreviewwidget.h index e4e5524c76..db8fc43efe 100644 --- a/core/libs/dimg/filters/hsl/hspreviewwidget.h +++ b/core/libs/dimg/filters/hsl/hspreviewwidget.h @@ -1,68 +1,68 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-01-08 * Description : Hue/Saturation preview widget * * Copyright (C) 2007-2020 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. * * ============================================================ */ #ifndef DIGIKAM_HS_PREVIEW_WIDGET_H #define DIGIKAM_HS_PREVIEW_WIDGET_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT HSPreviewWidget : public QWidget { Q_OBJECT public: explicit HSPreviewWidget(QWidget* const parent=nullptr); ~HSPreviewWidget(); void setHS(double hue, double sat); protected: void resizeEvent(QResizeEvent*) override; - void paintEvent(QPaintEvent*) override; + void paintEvent(QPaintEvent*) override; private: void updatePixmap(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_HS_PREVIEW_WIDGET_H diff --git a/core/libs/dimg/filters/icc/digikam-lcms.cpp b/core/libs/dimg/filters/icc/digikam-lcms.cpp index 88b0fc9c79..3cb1eca3f7 100644 --- a/core/libs/dimg/filters/icc/digikam-lcms.cpp +++ b/core/libs/dimg/filters/icc/digikam-lcms.cpp @@ -1,655 +1,701 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-03 * Description : LCMS2 wrapper * * Copyright (C) 2012 by Francesco Riosa * Copyright (C) 2012-2020 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-lcms.h" // Lcms includes // Turn off the specific compiler warnings with LCMS header. #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wundef" # pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif // Local includes #include "digikam_debug.h" /////////////////////////////////////////////////////////////////////// void _l2tol1MAT3(MAT3* const l2, MAT3* const l1) { // TODO: this seem plain wrong and don't provide perfect result + l1->Red.X = static_cast( l2->Red.X ); l1->Red.Y = static_cast( l2->Green.X ); l1->Red.Z = static_cast( l2->Blue.X ); l1->Green.X = static_cast( l2->Red.Y ); l1->Green.Y = static_cast( l2->Green.Y ); l1->Green.Z = static_cast( l2->Blue.Y ); l1->Blue.X = static_cast( l2->Red.Z ); l1->Blue.Y = static_cast( l2->Green.Z ); l1->Blue.Z = static_cast( l2->Blue.Z ); } void _l1LPMAT3tol2cmsMAT3(LPMAT3 l1, cmsMAT3* const l2) { l2->v[0].n[0] = static_cast( l1->Red.X ); l2->v[0].n[1] = static_cast( l1->Red.Y ); l2->v[0].n[2] = static_cast( l1->Red.Z ); l2->v[1].n[0] = static_cast( l1->Green.X ); l2->v[1].n[1] = static_cast( l1->Green.Y ); l2->v[1].n[2] = static_cast( l1->Green.Z ); l2->v[2].n[0] = static_cast( l1->Blue.X ); l2->v[2].n[1] = static_cast( l1->Blue.Y ); l2->v[2].n[2] = static_cast( l1->Blue.Z ); } void _l2cmsMAT3tol1LPMAT3(cmsMAT3* const l2, LPMAT3 l1) { l1->Red.X = static_cast( l2->v[0].n[0] ); l1->Red.Y = static_cast( l2->v[0].n[1] ); l1->Red.Z = static_cast( l2->v[0].n[2] ); l1->Green.X = static_cast( l2->v[1].n[0] ); l1->Green.Y = static_cast( l2->v[1].n[1] ); l1->Green.Z = static_cast( l2->v[1].n[2] ); l1->Blue.X = static_cast( l2->v[2].n[0] ); l1->Blue.Y = static_cast( l2->v[2].n[1] ); l1->Blue.Z = static_cast( l2->v[2].n[2] ); } /////////////////////////////////////////////////////////////////////// #define MATRIX_DET_TOLERANCE 0.0001 -/// Compute chromatic adaptation matrix using Chad as cone matrix +/** + * Compute chromatic adaptation matrix using Chad as cone matrix + */ static cmsBool ComputeChromaticAdaptation(cmsMAT3* const Conversion, const cmsCIEXYZ* const SourceWhitePoint, const cmsCIEXYZ* const DestWhitePoint, const cmsMAT3* const Chad) { cmsMAT3 Chad_Inv; cmsVEC3 ConeSourceXYZ, ConeSourceRGB; cmsVEC3 ConeDestXYZ, ConeDestRGB; cmsMAT3 Cone, Tmp; Tmp = *Chad; if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) + { return FALSE; + } _cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, SourceWhitePoint -> Y, SourceWhitePoint -> Z); _cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X, DestWhitePoint -> Y, DestWhitePoint -> Z); _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); _cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); // Build matrix _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0 ); _cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0 ); _cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); // Normalize _cmsMAT3per(&Tmp, &Cone, Chad); _cmsMAT3per(Conversion, &Chad_Inv, &Tmp); return TRUE; } -/** Returns the final chromatic adaptation from illuminant FromIll to Illuminant ToIll - * The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed +/** + * Returns the final chromatic adaptation from illuminant FromIll to Illuminant ToIll + * The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed */ cmsBool _cmsAdaptationMatrix(cmsMAT3* const r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* const FromIll, const cmsCIEXYZ* const ToIll) { // Bradford matrix + cmsMAT3 LamRigg = {{ {{ 0.8951, 0.2664, -0.1614 }}, {{ -0.7502, 1.7135, 0.0367 }}, {{ 0.0389, -0.0685, 1.0296 }} }}; if (ConeMatrix == nullptr) + { ConeMatrix = &LamRigg; + } return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix); } -/// Same as anterior, but assuming D50 destination. White point is given in xyY +/** + * Same as anterior, but assuming D50 destination. White point is given in xyY + */ static cmsBool _cmsAdaptMatrixToD50(cmsMAT3* const r, const cmsCIExyY* const SourceWhitePt) { cmsCIEXYZ Dn; cmsMAT3 Bradford; cmsMAT3 Tmp; cmsxyY2XYZ(&Dn, SourceWhitePt); if (!_cmsAdaptationMatrix(&Bradford, nullptr, &Dn, cmsD50_XYZ())) + { return FALSE; + } Tmp = *r; _cmsMAT3per(r, &Bradford, &Tmp); return TRUE; } -/** Build a White point, primary chromas transfer matrix from RGB to CIE XYZ - This is just an approximation, I am not handling all the non-linear - aspects of the RGB to XYZ process, and assuming that the gamma correction - has transitive property in the transformation chain. - - the algorithm: - - - First I build the absolute conversion matrix using - primaries in XYZ. This matrix is next inverted - - Then I evaluate the source white point across this matrix - obtaining the coefficients of the transformation - - Then, I apply these coefficients to the original matrix -*/ +/** + * Build a White point, primary chromas transfer matrix from RGB to CIE XYZ + * This is just an approximation, I am not handling all the non-linear + * aspects of the RGB to XYZ process, and assuming that the gamma correction + * has transitive property in the transformation chain. + * + * the algorithm: + * + * - First I build the absolute conversion matrix using + * primaries in XYZ. This matrix is next inverted + * - Then I evaluate the source white point across this matrix + * obtaining the coefficients of the transformation + * - Then, I apply these coefficients to the original matrix + */ cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* const r, const cmsCIExyY* const WhitePt, const cmsCIExyYTRIPLE* const Primrs) { cmsVEC3 WhitePoint, Coef; cmsMAT3 Result, Primaries; cmsFloat64Number xn, yn; cmsFloat64Number xr, yr; cmsFloat64Number xg, yg; cmsFloat64Number xb, yb; xn = WhitePt -> x; yn = WhitePt -> y; xr = Primrs -> Red.x; yr = Primrs -> Red.y; xg = Primrs -> Green.x; yg = Primrs -> Green.y; xb = Primrs -> Blue.x; yb = Primrs -> Blue.y; // Build Primaries matrix + _cmsVEC3init(&Primaries.v[0], xr, xg, xb ); _cmsVEC3init(&Primaries.v[1], yr, yg, yb ); _cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); // Result = Primaries ^ (-1) inverse matrix + if (!_cmsMAT3inverse(&Primaries, &Result)) + { return FALSE; + } _cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); - // Across inverse primaries ... + // Across inverse primaries + _cmsMAT3eval(&Coef, &Result, &WhitePoint); // Give us the Coefs, then I build transformation matrix + _cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb ); _cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb ); _cmsVEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); return _cmsAdaptMatrixToD50(r, WhitePt); } /////////////////////////////////////////////////////////////////////// -/// WAS: Same as anterior, but assuming D50 source. White point is given in xyY +/** + * Same as anterior, but assuming D50 source. White point is given in xyY + */ static cmsBool cmsAdaptMatrixFromD50(cmsMAT3* const r, const cmsCIExyY* const DestWhitePt) { cmsCIEXYZ Dn; cmsMAT3 Bradford; cmsMAT3 Tmp; cmsxyY2XYZ(&Dn, DestWhitePt); if (!_cmsAdaptationMatrix(&Bradford, nullptr, &Dn, cmsD50_XYZ())) + { return FALSE; + } Tmp = *r; _cmsMAT3per(r, &Bradford, &Tmp); return TRUE; } //////////////////////////////////////////////////// int dkCmsErrorAction(int nAction) { - Q_UNUSED(nAction); + qCWarning(DIGIKAM_DIMG_LOG) << "Error while running Lcms action (" << nAction << ")"; - // TODO: Where is error logging? return 0; } DWORD dkCmsGetProfileICCversion(cmsHPROFILE hProfile) { return (DWORD) cmsGetEncodedICCversion(hProfile); } void dkCmsSetAlarmCodes(int r, int g, int b) { cmsUInt16Number NewAlarm[cmsMAXCHANNELS]; NewAlarm[0] = (cmsUInt16Number)r * 256; NewAlarm[1] = (cmsUInt16Number)g * 256; NewAlarm[2] = (cmsUInt16Number)b * 256; cmsSetAlarmCodes(NewAlarm); } QString dkCmsTakeProductName(cmsHPROFILE hProfile) { static char Name[1024*2+4]; char Manufacturer[1024], Model[1024]; Name[0] = '\0'; Manufacturer[0] = Model[0] = '\0'; cmsMLU* mlu = nullptr; if (cmsIsTag(hProfile, cmsSigDeviceMfgDescTag)) { mlu = static_cast(cmsReadTag(hProfile, cmsSigDeviceMfgDescTag)); cmsMLUgetASCII(mlu, "en", "US", Manufacturer, 1024); } if (cmsIsTag(hProfile, cmsSigDeviceModelDescTag)) { mlu = static_cast(cmsReadTag(hProfile, cmsSigDeviceModelDescTag)); cmsMLUgetASCII(mlu, "en", "US", Model, 1024); } if (!Manufacturer[0] && !Model[0]) { if (cmsIsTag(hProfile, cmsSigProfileDescriptionTag)) { mlu = static_cast(cmsReadTag(hProfile, cmsSigProfileDescriptionTag)); cmsMLUgetASCII(mlu, "en", "US", Name, 1024); return QLatin1String(Name); } else { return QLatin1String("{no name}"); } } - if (!Manufacturer[0] || strncmp(Model, Manufacturer, 8) == 0 || strlen(Model) > 30) + if (!Manufacturer[0] || (strncmp(Model, Manufacturer, 8) == 0) || (strlen(Model) > 30)) { strcpy(Name, Model); } else { sprintf(Name, "%s - %s", Model, Manufacturer); } return QLatin1String(Name); } QString dkCmsTakeProductDesc(cmsHPROFILE hProfile) { static char Name[2048]; if (cmsIsTag(hProfile, cmsSigProfileDescriptionTag)) { cmsMLU* const mlu = static_cast(cmsReadTag(hProfile, cmsSigProfileDescriptionTag)); cmsMLUgetASCII(mlu, "en", "US", Name, 1024); } else { return dkCmsTakeProductName(hProfile); } if (strncmp(Name, "Copyrig", 7) == 0) { return dkCmsTakeProductName(hProfile); } return QLatin1String(Name); } QString dkCmsTakeProductInfo(cmsHPROFILE hProfile) { static char Info[4096]; cmsMLU* mlu = nullptr; Info[0] = '\0'; if (cmsIsTag(hProfile, cmsSigProfileDescriptionTag)) { char Desc[1024]; mlu = static_cast(cmsReadTag(hProfile, cmsSigProfileDescriptionTag)); cmsMLUgetASCII(mlu, "en", "US", Desc, 1024); strcat(Info, Desc); } if (cmsIsTag(hProfile, cmsSigCopyrightTag)) { char Copyright[1024]; mlu = static_cast(cmsReadTag(hProfile, cmsSigCopyrightTag)); cmsMLUgetASCII(mlu, "en", "US", Copyright, 1024); strcat(Info, " - "); strcat(Info, Copyright); } #define K007 static_cast( 0x4B303037 ) if (cmsIsTag(hProfile, K007)) { char MonCal[1024]; mlu = static_cast(cmsReadTag(hProfile, K007)); cmsMLUgetASCII(mlu, "en", "US", MonCal, 1024); strcat(Info, " - "); strcat(Info, MonCal); } else { - /* - * _cmsIdentifyWhitePoint is complex and partly redundant - * with cietonguewidget, leave this part off - * until the full lcms2 implementation - * +/* + // _cmsIdentifyWhitePoint is complex and partly redundant + // with cietonguewidget, leave this part off + // until the full lcms2 implementation + cmsCIEXYZ WhitePt; char WhiteStr[1024]; dkCmsTakeMediaWhitePoint(&WhitePt, hProfile); _cmsIdentifyWhitePoint(WhiteStr, &WhitePt); strcat(Info, " - "); strcat(Info, WhiteStr); - */ +*/ } #undef K007 return QLatin1String(Info); } QString dkCmsTakeManufacturer(cmsHPROFILE hProfile) { char buffer[1024]; buffer[0] = '\0'; cmsGetProfileInfoASCII(hProfile, cmsInfoManufacturer, "en", "US", buffer, 1024); + return QLatin1String(buffer); } LCMSBOOL dkCmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) { LPcmsCIEXYZ tag = static_cast(cmsReadTag(hProfile, cmsSigMediaWhitePointTag)); if (tag == nullptr) + { return FALSE; + } *Dest = *tag; + return TRUE; } QString dkCmsTakeModel(cmsHPROFILE hProfile) { char buffer[1024]; const cmsMLU* const mlu = (cmsMLU*)cmsReadTag(hProfile, cmsSigDeviceModelDescTag); buffer[0] = '\0'; if (mlu == nullptr) + { return QString(); + } cmsMLUgetASCII(mlu, "en", "US", buffer, 1024); + return QLatin1String(buffer); } QString dkCmsTakeCopyright(cmsHPROFILE hProfile) { char buffer[1024]; const cmsMLU* const mlu = (cmsMLU*)cmsReadTag(hProfile, cmsSigCopyrightTag); buffer[0] = '\0'; if (mlu == nullptr) + { return QString(); + } cmsMLUgetASCII(mlu, "en", "US", buffer, 1024); + return QLatin1String(buffer); } DWORD dkCmsTakeHeaderFlags(cmsHPROFILE hProfile) { return static_cast(cmsGetHeaderFlags(hProfile)); } const BYTE* dkCmsTakeProfileID(cmsHPROFILE hProfile) { cmsUInt8Number* const ProfileID = new cmsUInt8Number[16]; cmsGetHeaderProfileID(hProfile, ProfileID); + return static_cast(ProfileID); } int dkCmsTakeRenderingIntent(cmsHPROFILE hProfile) { return static_cast(cmsGetHeaderRenderingIntent(hProfile)); } -// White Point & Primary chromas handling -// Returns the final chromatic adaptation from illuminant FromIll to Illuminant ToIll -// The cone matrix can be specified in ConeMatrix. -// If NULL, assuming D50 source. White point is given in xyY - +/** + * White Point & Primary chromas handling + * Returns the final chromatic adaptation from illuminant FromIll to Illuminant ToIll + * The cone matrix can be specified in ConeMatrix. + * If NULL, assuming D50 source. White point is given in xyY + */ LCMSBOOL dkCmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt) { // TODO: all based on private stuff, need to understand what digikam do in cietonguewidget with dkCmsAdaptMatrixFromD50 + cmsMAT3 result; _l1LPMAT3tol2cmsMAT3(r, &result); bool ret = cmsAdaptMatrixFromD50(&result, static_cast(DestWhitePt)); _l2cmsMAT3tol1LPMAT3(&result, r); return ret; } -// LCMSBOOL dkCmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt) -// { -// // TODO: all based on private stuff, need to understand what digikam do in cietonguewidget with dkCmsAdaptMatrixFromD50 -// cmsMAT3 result; -// -// result.v[0].n[0] = r->Red.X ; -// result.v[0].n[1] = r->Red.Y ; -// result.v[0].n[2] = r->Red.Z ; -// result.v[1].n[0] = r->Green.X; -// result.v[1].n[1] = r->Green.Y; -// result.v[1].n[2] = r->Green.Z; -// result.v[2].n[0] = r->Blue.X ; -// result.v[2].n[1] = r->Blue.Y ; -// result.v[2].n[2] = r->Blue.Z ; -// -// bool ret = cmsAdaptMatrixFromD50(&result, static_cast( DestWhitePt )); -// -// r->Red.X = result.v[0].n[0]; -// r->Red.Y = result.v[0].n[1]; -// r->Red.Z = result.v[0].n[2]; -// r->Green.X = result.v[1].n[0]; -// r->Green.Y = result.v[1].n[1]; -// r->Green.Z = result.v[1].n[2]; -// r->Blue.X = result.v[2].n[0]; -// r->Blue.Y = result.v[2].n[1]; -// r->Blue.Z = result.v[2].n[2]; -// -// return ret; -// } +/* +LCMSBOOL dkCmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt) +{ + // TODO: all based on private stuff, need to understand what digikam do in cietonguewidget with dkCmsAdaptMatrixFromD50 + + cmsMAT3 result; + + result.v[0].n[0] = r->Red.X ; + result.v[0].n[1] = r->Red.Y ; + result.v[0].n[2] = r->Red.Z ; + result.v[1].n[0] = r->Green.X; + result.v[1].n[1] = r->Green.Y; + result.v[1].n[2] = r->Green.Z; + result.v[2].n[0] = r->Blue.X ; + result.v[2].n[1] = r->Blue.Y ; + result.v[2].n[2] = r->Blue.Z ; + + bool ret = cmsAdaptMatrixFromD50(&result, static_cast( DestWhitePt )); + + r->Red.X = result.v[0].n[0]; + r->Red.Y = result.v[0].n[1]; + r->Red.Z = result.v[0].n[2]; + r->Green.X = result.v[1].n[0]; + r->Green.Y = result.v[1].n[1]; + r->Green.Z = result.v[1].n[2]; + r->Blue.X = result.v[2].n[0]; + r->Blue.Y = result.v[2].n[1]; + r->Blue.Z = result.v[2].n[2]; + + return ret; +} +*/ cmsBool GetProfileRGBPrimaries(cmsHPROFILE hProfile, cmsCIEXYZTRIPLE* const result, cmsUInt32Number intent) { cmsHPROFILE hXYZ; cmsHTRANSFORM hTransform; - cmsFloat64Number rgb[3][3] = {{1., 0., 0.}, + cmsFloat64Number rgb[3][3] = { + {1., 0., 0.}, {0., 1., 0.}, - {0., 0., 1.}}; + {0., 0., 1.} + }; hXYZ = cmsCreateXYZProfile(); if (hXYZ == nullptr) + { return FALSE; + } hTransform = cmsCreateTransform(hProfile, TYPE_RGB_DBL, hXYZ, TYPE_XYZ_DBL, intent, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE); cmsCloseProfile(hXYZ); if (hTransform == nullptr) + { return FALSE; + } cmsDoTransform(hTransform, rgb, result, 3); cmsDeleteTransform(hTransform); + return TRUE; } LCMSBOOL dkCmsReadICCMatrixRGB2XYZ(LPMAT3 r, cmsHPROFILE hProfile) { MAT3 result; // See README @ Monday, July 27, 2009 @ Less is more // return static_cast(GetProfileRGBPrimaries(hProfile, r, INTENT_RELATIVE_COLORIMETRIC)); LCMSBOOL ret = GetProfileRGBPrimaries(hProfile, &result, INTENT_RELATIVE_COLORIMETRIC); if (ret) { _l2tol1MAT3(&result, r); } return ret; } cmsHPROFILE dkCmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize) { return cmsOpenProfileFromMem(MemPtr, static_cast(dwSize)); } icProfileClassSignature dkCmsGetDeviceClass(cmsHPROFILE hProfile) { return static_cast(cmsGetDeviceClass(hProfile)); } LCMSBOOL dkCmsCloseProfile(cmsHPROFILE hProfile) { return static_cast(cmsCloseProfile(hProfile)); } cmsHTRANSFORM dkCmsCreateProofingTransform(cmsHPROFILE Input, DWORD InputFormat, cmsHPROFILE Output, DWORD OutputFormat, cmsHPROFILE Proofing, int Intent, int ProofingIntent, DWORD dwFlags) { return cmsCreateProofingTransform(Input, static_cast( InputFormat ), static_cast( Output ), static_cast( OutputFormat ), Proofing, static_cast( Intent ), static_cast( ProofingIntent ), static_cast( dwFlags )); } cmsHTRANSFORM dkCmsCreateTransform(cmsHPROFILE Input, DWORD InputFormat, cmsHPROFILE Output, DWORD OutputFormat, int Intent, DWORD dwFlags) { return cmsCreateTransform(Input, static_cast( InputFormat ), Output, static_cast( OutputFormat ), static_cast( Intent ), static_cast( dwFlags )); } cmsHPROFILE dkCmsCreateXYZProfile() { return cmsCreateXYZProfile(); } cmsHPROFILE dkCmsCreate_sRGBProfile() { return cmsCreate_sRGBProfile(); } void dkCmsDeleteTransform(cmsHTRANSFORM hTransform) { cmsDeleteTransform(hTransform); } double dkCmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2) { return static_cast(cmsDeltaE(static_cast(Lab1), static_cast(Lab2))); } void dkCmsDoTransform(cmsHTRANSFORM Transform, LPVOID InputBuffer, LPVOID OutputBuffer, unsigned int Size) { cmsDoTransform(Transform, static_cast( InputBuffer ), static_cast( OutputBuffer ), static_cast( Size )); } void dkCmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* const fXYZ) { cmsFloat2XYZEncoded(XYZ, fXYZ); } icColorSpaceSignature dkCmsGetColorSpace(cmsHPROFILE hProfile) { return static_cast(cmsGetColorSpace(hProfile)); } icColorSpaceSignature dkCmsGetPCS(cmsHPROFILE hProfile) { return static_cast(cmsGetPCS(hProfile)); } LCMSBOOL dkCmsIsTag(cmsHPROFILE hProfile, icTagSignature sig) { return static_cast(cmsIsTag(hProfile, static_cast(sig))); } cmsHPROFILE dkCmsOpenProfileFromFile(const char* const ICCProfile, const char* const sAccess) { return cmsOpenProfileFromFile(ICCProfile, sAccess); } void dkCmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* const Source) { cmsXYZ2xyY(static_cast(Dest), Source); } diff --git a/core/libs/dimg/filters/icc/digikam-lcms.h b/core/libs/dimg/filters/icc/digikam-lcms.h index cf5c9499af..533bb9a8f9 100644 --- a/core/libs/dimg/filters/icc/digikam-lcms.h +++ b/core/libs/dimg/filters/icc/digikam-lcms.h @@ -1,264 +1,264 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-03 * Description : LCMS2 wrapper * * Copyright (C) 2012 by Francesco Riosa * Copyright (C) 2012-2020 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. * * ============================================================ */ #ifndef DIGIKAM_LCMS_H #define DIGIKAM_LCMS_H // Qt includes #include // Local includes #include "digikam_export.h" #if defined (__MINGW32__) // krazy:exclude=cpp # define CMS_IS_WINDOWS_ 1 #else # ifndef CMS_DLL # define CMS_DLL # endif #endif // Turn off the specific compiler warnings with LCMS header. #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wundef" # pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif #define LCMS_DESC_MAX 512 #if !defined FAR # define FAR #endif #define LCMS_ERROR_SHOW 1 #define cmsFLAGS_NOTPRECALC 0x0100 #define cmsFLAGS_WHITEBLACKCOMPENSATION 0x2000 typedef int LCMSBOOL; typedef unsigned char BYTE, *LPBYTE; typedef unsigned short WORD, *LPWORD; typedef unsigned long DWORD, *LPDWORD; typedef void* LPVOID; // Colorspaces typedef cmsCIEXYZ FAR* LPcmsCIEXYZ; typedef cmsCIExyY FAR* LPcmsCIExyY; typedef cmsCIELab FAR* LPcmsCIELab; -typedef void* cmsHPROFILE; // Opaque typedefs to hide internals +typedef void* cmsHPROFILE; ///< Opaque typedefs to hide internals + +// These have changed from previous definitions -// these have changed from previous definitions typedef cmsCIEXYZTRIPLE MAT3; typedef cmsCIEXYZTRIPLE FAR* LPMAT3; /* profileClass enumerations */ typedef enum { - icSigInputClass = 0x73636E72L, /* 'scnr' */ - icSigDisplayClass = 0x6D6E7472L, /* 'mntr' */ - icSigOutputClass = 0x70727472L, /* 'prtr' */ - icSigLinkClass = 0x6C696E6BL, /* 'link' */ - icSigAbstractClass = 0x61627374L, /* 'abst' */ - icSigColorSpaceClass = 0x73706163L, /* 'spac' */ - icSigNamedColorClass = 0x6e6d636cL, /* 'nmcl' */ + icSigInputClass = 0x73636E72L, ///< 'scnr' + icSigDisplayClass = 0x6D6E7472L, ///< 'mntr' + icSigOutputClass = 0x70727472L, ///< 'prtr' + icSigLinkClass = 0x6C696E6BL, ///< 'link' + icSigAbstractClass = 0x61627374L, ///< 'abst' + icSigColorSpaceClass = 0x73706163L, ///< 'spac' + icSigNamedColorClass = 0x6e6d636cL, ///< 'nmcl' icMaxEnumClass = 0xFFFFFFFFL } icProfileClassSignature; /* * Color Space Signatures * Note that only icSigXYZData and icSigLabData are valid * Profile Connection Spaces (PCSs) */ typedef enum { - icSigXYZData = 0x58595A20L, /* 'XYZ ' */ - icSigLabData = 0x4C616220L, /* 'Lab ' */ - icSigLuvData = 0x4C757620L, /* 'Luv ' */ - icSigYCbCrData = 0x59436272L, /* 'YCbr' */ - icSigYxyData = 0x59787920L, /* 'Yxy ' */ - icSigRgbData = 0x52474220L, /* 'RGB ' */ - icSigGrayData = 0x47524159L, /* 'GRAY' */ - icSigHsvData = 0x48535620L, /* 'HSV ' */ - icSigHlsData = 0x484C5320L, /* 'HLS ' */ - icSigCmykData = 0x434D594BL, /* 'CMYK' */ - icSigCmyData = 0x434D5920L, /* 'CMY ' */ - icSig2colorData = 0x32434C52L, /* '2CLR' */ - icSig3colorData = 0x33434C52L, /* '3CLR' */ - icSig4colorData = 0x34434C52L, /* '4CLR' */ - icSig5colorData = 0x35434C52L, /* '5CLR' */ - icSig6colorData = 0x36434C52L, /* '6CLR' */ - icSig7colorData = 0x37434C52L, /* '7CLR' */ - icSig8colorData = 0x38434C52L, /* '8CLR' */ - icSig9colorData = 0x39434C52L, /* '9CLR' */ - icSig10colorData = 0x41434C52L, /* 'ACLR' */ - icSig11colorData = 0x42434C52L, /* 'BCLR' */ - icSig12colorData = 0x43434C52L, /* 'CCLR' */ - icSig13colorData = 0x44434C52L, /* 'DCLR' */ - icSig14colorData = 0x45434C52L, /* 'ECLR' */ - icSig15colorData = 0x46434C52L, /* 'FCLR' */ + icSigXYZData = 0x58595A20L, ///< 'XYZ ' + icSigLabData = 0x4C616220L, ///< 'Lab ' + icSigLuvData = 0x4C757620L, ///< 'Luv ' + icSigYCbCrData = 0x59436272L, ///< 'YCbr' + icSigYxyData = 0x59787920L, ///< 'Yxy ' + icSigRgbData = 0x52474220L, ///< 'RGB ' + icSigGrayData = 0x47524159L, ///< 'GRAY' + icSigHsvData = 0x48535620L, ///< 'HSV ' + icSigHlsData = 0x484C5320L, ///< 'HLS ' + icSigCmykData = 0x434D594BL, ///< 'CMYK' + icSigCmyData = 0x434D5920L, ///< 'CMY ' + icSig2colorData = 0x32434C52L, ///< '2CLR' + icSig3colorData = 0x33434C52L, ///< '3CLR' + icSig4colorData = 0x34434C52L, ///< '4CLR' + icSig5colorData = 0x35434C52L, ///< '5CLR' + icSig6colorData = 0x36434C52L, ///< '6CLR' + icSig7colorData = 0x37434C52L, ///< '7CLR' + icSig8colorData = 0x38434C52L, ///< '8CLR' + icSig9colorData = 0x39434C52L, ///< '9CLR' + icSig10colorData = 0x41434C52L, ///< 'ACLR' + icSig11colorData = 0x42434C52L, ///< 'BCLR' + icSig12colorData = 0x43434C52L, ///< 'CCLR' + icSig13colorData = 0x44434C52L, ///< 'DCLR' + icSig14colorData = 0x45434C52L, ///< 'ECLR' + icSig15colorData = 0x46434C52L, ///< 'FCLR' icMaxEnumData = 0xFFFFFFFFL } icColorSpaceSignature; /*------------------------------------------------------------------------*/ /* public tags and sizes */ typedef enum { - icSigAToB0Tag = 0x41324230L, /* 'A2B0' */ - icSigAToB1Tag = 0x41324231L, /* 'A2B1' */ - icSigAToB2Tag = 0x41324232L, /* 'A2B2' */ - icSigBlueColorantTag = 0x6258595AL, /* 'bXYZ' */ - icSigBlueTRCTag = 0x62545243L, /* 'bTRC' */ - icSigBToA0Tag = 0x42324130L, /* 'B2A0' */ - icSigBToA1Tag = 0x42324131L, /* 'B2A1' */ - icSigBToA2Tag = 0x42324132L, /* 'B2A2' */ - icSigCalibrationDateTimeTag = 0x63616C74L, /* 'calt' */ - icSigCharTargetTag = 0x74617267L, /* 'targ' */ - icSigCopyrightTag = 0x63707274L, /* 'cprt' */ - icSigCrdInfoTag = 0x63726469L, /* 'crdi' */ - icSigDeviceMfgDescTag = 0x646D6E64L, /* 'dmnd' */ - icSigDeviceModelDescTag = 0x646D6464L, /* 'dmdd' */ - icSigGamutTag = 0x67616D74L, /* 'gamt ' */ - icSigGrayTRCTag = 0x6b545243L, /* 'kTRC' */ - icSigGreenColorantTag = 0x6758595AL, /* 'gXYZ' */ - icSigGreenTRCTag = 0x67545243L, /* 'gTRC' */ - icSigLuminanceTag = 0x6C756d69L, /* 'lumi' */ - icSigMeasurementTag = 0x6D656173L, /* 'meas' */ - icSigMediaBlackPointTag = 0x626B7074L, /* 'bkpt' */ - icSigMediaWhitePointTag = 0x77747074L, /* 'wtpt' */ - icSigNamedColorTag = 0x6E636f6CL, /* 'ncol' - * OBSOLETE, use ncl2 */ - icSigNamedColor2Tag = 0x6E636C32L, /* 'ncl2' */ - icSigPreview0Tag = 0x70726530L, /* 'pre0' */ - icSigPreview1Tag = 0x70726531L, /* 'pre1' */ - icSigPreview2Tag = 0x70726532L, /* 'pre2' */ - icSigProfileDescriptionTag = 0x64657363L, /* 'desc' */ - icSigProfileSequenceDescTag = 0x70736571L, /* 'pseq' */ - icSigPs2CRD0Tag = 0x70736430L, /* 'psd0' */ - icSigPs2CRD1Tag = 0x70736431L, /* 'psd1' */ - icSigPs2CRD2Tag = 0x70736432L, /* 'psd2' */ - icSigPs2CRD3Tag = 0x70736433L, /* 'psd3' */ - icSigPs2CSATag = 0x70733273L, /* 'ps2s' */ - icSigPs2RenderingIntentTag = 0x70733269L, /* 'ps2i' */ - icSigRedColorantTag = 0x7258595AL, /* 'rXYZ' */ - icSigRedTRCTag = 0x72545243L, /* 'rTRC' */ - icSigScreeningDescTag = 0x73637264L, /* 'scrd' */ - icSigScreeningTag = 0x7363726EL, /* 'scrn' */ - icSigTechnologyTag = 0x74656368L, /* 'tech' */ - icSigUcrBgTag = 0x62666420L, /* 'bfd ' */ - icSigViewingCondDescTag = 0x76756564L, /* 'vued' */ - icSigViewingConditionsTag = 0x76696577L, /* 'view' */ + icSigAToB0Tag = 0x41324230L, ///< 'A2B0' + icSigAToB1Tag = 0x41324231L, ///< 'A2B1' + icSigAToB2Tag = 0x41324232L, ///< 'A2B2' + icSigBlueColorantTag = 0x6258595AL, ///< 'bXYZ' + icSigBlueTRCTag = 0x62545243L, ///< 'bTRC' + icSigBToA0Tag = 0x42324130L, ///< 'B2A0' + icSigBToA1Tag = 0x42324131L, ///< 'B2A1' + icSigBToA2Tag = 0x42324132L, ///< 'B2A2' + icSigCalibrationDateTimeTag = 0x63616C74L, ///< 'calt' + icSigCharTargetTag = 0x74617267L, ///< 'targ' + icSigCopyrightTag = 0x63707274L, ///< 'cprt' + icSigCrdInfoTag = 0x63726469L, ///< 'crdi' + icSigDeviceMfgDescTag = 0x646D6E64L, ///< 'dmnd' + icSigDeviceModelDescTag = 0x646D6464L, ///< 'dmdd' + icSigGamutTag = 0x67616D74L, ///< 'gamt' + icSigGrayTRCTag = 0x6b545243L, ///< 'kTRC' + icSigGreenColorantTag = 0x6758595AL, ///< 'gXYZ' + icSigGreenTRCTag = 0x67545243L, ///< 'gTRC' + icSigLuminanceTag = 0x6C756d69L, ///< 'lumi' + icSigMeasurementTag = 0x6D656173L, ///< 'meas' + icSigMediaBlackPointTag = 0x626B7074L, ///< 'bkpt' + icSigMediaWhitePointTag = 0x77747074L, ///< 'wtpt' + icSigNamedColorTag = 0x6E636f6CL, ///< 'ncol' NOTE: OBSOLETE, use ncl2 + icSigNamedColor2Tag = 0x6E636C32L, ///< 'ncl2' + icSigPreview0Tag = 0x70726530L, ///< 'pre0' + icSigPreview1Tag = 0x70726531L, ///< 'pre1' + icSigPreview2Tag = 0x70726532L, ///< 'pre2' + icSigProfileDescriptionTag = 0x64657363L, ///< 'desc' + icSigProfileSequenceDescTag = 0x70736571L, ///< 'pseq' + icSigPs2CRD0Tag = 0x70736430L, ///< 'psd0' + icSigPs2CRD1Tag = 0x70736431L, ///< 'psd1' + icSigPs2CRD2Tag = 0x70736432L, ///< 'psd2' + icSigPs2CRD3Tag = 0x70736433L, ///< 'psd3' + icSigPs2CSATag = 0x70733273L, ///< 'ps2s' + icSigPs2RenderingIntentTag = 0x70733269L, ///< 'ps2i' + icSigRedColorantTag = 0x7258595AL, ///< 'rXYZ' + icSigRedTRCTag = 0x72545243L, ///< 'rTRC' + icSigScreeningDescTag = 0x73637264L, ///< 'scrd' + icSigScreeningTag = 0x7363726EL, ///< 'scrn' + icSigTechnologyTag = 0x74656368L, ///< 'tech' + icSigUcrBgTag = 0x62666420L, ///< 'bfd ' + icSigViewingCondDescTag = 0x76756564L, ///< 'vued' + icSigViewingConditionsTag = 0x76696577L, ///< 'view' icMaxEnumTag = 0xFFFFFFFFL } icTagSignature; DIGIKAM_EXPORT int dkCmsErrorAction(int nAction); DIGIKAM_EXPORT DWORD dkCmsGetProfileICCversion(cmsHPROFILE hProfile); DIGIKAM_EXPORT void dkCmsSetAlarmCodes(int r, int g, int b); DIGIKAM_EXPORT QString dkCmsTakeProductName(cmsHPROFILE hProfile); DIGIKAM_EXPORT QString dkCmsTakeProductDesc(cmsHPROFILE hProfile); DIGIKAM_EXPORT QString dkCmsTakeProductInfo(cmsHPROFILE hProfile); DIGIKAM_EXPORT QString dkCmsTakeManufacturer(cmsHPROFILE hProfile); DIGIKAM_EXPORT LCMSBOOL dkCmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); DIGIKAM_EXPORT QString dkCmsTakeModel(cmsHPROFILE hProfile); DIGIKAM_EXPORT QString dkCmsTakeCopyright(cmsHPROFILE hProfile); DIGIKAM_EXPORT DWORD dkCmsTakeHeaderFlags(cmsHPROFILE hProfile); DIGIKAM_EXPORT const BYTE* dkCmsTakeProfileID(cmsHPROFILE hProfile); DIGIKAM_EXPORT LCMSBOOL dkCmsIsTag(cmsHPROFILE hProfile, icTagSignature sig); DIGIKAM_EXPORT int dkCmsTakeRenderingIntent(cmsHPROFILE hProfile); DIGIKAM_EXPORT LCMSBOOL dkCmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt); DIGIKAM_EXPORT LCMSBOOL dkCmsReadICCMatrixRGB2XYZ(LPMAT3 r, cmsHPROFILE hProfile); DIGIKAM_EXPORT cmsHPROFILE dkCmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize); DIGIKAM_EXPORT icProfileClassSignature dkCmsGetDeviceClass(cmsHPROFILE hProfile); DIGIKAM_EXPORT LCMSBOOL dkCmsCloseProfile(cmsHPROFILE hProfile); DIGIKAM_EXPORT cmsHTRANSFORM dkCmsCreateProofingTransform(cmsHPROFILE Input, DWORD InputFormat, cmsHPROFILE Output, DWORD OutputFormat, cmsHPROFILE Proofing, int Intent, int ProofingIntent, DWORD dwFlags); DIGIKAM_EXPORT cmsHTRANSFORM dkCmsCreateTransform(cmsHPROFILE Input, DWORD InputFormat, cmsHPROFILE Output, DWORD OutputFormat, int Intent, DWORD dwFlags); DIGIKAM_EXPORT cmsHPROFILE dkCmsCreateXYZProfile(); DIGIKAM_EXPORT cmsHPROFILE dkCmsCreate_sRGBProfile(); DIGIKAM_EXPORT void dkCmsDeleteTransform(cmsHTRANSFORM hTransform); DIGIKAM_EXPORT double dkCmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); DIGIKAM_EXPORT void dkCmsDoTransform(cmsHTRANSFORM Transform, LPVOID InputBuffer, LPVOID OutputBuffer, unsigned int Size); DIGIKAM_EXPORT void dkCmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* const fXYZ); DIGIKAM_EXPORT icColorSpaceSignature dkCmsGetColorSpace(cmsHPROFILE hProfile); DIGIKAM_EXPORT icColorSpaceSignature dkCmsGetPCS(cmsHPROFILE hProfile); DIGIKAM_EXPORT LCMSBOOL dkCmsIsTag(cmsHPROFILE hProfile, icTagSignature sig); DIGIKAM_EXPORT cmsHPROFILE dkCmsOpenProfileFromFile(const char* const ICCProfile, const char* const sAccess); DIGIKAM_EXPORT void dkCmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* const Source); #endif // DIGIKAM_LCMS_H diff --git a/core/libs/dimg/filters/icc/iccmanager.h b/core/libs/dimg/filters/icc/iccmanager.h index 9fdcea0b38..f27062bf4f 100644 --- a/core/libs/dimg/filters/icc/iccmanager.h +++ b/core/libs/dimg/filters/icc/iccmanager.h @@ -1,151 +1,156 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-12 * Description : methods that implement color management tasks * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2009-2011 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_MANAGER_H #define DIGIKAM_ICC_MANAGER_H // Local includes #include "digikam_export.h" #include "iccprofile.h" #include "iccsettings.h" #include "iccsettingscontainer.h" namespace Digikam { class DImg; class DImgLoaderObserver; class IccTransform; class DIGIKAM_EXPORT IccManager { public: /** * Constructs an IccManager object. * The DImg will be edited. The filePath is for display only. */ explicit IccManager(const DImg& image, const ICCSettingsContainer& settings = IccSettings::instance()->settings()); ~IccManager(); void setObserver(DImgLoaderObserver* const observer); DImg image() const; ICCSettingsContainer settings() const; DImgLoaderObserver* observer() const; bool hasValidWorkspace() const; bool isUncalibratedColor() const; bool isMissingProfile() const; bool isProfileMismatch() const; /** * Transforms the image for full editing, using default settings. * If the default settings require showing a dialog, the image is marked as such * but no action is taken. See IccPostLoadingManager. */ void transformDefault(); /** * Same as above, but not using default settings but the given settings. */ void transform(ICCSettingsContainer::Behavior behavior, const IccProfile& specifiedProfile = IccProfile()); /** * Transforms the image to sRGB */ void transformToSRGB(); /** * Returns true if a call to transformToSRGB() would have an effect. */ static bool isSRGB(const DImg& img); /** * Transforms the image for output to the specified output profile */ void transformForOutput(const IccProfile& outputProfile); /** * Transforms the image for display on screen. The result is not suitable * for editing or storage. * You can specify the widget in which the image will be displayed, or specify * the display profile yourself. * You can retrieve the profile with displayProfile() and pass it to transformForDisplay() * later (in a thread), or you can get a transform from displayTransform and apply it yourself. */ void transformForDisplay(); void transformForDisplay(QWidget* const widget); void transformForDisplay(const IccProfile& displayProfile); static IccProfile displayProfile(QWidget* const displayingWidget = nullptr); IccTransform displayTransform(QWidget* const displayingWidget = nullptr); IccTransform displayTransform(const IccProfile& displayProfile); /** * Returns a display transform, with soft-proofing enabled for the given device profile. */ IccTransform displaySoftProofingTransform(const IccProfile& deviceProfile, QWidget* const displayingWidget = nullptr); IccTransform displaySoftProofingTransform(const IccProfile& deviceProfile, const IccProfile& displayProfile); /** * Returns true if the given image is marked as needing user interaction * for further color management decision after loading. * If this returns true, use IccPostLoadingManager to do this. */ static bool needsPostLoadingManagement(const DImg& img); - /** Returns the profile that will be used to interpret the image, - * using the given behavior + /** + * Returns the profile that will be used to interpret the image, + * using the given behavior */ IccProfile imageProfile(ICCSettingsContainer::Behavior behavior, const IccProfile& specifiedProfile = IccProfile()); - /** Transforms the given QImage from the given inputProfile to sRGB. */ + /** + * Transforms the given QImage from the given inputProfile to sRGB. + */ static void transformToSRGB(QImage& qimage, const IccProfile& inputProfile); - /** Transforms the given QImage from sRGB to given display profile. */ + /** + * Transforms the given QImage from sRGB to given display profile. + */ static void transformForDisplay(QImage& qimage, const IccProfile& displayProfile1 = displayProfile()); protected: void getTransform(IccTransform& trans, ICCSettingsContainer::Behavior behavior, const IccProfile& specifiedProfile); void setIccProfile(const IccProfile& profile); ICCSettingsContainer::Behavior safestBestBehavior() const; private: // Hidden copy constructor and assignment operator. IccManager(const IccManager&); IccManager& operator=(const IccManager&); class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ICC_MANAGER_H diff --git a/core/libs/dimg/filters/icc/iccprofile.cpp b/core/libs/dimg/filters/icc/iccprofile.cpp index be62e72854..c7adbab827 100644 --- a/core/libs/dimg/filters/icc/iccprofile.cpp +++ b/core/libs/dimg/filters/icc/iccprofile.cpp @@ -1,620 +1,622 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-07 * Description : a wrapper class for an ICC color profile * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2009-2011 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 "iccprofile.h" #include "digikam-lcms.h" // Qt includes #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "dimg.h" namespace Digikam { class Q_DECL_HIDDEN IccProfile::Private : public QSharedData { public: explicit Private() : type(IccProfile::InvalidType), handle(nullptr) { } explicit Private(const Private& other) - : QSharedData(other) + : QSharedData(other), + handle(nullptr) { - handle = nullptr; operator = (other); } Private& operator=(const Private& other) { data = other.data; filePath = other.filePath; description = other.description; type = other.type; close(); handle = nullptr; + return *this; } ~Private() { close(); } void close() { if (handle) { LcmsLock lock; dkCmsCloseProfile(handle); handle = nullptr; } } public: QByteArray data; QString filePath; QString description; IccProfile::ProfileType type; cmsHPROFILE handle; }; // ---------------------------------------------------------------------------------- class Q_DECL_HIDDEN IccProfileStatic { public: IccProfileStatic() : lcmsMutex() { } QMutex lcmsMutex; QString adobeRGBPath; }; Q_GLOBAL_STATIC(IccProfileStatic, static_d) // ---------------------------------------------------------------------------------- LcmsLock::LcmsLock() { static_d->lcmsMutex.lock(); } LcmsLock::~LcmsLock() { static_d->lcmsMutex.unlock(); } // ---------------------------------------------------------------------------------- IccProfile::IccProfile() : d(nullptr) { } IccProfile::IccProfile(const QByteArray& data) : d(new Private) { d->data = data; } IccProfile::IccProfile(const QString& filePath) : d(new Private) { d->filePath = filePath; } IccProfile::IccProfile(const char* const location, const QString& relativePath) : d(nullptr) { QString filePath; // NOTE: if necessary, implement new location support here. if (QLatin1String(location) == QLatin1String("data")) { //qCDebug(DIGIKAM_DIMG_LOG) << "Searching ICC profile from data directory with relative path:" << relativePath; filePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, relativePath); } else { qCDebug(DIGIKAM_DIMG_LOG) << "Data location " << location << " to handle bundled profile is not supported."; } if (filePath.isNull()) { qCDebug(DIGIKAM_DIMG_LOG) << "The bundled profile" << relativePath << "cannot be found. Check your installation."; return; } d = new Private; d->filePath = filePath; } IccProfile IccProfile::sRGB() { // The srgb.icm file seems to have a whitepoint of D50, see #133913 return IccProfile("data", QLatin1String("digikam/profiles/srgb-d65.icm")); } IccProfile IccProfile::adobeRGB() { QString path = static_d->adobeRGBPath; if (path.isEmpty()) { path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("digikam/profiles/compatibleWithAdobeRGB1998.icc")); } return IccProfile(path); } IccProfile IccProfile::wideGamutRGB() { return IccProfile("data", QLatin1String("digikam/profiles/widegamut.icm")); } IccProfile IccProfile::proPhotoRGB() { return IccProfile("data", QLatin1String("digikam/profiles/prophoto.icm")); } QList IccProfile::defaultProfiles() { QList profiles; profiles << sRGB() << adobeRGB() << proPhotoRGB() << wideGamutRGB(); return profiles; } IccProfile::IccProfile(const IccProfile& other) : d(other.d) { } IccProfile::~IccProfile() { } IccProfile& IccProfile::operator=(const IccProfile& other) { d = other.d; + return *this; } bool IccProfile::isNull() const { return !d; } bool IccProfile::operator==(const IccProfile& other) const { if (d == other.d) { return true; } if (d && other.d) { if (!d->filePath.isNull() || !other.d->filePath.isNull()) { return (d->filePath == other.d->filePath); } if (!d->data.isNull() || other.d->data.isNull()) { return (d->data == other.d->data); } } return false; } bool IccProfile::isSameProfileAs(IccProfile& other) { if (d == other.d) { return true; } if (d && other.d) { // uses memcmp return (data() == other.data()); } return false; } QByteArray IccProfile::data() { if (!d) { return QByteArray(); } if (!d->data.isEmpty()) { return d->data; } else if (!d->filePath.isNull()) { QFile file(d->filePath); if (!file.open(QIODevice::ReadOnly)) { return QByteArray(); } d->data = file.readAll(); file.close(); return d->data; } return QByteArray(); } bool IccProfile::open() { if (!d) { return false; } if (d->handle) { return true; } - if (!d->data.isEmpty()) + if (!d->data.isEmpty()) { LcmsLock lock; d->handle = dkCmsOpenProfileFromMem(d->data.data(), (DWORD)d->data.size()); } else if (!d->filePath.isNull()) { // read file data(); if (d->data.isEmpty()) { return false; } LcmsLock lock; d->handle = dkCmsOpenProfileFromMem(d->data.data(), (DWORD)d->data.size()); } return d->handle; } void IccProfile::close() { if (!d) { return; } d->close(); } bool IccProfile::isOpen() const { if (!d) { return false; } return d->handle; } QString IccProfile::filePath() const { if (!d) { return QString(); } return d->filePath; } QString IccProfile::description() { if (!d) { return QString(); } if (!d->description.isNull()) { return d->description; } if (!open()) { return QString(); } LcmsLock lock; if (!QString(dkCmsTakeProductDesc(d->handle)).isEmpty()) { d->description = QString(dkCmsTakeProductDesc(d->handle)).replace(QLatin1Char('\n'), QLatin1Char(' ')); } return d->description; } IccProfile::ProfileType IccProfile::type() { if (!d) { return InvalidType; } if (d->type != InvalidType) { return d->type; } if (!open()) { return InvalidType; } LcmsLock lock; switch ((int)dkCmsGetDeviceClass(d->handle)) { case icSigInputClass: - case 0x6e6b7066: // 'nkbf', proprietary in Nikon profiles + case 0x6e6b7066: ///< 'nkbf', proprietary in Nikon profiles d->type = Input; break; case icSigDisplayClass: d->type = Display; break; case icSigOutputClass: d->type = Output; break; case icSigColorSpaceClass: d->type = ColorSpace; break; case icSigLinkClass: d->type = DeviceLink; break; case icSigAbstractClass: d->type = Abstract; break; case icSigNamedColorClass: d->type = NamedColor; break; default: break; } return d->type; } bool IccProfile::writeToFile(const QString& filePath) { if (!d) { return false; } QByteArray profile = data(); if (!profile.isEmpty()) { QFile file(filePath); if (!file.open(QIODevice::WriteOnly)) { return false; } if (file.write(profile) == -1) { return false; } file.close(); return true; } return false; } void* IccProfile::handle() const { if (!d) { return nullptr; } return d->handle; } QStringList IccProfile::defaultSearchPaths() { QStringList paths; QStringList candidates; paths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("color/icc"), QStandardPaths::LocateDirectory); #ifdef Q_OS_WIN candidates << QDir::rootPath() + QLatin1String("/Windows/Spool/Drivers/Color/"); // For Win2K and WinXP candidates << QDir::rootPath() + QLatin1String("/Windows/Color/"); // For Win98 and WinMe #elif defined (Q_OS_OSX) // Use a scheme highly identical to the Linux scheme, adapted for MacPorts in /opt/local, ofcial PKG installer, and the OS X standard ColorSync directories candidates << QLatin1String("/System/Library/ColorSync/Profiles"); candidates << QLatin1String("/Library/ColorSync/Profiles"); candidates << QDir::homePath() + QLatin1String("/Library/ColorSync/Profiles"); // MacPorts installs for KDE, so we include the XDG data dirs, including /usr/share/color/icc QStringList dataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(QLatin1Char(':'), QString::SkipEmptyParts); if (!dataDirs.contains(QLatin1String("/opt/local/share"))) { dataDirs << QLatin1String("/opt/local/share"); } if (!dataDirs.contains(QLatin1String("/opt/digikam/share"))) { dataDirs << QLatin1String("/opt/digikam/share"); } foreach (const QString& dataDir, dataDirs) { candidates << dataDir + QLatin1String("/color/icc"); } // XDG_DATA_HOME QString dataHomeDir = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME")); if (!dataHomeDir.isEmpty()) { candidates << dataHomeDir + QLatin1String("/color/icc"); candidates << dataHomeDir + QLatin1String("/icc"); } // home dir directories candidates << QDir::homePath() + QLatin1String("/.local/share/color/icc/"); candidates << QDir::homePath() + QLatin1String("/.local/share/icc/"); candidates << QDir::homePath() + QLatin1String("/.color/icc/"); #else // LINUX // XDG data dirs, including /usr/share/color/icc QStringList dataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(QLatin1Char(':'), QString::SkipEmptyParts); if (!dataDirs.contains(QLatin1String("/usr/share"))) { dataDirs << QLatin1String("/usr/share"); } if (!dataDirs.contains(QLatin1String("/usr/local/share"))) { dataDirs << QLatin1String("/usr/local/share"); } foreach (const QString& dataDir, dataDirs) { candidates << dataDir + QLatin1String("/color/icc"); } // XDG_DATA_HOME QString dataHomeDir = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME")); if (!dataHomeDir.isEmpty()) { candidates << dataHomeDir + QLatin1String("/color/icc"); candidates << dataHomeDir + QLatin1String("/icc"); } // home dir directories candidates << QDir::homePath() + QLatin1String("/.local/share/color/icc/"); candidates << QDir::homePath() + QLatin1String("/.local/share/icc/"); candidates << QDir::homePath() + QLatin1String("/.color/icc/"); #endif foreach (const QString& candidate, candidates) { QDir dir(candidate); if (dir.exists() && dir.isReadable()) { QString path = dir.canonicalPath(); if (!paths.contains(path)) { paths << path; } } } //qCDebug(DIGIKAM_DIMG_LOG) << candidates << '\n' << paths; return paths; } void IccProfile::considerOriginalAdobeRGB(const QString& filePath) { if (!static_d->adobeRGBPath.isNull()) { return; } QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(&file); file.close(); if (md5.result().toHex() == QByteArray("dea88382d899d5f6e573b432473ae138")) { qCDebug(DIGIKAM_DIMG_LOG) << "The original Adobe RGB (1998) profile has been found at" << filePath; static_d->adobeRGBPath = filePath; } } } } // namespace Digikam diff --git a/core/libs/dimg/filters/icc/iccprofile.h b/core/libs/dimg/filters/icc/iccprofile.h index 4e5258440b..1ac537f852 100644 --- a/core/libs/dimg/filters/icc/iccprofile.h +++ b/core/libs/dimg/filters/icc/iccprofile.h @@ -1,202 +1,204 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-07 * Description : a wrapper class for an ICC color profile * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2009-2011 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_PROFILE_H #define DIGIKAM_ICC_PROFILE_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT IccProfile { public: enum ProfileType { /// Returned for a null profile or an unknown (non-standard) profile type InvalidType, /// For an input device like a scanner or digital camera Input, /// For an output device like a printer Output, /// For a display device like a monitor Display, Abstract, ColorSpace, DeviceLink, NamedColor }; public: /** * Creates a null profile */ IccProfile(); /** * Creates a profile from the given data in memory */ explicit IccProfile(const QByteArray& data); /** * Creates a profile from the given file */ explicit IccProfile(const QString& filePath); /** * Returns the profiles available with RawEngine. You still need to call open() on them. */ static IccProfile sRGB(); static IccProfile adobeRGB(); static IccProfile wideGamutRGB(); static IccProfile proPhotoRGB(); - /// Returns a list with the profiles above + /** + * Returns a list with the profiles above + */ static QList defaultProfiles(); IccProfile(const IccProfile& other); ~IccProfile(); IccProfile& operator=(const IccProfile& other); bool isNull() const; /** * Returns true if both profiles are null, if both profiles are created from the * same file profile, or if the loaded profile data is identical. * Note: This will not ensure that the data is loaded. Use isSameProfile(). */ bool operator==(const IccProfile& other) const; bool operator!=(const IccProfile& other) const { return !operator==(other); } /** * This method compares the actual profile data bit by bit. */ bool isSameProfileAs(IccProfile& other); /** * Open this profile. Returns true if the operation succeeded * or the profile is already open. Returns false if the profile is null * or the operation failed. * You need to open each profile after construction. */ bool open(); /** * Close the profile, freeing resources. You can re-open. * Called automatically at destruction. */ void close(); /** * Returns if the profile is opened. */ bool isOpen() const; /** * Returns the filename that this profile was read from. * returns a null QString() if this profile was loaded from memory. */ QString filePath() const; /** * Reads the profile description. Opens the profile if necessary. */ QString description(); ProfileType type(); /** * Returns the raw profile data. * Reads the data from disk if loaded from disk and not yet loaded. */ QByteArray data(); /** * Writes the profile to the given file. */ bool writeToFile(const QString& filePath); /// Access to the LCMS cmsHPROFILE handle void* handle() const; operator void*() const { return handle(); } /** * Returns the default search paths for ICC profiles. * This does not include any user-specified settings. */ static QStringList defaultSearchPaths(); static QList scanDirectories(const QStringList& dirs); static void considerOriginalAdobeRGB(const QString& filePath); private: IccProfile(const char* const location, const QString& relativePath); private: class Private; QSharedDataPointer d; }; // -------------------------------------------------------------------------------------- class DIGIKAM_EXPORT LcmsLock { public: /** * Obtain an LcmsLock if you access not clearly thread-safe LittleCMS methods */ explicit LcmsLock(); ~LcmsLock(); }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::IccProfile) #endif // DIGIKAM_ICC_PROFILE_H diff --git a/core/libs/dimg/filters/icc/iccsettings.cpp b/core/libs/dimg/filters/icc/iccsettings.cpp index fa25253248..6686c0ee67 100644 --- a/core/libs/dimg/filters/icc/iccsettings.cpp +++ b/core/libs/dimg/filters/icc/iccsettings.cpp @@ -1,641 +1,650 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-09 * Description : central place for ICC settings * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2009-2011 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 "iccsettings.h" #include "digikam_config.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "digikam_debug.h" #include "iccprofile.h" #include "icctransform.h" // X11 includes // Note must be after all other to prevent broken compilation #ifdef HAVE_X11 # include # include # include # include #endif // HAVE_X11 namespace Digikam { class Q_DECL_HIDDEN IccSettings::Private { public: explicit Private() : settings(ICCSettingsContainer()), configGroup(QLatin1String("Color Management")) { } QList scanDirectories(const QStringList& dirs); void scanDirectory(const QString& path, const QStringList& filter, QList* const profiles); IccProfile profileFromWindowSystem(QWidget* const widget); ICCSettingsContainer readFromConfig() const; void writeToConfig() const; void writeManagedViewToConfig() const; void writeManagedPreviewsToConfig() const; public: ICCSettingsContainer settings; QMutex mutex; QList profiles; QHash screenProfiles; const QString configGroup; }; // ----------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN IccSettingsCreator { public: IccSettings object; }; Q_GLOBAL_STATIC(IccSettingsCreator, creator) // ----------------------------------------------------------------------------------------------- IccSettings* IccSettings::instance() { return &creator->object; } IccSettings::IccSettings() : d(new Private) { IccTransform::init(); readFromConfig(); qRegisterMetaType("ICCSettingsContainer"); } IccSettings::~IccSettings() { delete d; } ICCSettingsContainer IccSettings::settings() { QMutexLocker lock(&d->mutex); ICCSettingsContainer s(d->settings); + return s; } IccProfile IccSettings::monitorProfile(QWidget* const widget) { // system-wide profile set? + IccProfile profile = d->profileFromWindowSystem(widget); if (!profile.isNull()) { return profile; } QMutexLocker lock(&d->mutex); if (!d->settings.monitorProfile.isNull()) { return IccProfile(d->settings.monitorProfile); } else { return IccProfile::sRGB(); } } bool IccSettings::monitorProfileFromSystem() const { // First, look into cache + { QMutexLocker lock(&d->mutex); foreach (const IccProfile& profile, d->screenProfiles) { if (!profile.isNull()) { return true; } } } // Second, check all toplevel widgets + QList topLevels = qApp->topLevelWidgets(); foreach (QWidget* const widget, topLevels) { if (!d->profileFromWindowSystem(widget).isNull()) { return true; } } return false; } /* * From koffice/libs/pigment/colorprofiles/KoLcmsColorProfileContainer.cpp * Copyright (C) 2000 Matthias Elter * 2001 John Califf * 2004 Boudewijn Rempt * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Adrian Page */ IccProfile IccSettings::Private::profileFromWindowSystem(QWidget* const widget) { #ifdef HAVE_X11 if (!QX11Info::isPlatformX11()) { qCDebug(DIGIKAM_DIMG_LOG) << "Desktop platform is not X11"; return IccProfile(); } unsigned long appRootWindow; QString atomName; QScreen* const screen = qApp->primaryScreen(); if (!screen) { qCDebug(DIGIKAM_DIMG_LOG) << "No screen available for application"; return IccProfile(); } QScreen* widgetScreen = screen; if (widget) { QWindow* winHandle = widget->windowHandle(); if (!winHandle) { if (QWidget* const nativeParent = widget->nativeParentWidget()) { winHandle = nativeParent->windowHandle(); } } if (winHandle) { widgetScreen = winHandle->screen(); } } int screenNumber = qMax(qApp->screens().indexOf(widgetScreen), 0); IccProfile profile; { QMutexLocker lock(&mutex); if (screenProfiles.contains(screenNumber)) { return screenProfiles.value(screenNumber); } } if (screen->virtualSiblings().size() > 1) { appRootWindow = QX11Info::appRootWindow(QX11Info::appScreen()); atomName = QString::fromLatin1("_ICC_PROFILE_%1").arg(screenNumber); } else { appRootWindow = QX11Info::appRootWindow(screenNumber); atomName = QLatin1String("_ICC_PROFILE"); } Atom type; int format; unsigned long nitems; unsigned long bytes_after; quint8* str = nullptr; static Atom icc_atom = XInternAtom(QX11Info::display(), atomName.toLatin1().constData(), True); if ((icc_atom != None) && (XGetWindowProperty(QX11Info::display(), appRootWindow, icc_atom, 0, INT_MAX, False, XA_CARDINAL, &type, &format, &nitems, &bytes_after, (unsigned char**)& str) == Success) && nitems ) { QByteArray bytes = QByteArray::fromRawData((char*)str, (quint32)nitems); if (!bytes.isEmpty()) { profile = IccProfile(bytes); } qCDebug(DIGIKAM_DIMG_LOG) << "Found X.org XICC monitor profile " << profile.description(); } else { qCDebug(DIGIKAM_DIMG_LOG) << "No X.org XICC profile installed for screen " << screenNumber; } // insert to cache even if null { QMutexLocker lock(&mutex); screenProfiles.insert(screenNumber, profile); } #elif defined Q_OS_WIN //TODO Q_UNUSED(widget); #elif defined Q_OS_OSX //TODO Q_UNUSED(widget); #else // Unsupported platform Q_UNUSED(widget); #endif return IccProfile(); } bool IccSettings::isEnabled() const { return d->settings.enableCM; } bool IccSettings::useManagedPreviews() const { return (isEnabled() && d->settings.useManagedPreviews); } ICCSettingsContainer IccSettings::Private::readFromConfig() const { ICCSettingsContainer s; KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroup); s.readFromConfig(group); return s; } void IccSettings::Private::writeToConfig() const { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroup); settings.writeToConfig(group); } void IccSettings::Private::writeManagedViewToConfig() const { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroup); settings.writeManagedViewToConfig(group); } void IccSettings::Private::writeManagedPreviewsToConfig() const { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroup); settings.writeManagedPreviewsToConfig(group); } void IccSettings::readFromConfig() { ICCSettingsContainer old, s; s = d->readFromConfig(); + { QMutexLocker lock(&d->mutex); old = d->settings; d->settings = s; } emit settingsChanged(); emit settingsChanged(s, old); } void IccSettings::setSettings(const ICCSettingsContainer& settings) { ICCSettingsContainer old; + { QMutexLocker lock(&d->mutex); if (settings.iccFolder != d->settings.iccFolder) { d->profiles.clear(); } old = d->settings; d->settings = settings; } d->writeToConfig(); emit settingsChanged(); emit settingsChanged(settings, old); } void IccSettings::setUseManagedView(bool useManagedView) { ICCSettingsContainer old, current; + { QMutexLocker lock(&d->mutex); old = d->settings; d->settings.useManagedView = useManagedView; current = d->settings; } d->writeManagedViewToConfig(); emit settingsChanged(); emit settingsChanged(current, old); } void IccSettings::setUseManagedPreviews(bool useManagedPreviews) { ICCSettingsContainer old, current; + { QMutexLocker lock(&d->mutex); old = d->settings; d->settings.useManagedPreviews = useManagedPreviews; current = d->settings; } d->writeManagedPreviewsToConfig(); emit settingsChanged(); emit settingsChanged(current, old); } void IccSettings::setIccPath(const QString& path) { ICCSettingsContainer old, current; + { QMutexLocker lock(&d->mutex); if (path == d->settings.iccFolder) { return; } d->profiles.clear(); old = d->settings; d->settings.iccFolder = path; current = d->settings; } d->writeManagedViewToConfig(); emit settingsChanged(); emit settingsChanged(current, old); } QList IccSettings::Private::scanDirectories(const QStringList& dirs) { QList profiles; QStringList filters; filters << QLatin1String("*.icc") << QLatin1String("*.icm"); qCDebug(DIGIKAM_DIMG_LOG) << dirs; foreach (const QString& dirPath, dirs) { QDir dir(dirPath); if (!dir.exists()) { continue; } scanDirectory(dir.path(), filters, &profiles); } return profiles; } void IccSettings::Private::scanDirectory(const QString& path, const QStringList& filter, QList* const profiles) { QDir dir(path); QFileInfoList infos; infos << dir.entryInfoList(filter, QDir::Files | QDir::Readable); infos << dir.entryInfoList(QDir::Dirs | QDir::Readable | QDir::NoDotAndDotDot); foreach (const QFileInfo& info, infos) { if (info.isFile()) { //qCDebug(DIGIKAM_DIMG_LOG) << info.filePath() << (info.exists() && info.isReadable()); IccProfile profile(info.filePath()); if (profile.open()) { *profiles << profile; if (info.fileName() == QLatin1String("AdobeRGB1998.icc")) { IccProfile::considerOriginalAdobeRGB(info.filePath()); } } } else if (info.isDir() && !info.isSymLink()) { scanDirectory(info.filePath(), filter, profiles); } } } QList IccSettings::allProfiles() { QString extraPath; { QMutexLocker lock(&d->mutex); if (!d->profiles.isEmpty()) { return d->profiles; } extraPath = d->settings.iccFolder; } QList profiles; // get system paths, e.g. /usr/share/color/icc QStringList paths = IccProfile::defaultSearchPaths(); // add user-specified path if (!extraPath.isEmpty() && !paths.contains(extraPath)) { paths << extraPath; } // check search directories profiles << d->scanDirectories(paths); // load profiles that come with RawEngine profiles << IccProfile::defaultProfiles(); QMutexLocker lock(&d->mutex); d->profiles = profiles; return d->profiles; } QList IccSettings::workspaceProfiles() { QList profiles; foreach (IccProfile profile, allProfiles()) // krazy:exclude=foreach { switch (profile.type()) { case IccProfile::Display: case IccProfile::ColorSpace: profiles << profile; break; default: break; } } return profiles; } QList IccSettings::displayProfiles() { QList profiles; foreach (IccProfile profile, allProfiles()) // krazy:exclude=foreach { if (profile.type() == IccProfile::Display) { profiles << profile; } } return profiles; } QList IccSettings::inputProfiles() { QList profiles; foreach (IccProfile profile, allProfiles()) // krazy:exclude=foreach { switch (profile.type()) { case IccProfile::Input: case IccProfile::ColorSpace: profiles << profile; break; default: break; } } return profiles; } QList IccSettings::outputProfiles() { QList profiles; foreach (IccProfile profile, allProfiles()) // krazy:exclude=foreach { if (profile.type() == IccProfile::Output) { profiles << profile; } } return profiles; } QList IccSettings::profilesForDescription(const QString& description) { QList profiles; if (description.isEmpty()) { return profiles; } foreach (IccProfile profile, allProfiles()) // krazy:exclude=foreach { if (profile.description() == description) { profiles << profile; } } return profiles; } void IccSettings::loadAllProfilesProperties() { allProfiles(); const int size = d->profiles.size(); for (int i = 0 ; i < size ; ++i) { IccProfile& profile = d->profiles[i]; if (!profile.isOpen()) { profile.description(); profile.type(); profile.close(); } else { profile.description(); profile.type(); } } } } // namespace Digikam diff --git a/core/libs/dimg/filters/icc/iccsettings.h b/core/libs/dimg/filters/icc/iccsettings.h index 6958c8fbd4..f7148c0cbd 100644 --- a/core/libs/dimg/filters/icc/iccsettings.h +++ b/core/libs/dimg/filters/icc/iccsettings.h @@ -1,135 +1,154 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-09 * Description : central place for ICC settings * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2009-2011 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_SETTINGS_H #define DIGIKAM_ICC_SETTINGS_H // Qt includes #include // Local includes #include "digikam_export.h" #include "iccsettingscontainer.h" namespace Digikam { class IccProfile; class DIGIKAM_EXPORT IccSettings : public QObject { Q_OBJECT public: - /** Global container for ICC settings. All accessor methods are thread-safe. + /** + * Global container for ICC settings. All accessor methods are thread-safe. */ static IccSettings* instance(); - /// Returns the current ICC settings. + /** + * Returns the current ICC settings. + */ ICCSettingsContainer settings(); - /// Returns if color management is enabled. + /** + * Returns if color management is enabled. + */ bool isEnabled() const; - /// Returns if color management for previews is enabled. + /** + * Returns if color management for previews is enabled. + */ bool useManagedPreviews() const; /** * Returns the monitor profile (for color managed view). * If there are multiple screens, a system-wide settings specifies the monitor profile, * and the widget parameter is passed, the returned profile is for the widget's screen. * If no settings is specified, the default sRGB profile is returned. */ IccProfile monitorProfile(QWidget* const widget = nullptr); /** * Returns if the monitor profile (as returned by monitorProfile()) * is set system-wide, so that the monitorProfile field of the current settings * need not be set and will not be used by monitorProfile(). */ bool monitorProfileFromSystem() const; /** * Sets the current ICC settings and writes them to config. */ void setSettings(const ICCSettingsContainer& settings); - /// Set single parts of the settings + /** + * Set single parts of the settings + */ void setUseManagedView(bool useManagedView); void setUseManagedPreviews(bool useManagedPreviews); void setIccPath(const QString& path); QList allProfiles(); - /// Get available profiles suitable as workspace profile + /** + * Get available profiles suitable as workspace profile + */ QList workspaceProfiles(); - /// Get available profiles suitable as monitor/display profile + /** + * Get available profiles suitable as monitor/display profile + */ QList displayProfiles(); - /// Get available profiles suitable as input profile + /** + * Get available profiles suitable as input profile + */ QList inputProfiles(); - /// Get available profiles suitable as proof/output profiles + /** + * Get available profiles suitable as proof/output profiles + */ QList outputProfiles(); - /// Returns a list of profiles with the given description() + /** + * Returns a list of profiles with the given description() + */ QList profilesForDescription(const QString& description); /** * IccProfile caches some of its properties (description, type) * when it was read once. Subsequently, to read these values no * opening is needed. This ensures that all profiles have these * values read. May imply scanning and opening all profiles. */ void loadAllProfilesProperties(); Q_SIGNALS: void settingsChanged(); void settingsChanged(const ICCSettingsContainer& current, const ICCSettingsContainer& previous); private: explicit IccSettings(); ~IccSettings(); void readFromConfig(); private: class Private; Private* const d; friend class Private; friend class IccSettingsCreator; }; } // namespace Digikam #endif // DIGIKAM_ICC_SETTINGS_H diff --git a/core/libs/dimg/filters/icc/iccsettingscontainer.cpp b/core/libs/dimg/filters/icc/iccsettingscontainer.cpp index 4d971b12ab..6766803805 100644 --- a/core/libs/dimg/filters/icc/iccsettingscontainer.cpp +++ b/core/libs/dimg/filters/icc/iccsettingscontainer.cpp @@ -1,148 +1,145 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-12-08 * Description : ICC Settings Container. * * Copyright (C) 2005-2007 by F.J. Cruz * Copyright (C) 2005-2020 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 "iccsettingscontainer.h" // KDE includes #include // Local includes #include "icctransform.h" #include "iccprofile.h" namespace Digikam { ICCSettingsContainer::ICCSettingsContainer() + : enableCM(true), + defaultMismatchBehavior(EmbeddedToWorkspace), + defaultMissingProfileBehavior(SRGBToWorkspace), + defaultUncalibratedBehavior(AutoToWorkspace), + lastMismatchBehavior(EmbeddedToWorkspace), + lastMissingProfileBehavior(SRGBToWorkspace), + lastUncalibratedBehavior(AutoToWorkspace), + useManagedView(true), + useManagedPreviews(true), + useBPC(true), + renderingIntent(IccTransform::Perceptual), + proofingRenderingIntent(IccTransform::AbsoluteColorimetric), + doGamutCheck(false), + gamutCheckMaskColor(QColor(126, 255, 255)) { - enableCM = true; - - defaultMismatchBehavior = EmbeddedToWorkspace; - defaultMissingProfileBehavior = SRGBToWorkspace; - defaultUncalibratedBehavior = AutoToWorkspace; - - lastMismatchBehavior = EmbeddedToWorkspace; - lastMissingProfileBehavior = SRGBToWorkspace; - lastUncalibratedBehavior = AutoToWorkspace; - - useManagedView = true; - useManagedPreviews = true; - useBPC = true; - renderingIntent = IccTransform::Perceptual; - proofingRenderingIntent = IccTransform::AbsoluteColorimetric; - doGamutCheck = false; - gamutCheckMaskColor = QColor(126, 255, 255); } void ICCSettingsContainer::readFromConfig(KConfigGroup& group) { enableCM = group.readEntry("EnableCM", true); //if (!group.hasKey("OnProfileMismatch") && group.hasKey("BehaviourICC")) // legacy // behavior = group.readEntry("BehaviourICC", false) ? "convert" : "ask"; QString sRGB = IccProfile::sRGB().filePath(); workspaceProfile = group.readPathEntry("WorkProfileFile", sRGB); monitorProfile = group.readPathEntry("MonitorProfileFile", sRGB); defaultInputProfile = group.readPathEntry("InProfileFile", QString()); defaultProofProfile = group.readPathEntry("ProofProfileFile", QString()); defaultMismatchBehavior = (Behavior)group.readEntry("DefaultMismatchBehavior", (int)EmbeddedToWorkspace); defaultMissingProfileBehavior = (Behavior)group.readEntry("DefaultMissingProfileBehavior", (int)SRGBToWorkspace); defaultUncalibratedBehavior = (Behavior)group.readEntry("DefaultUncalibratedBehavior", (int)AutoToWorkspace); lastMismatchBehavior = (Behavior)group.readEntry("LastMismatchBehavior", (int)EmbeddedToWorkspace); lastMissingProfileBehavior = (Behavior)group.readEntry("LastMissingProfileBehavior", (int)SRGBToWorkspace); lastUncalibratedBehavior = (Behavior)group.readEntry("LastUncalibratedBehavior", (int)AutoToWorkspace); lastSpecifiedAssignProfile = group.readEntry("LastSpecifiedAssignProfile", sRGB); lastSpecifiedInputProfile = group.readEntry("LastSpecifiedInputProfile", defaultInputProfile); useBPC = group.readEntry("BPCAlgorithm", true); useManagedView = group.readEntry("ManagedView", true); useManagedPreviews = group.readEntry("ManagedPreviews", true); renderingIntent = group.readEntry("RenderingIntent", (int)IccTransform::Perceptual); proofingRenderingIntent = group.readEntry("ProofingRenderingIntent", (int)IccTransform::AbsoluteColorimetric); doGamutCheck = group.readEntry("DoGamutCheck", false); gamutCheckMaskColor = group.readEntry("GamutCheckMaskColor", QColor(126, 255, 255)); iccFolder = group.readEntry("DefaultPath", QString()); } void ICCSettingsContainer::writeToConfig(KConfigGroup& group) const { group.writeEntry("EnableCM", enableCM); if (!enableCM) { return; // No need to write settings in this case. } group.writeEntry("DefaultMismatchBehavior", (int)defaultMismatchBehavior); group.writeEntry("DefaultMissingProfileBehavior", (int)defaultMissingProfileBehavior); group.writeEntry("DefaultUncalibratedBehavior", (int)defaultUncalibratedBehavior); group.writeEntry("LastMismatchBehavior", (int)lastMismatchBehavior); group.writeEntry("LastMissingProfileBehavior", (int)lastMissingProfileBehavior); group.writeEntry("LastUncalibratedBehavior", (int)lastUncalibratedBehavior); group.writeEntry("LastSpecifiedAssignProfile", lastSpecifiedAssignProfile); group.writeEntry("LastSpecifiedInputProfile", lastSpecifiedInputProfile); group.writeEntry("BPCAlgorithm", useBPC); group.writeEntry("ManagedView", useManagedView); group.writeEntry("ManagedPreviews", useManagedPreviews); group.writeEntry("RenderingIntent", renderingIntent); group.writePathEntry("WorkProfileFile", workspaceProfile); group.writePathEntry("MonitorProfileFile", monitorProfile); group.writePathEntry("InProfileFile", defaultInputProfile); group.writePathEntry("ProofProfileFile", defaultProofProfile); group.writeEntry("ProofingRenderingIntent", proofingRenderingIntent); group.writeEntry("DoGamutCheck", doGamutCheck); group.writeEntry("GamutCheckMaskColor", gamutCheckMaskColor); group.writeEntry("DefaultPath", iccFolder); } void ICCSettingsContainer::writeManagedViewToConfig(KConfigGroup& group) const { // Save Color Managed View setting in config file. For performance // reason, no need to flush file, it cached in memory and will be flushed // to disk at end of session. group.writeEntry("ManagedView", useManagedView); } void ICCSettingsContainer::writeManagedPreviewsToConfig(KConfigGroup& group) const { // Save Color Managed Previews setting in config file. For performance // reason, no need to flush file, it cached in memory and will be flushed // to disk at end of session. group.writeEntry("ManagedPreviews", useManagedView); } } // namespace Digikam diff --git a/core/libs/dimg/filters/icc/iccsettingscontainer.h b/core/libs/dimg/filters/icc/iccsettingscontainer.h index 1cafdc095c..39a5e3dedb 100644 --- a/core/libs/dimg/filters/icc/iccsettingscontainer.h +++ b/core/libs/dimg/filters/icc/iccsettingscontainer.h @@ -1,136 +1,136 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-12-08 * Description : ICC Settings Container. * * Copyright (C) 2005-2007 by F.J. Cruz * Copyright (C) 2005-2020 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_SETTINGS_CONTAINER_H #define DIGIKAM_ICC_SETTINGS_CONTAINER_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT ICCSettingsContainer { public: enum BehaviorEnum { - // Note: Values are stored in config - keep them constant + /// Note: Values are stored in config - keep them constant InvalidBehavior = 0, /// Interpretation of the image data UseEmbeddedProfile = 1 << 0, UseSRGB = 1 << 1, UseWorkspace = 1 << 2, UseDefaultInputProfile = 1 << 3, UseSpecifiedProfile = 1 << 4, AutomaticColors = 1 << 5, DoNotInterpret = 1 << 6, /// Transformation / target profile KeepProfile = 1 << 10, ConvertToWorkspace = 1 << 11, /// Special flags and values LeaveFileUntagged = 1 << 18, AskUser = 1 << 20, SafestBestAction = 1 << 21, /// ready combinations for convenience PreserveEmbeddedProfile = UseEmbeddedProfile | KeepProfile, EmbeddedToWorkspace = UseEmbeddedProfile | ConvertToWorkspace, SRGBToWorkspace = UseSRGB | ConvertToWorkspace, AutoToWorkspace = AutomaticColors | ConvertToWorkspace, InputToWorkspace = UseDefaultInputProfile | ConvertToWorkspace, SpecifiedToWorkspace = UseSpecifiedProfile | ConvertToWorkspace, NoColorManagement = DoNotInterpret | LeaveFileUntagged }; Q_DECLARE_FLAGS(Behavior, BehaviorEnum) public: explicit ICCSettingsContainer(); ~ICCSettingsContainer() {}; void readFromConfig(KConfigGroup& group); - void writeToConfig(KConfigGroup& group) const; - void writeManagedViewToConfig(KConfigGroup& group) const; - void writeManagedPreviewsToConfig(KConfigGroup& group) const; + void writeToConfig(KConfigGroup& group) const; + void writeManagedViewToConfig(KConfigGroup& group) const; + void writeManagedPreviewsToConfig(KConfigGroup& group) const; public: bool enableCM; QString iccFolder; QString workspaceProfile; Behavior defaultMismatchBehavior; Behavior defaultMissingProfileBehavior; Behavior defaultUncalibratedBehavior; Behavior lastMismatchBehavior; Behavior lastMissingProfileBehavior; Behavior lastUncalibratedBehavior; QString lastSpecifiedAssignProfile; QString lastSpecifiedInputProfile; bool useManagedView; bool useManagedPreviews; QString monitorProfile; QString defaultInputProfile; QString defaultProofProfile; bool useBPC; int renderingIntent; - // Settings specific for soft proofing + /// Settings specific for soft proofing int proofingRenderingIntent; int doGamutCheck; QColor gamutCheckMaskColor; }; } // namespace Digikam Q_DECLARE_OPERATORS_FOR_FLAGS(Digikam::ICCSettingsContainer::Behavior) #endif // DIGIKAM_ICC_SETTINGS_CONTAINER_H diff --git a/core/libs/dimg/filters/icc/icctransform.cpp b/core/libs/dimg/filters/icc/icctransform.cpp index 8c3fab262d..3530ee6da4 100644 --- a/core/libs/dimg/filters/icc/icctransform.cpp +++ b/core/libs/dimg/filters/icc/icctransform.cpp @@ -1,787 +1,793 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-11-18 * Description : a class to apply ICC color correction to image. * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2009 by Marcel Wiesweg * Copyright (C) 2005-2020 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 "icctransform.h" #include "digikam-lcms.h" // Qt includes #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "dimgloaderobserver.h" namespace Digikam { class Q_DECL_HIDDEN TransformDescription { public: TransformDescription() + : inputFormat(0), + outputFormat(0), + intent(INTENT_PERCEPTUAL), + transformFlags(0), + proofIntent(INTENT_ABSOLUTE_COLORIMETRIC) { - inputFormat = 0; - outputFormat = 0; - intent = INTENT_PERCEPTUAL; - transformFlags = 0; - proofIntent = INTENT_ABSOLUTE_COLORIMETRIC; } bool operator==(const TransformDescription& other) const { - return inputProfile == other.inputProfile && - inputFormat == other.inputFormat && - outputProfile == other.outputProfile && - outputFormat == other.outputFormat && - intent == other.intent && - transformFlags == other.transformFlags && - proofProfile == other.proofProfile && - proofIntent == other.proofIntent; + return ( + (inputProfile == other.inputProfile) && + (inputFormat == other.inputFormat) && + (outputProfile == other.outputProfile) && + (outputFormat == other.outputFormat) && + (intent == other.intent) && + (transformFlags == other.transformFlags) && + (proofProfile == other.proofProfile) && + (proofIntent == other.proofIntent) + ); } public: IccProfile inputProfile; int inputFormat; IccProfile outputProfile; int outputFormat; int intent; int transformFlags; IccProfile proofProfile; int proofIntent; }; class Q_DECL_HIDDEN IccTransform::Private : public QSharedData { public: explicit Private() + : intent(IccTransform::Perceptual), + proofIntent(IccTransform::AbsoluteColorimetric), + useBPC(false), + checkGamut(false), + doNotEmbed(false), + checkGamutColor(QColor(126, 255, 255)), + handle(nullptr) { - intent = IccTransform::Perceptual; - proofIntent = IccTransform::AbsoluteColorimetric; - useBPC = false; - checkGamut = false; - doNotEmbed = false; - checkGamutColor = QColor(126, 255, 255); - handle = nullptr; } explicit Private(const Private& other) - : QSharedData(other) + : QSharedData(other), + handle(nullptr) { - handle = nullptr; operator=(other); } Private& operator=(const Private& other) { // Attention: This is sensitive. Add any new members here. // We can't use the default operator= because of handle. + intent = other.intent; proofIntent = other.proofIntent; useBPC = other.useBPC; checkGamut = other.checkGamut; doNotEmbed = other.doNotEmbed; checkGamutColor = other.checkGamutColor; embeddedProfile = other.embeddedProfile; inputProfile = other.inputProfile; outputProfile = other.outputProfile; proofProfile = other.proofProfile; builtinProfile = other.builtinProfile; close(); handle = nullptr; currentDescription = TransformDescription(); return *this; } ~Private() { close(); } void close() { if (handle) { currentDescription = TransformDescription(); LcmsLock lock; dkCmsDeleteTransform(handle); - handle = nullptr; + handle = nullptr; } } IccProfile& sRGB() { if (builtinProfile.isNull()) { builtinProfile = IccProfile::sRGB(); } return builtinProfile; } IccProfile& effectiveInputProfile() { if (!embeddedProfile.isNull()) { return embeddedProfile; } else if (!inputProfile.isNull()) { return inputProfile; } else { return sRGB(); } } IccProfile effectiveInputProfileConst() const { if (!embeddedProfile.isNull()) { return embeddedProfile; } else if (!inputProfile.isNull()) { return inputProfile; } else { return IccProfile::sRGB(); } } public: IccTransform::RenderingIntent intent; IccTransform::RenderingIntent proofIntent; bool useBPC; bool checkGamut; bool doNotEmbed; QColor checkGamutColor; IccProfile embeddedProfile; IccProfile inputProfile; IccProfile outputProfile; IccProfile proofProfile; IccProfile builtinProfile; cmsHTRANSFORM handle; TransformDescription currentDescription; }; IccTransform::IccTransform() : d(new Private) { } IccTransform::IccTransform(const IccTransform& other) : d(other.d) { } IccTransform& IccTransform::operator=(const IccTransform& other) { d = other.d; + return *this; } IccTransform::~IccTransform() { // close() is done in ~Private } void IccTransform::init() { LcmsLock lock; dkCmsErrorAction(LCMS_ERROR_SHOW); } void IccTransform::setInputProfile(const IccProfile& profile) { if (profile == d->inputProfile) { return; } close(); d->inputProfile = profile; } void IccTransform::setEmbeddedProfile(const DImg& image) { IccProfile profile = image.getIccProfile(); if (profile == d->embeddedProfile) { return; } close(); d->embeddedProfile = profile; } void IccTransform::setOutputProfile(const IccProfile& profile) { if (profile == d->outputProfile) { return; } close(); d->outputProfile = profile; } void IccTransform::setProofProfile(const IccProfile& profile) { if (profile == d->proofProfile) { return; } close(); d->proofProfile = profile; } IccProfile IccTransform::embeddedProfile() const { return d->embeddedProfile; } IccProfile IccTransform::inputProfile() const { return d->inputProfile; } IccProfile IccTransform::outputProfile() const { return d->outputProfile; } IccProfile IccTransform::proofProfile() const { return d->proofProfile; } IccProfile IccTransform::effectiveInputProfile() const { return d->effectiveInputProfileConst(); } void IccTransform::setIntent(RenderingIntent intent) { if (intent == d->intent) { return; } d->intent = intent; close(); } void IccTransform::setProofIntent(RenderingIntent intent) { if (intent == d->proofIntent) { return; } d->proofIntent = intent; close(); } void IccTransform::setUseBlackPointCompensation(bool useBPC) { if (d->useBPC == useBPC) { return; } close(); d->useBPC = useBPC; } void IccTransform::setCheckGamut(bool checkGamut) { if (d->checkGamut == checkGamut) { return; } close(); d->checkGamut = checkGamut; } void IccTransform::setCheckGamutMaskColor(const QColor& color) { d->checkGamutColor = color; } IccTransform::RenderingIntent IccTransform::intent() const { return d->intent; } IccTransform::RenderingIntent IccTransform::proofIntent() const { return d->proofIntent; } bool IccTransform::isUsingBlackPointCompensation() const { return d->useBPC; } bool IccTransform::isCheckingGamut() const { return d->checkGamut; } QColor IccTransform::checkGamutMaskColor() const { return d->checkGamutColor; } void IccTransform::setDoNotEmbedOutputProfile(bool doNotEmbed) { d->doNotEmbed = doNotEmbed; } /* void IccTransform::readFromConfig() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QString("Color Management")); int intent = group.readEntry("RenderingIntent", 0); bool useBPC = group.readEntry("BPCAlgorithm", false); setIntent(intent); setUseBlackPointCompensation(useBPC); } */ bool IccTransform::willHaveEffect() { if (d->outputProfile.isNull()) { return false; } return !d->effectiveInputProfile().isSameProfileAs(d->outputProfile); } static int renderingIntentToLcmsIntent(IccTransform::RenderingIntent intent) { switch (intent) { case IccTransform::Perceptual: return INTENT_PERCEPTUAL; case IccTransform::RelativeColorimetric: return INTENT_RELATIVE_COLORIMETRIC; case IccTransform::Saturation: return INTENT_SATURATION; case IccTransform::AbsoluteColorimetric: return INTENT_ABSOLUTE_COLORIMETRIC; default: return INTENT_PERCEPTUAL; } } TransformDescription IccTransform::getDescription(const DImg& image) { TransformDescription description; description.inputProfile = d->effectiveInputProfile(); description.outputProfile = d->outputProfile; description.intent = renderingIntentToLcmsIntent(d->intent); if (d->useBPC) { description.transformFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION; } LcmsLock lock; // Do not use TYPE_BGR_ - this implies 3 bytes per pixel, but even if !image.hasAlpha(), // our image data has 4 bytes per pixel with the fourth byte filled with 0xFF. + if (image.sixteenBit()) { /* switch (dkCmsGetColorSpace(description.inputProfile)) { case icSigGrayData: description.inputFormat = TYPE_GRAYA_16; break; case icSigCmykData: description.inputFormat = TYPE_CMYK_16; break; default: description.inputFormat = TYPE_BGRA_16; } */ // A Dimg is always BGRA, converted by the loader + description.inputFormat = TYPE_BGRA_16; description.outputFormat = TYPE_BGRA_16; } else { description.inputFormat = TYPE_BGRA_8; description.outputFormat = TYPE_BGRA_8; } return description; } TransformDescription IccTransform::getDescription(const QImage&) { TransformDescription description; description.inputProfile = d->effectiveInputProfile(); description.outputProfile = d->outputProfile; description.intent = renderingIntentToLcmsIntent(d->intent); if (d->useBPC) { description.transformFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION; } description.inputFormat = TYPE_BGRA_8; description.outputFormat = TYPE_BGRA_8; return description; } TransformDescription IccTransform::getProofingDescription(const DImg& image) { TransformDescription description = getDescription(image); - description.proofProfile = d->proofProfile; - description.proofIntent = renderingIntentToLcmsIntent(d->proofIntent); + description.proofProfile = d->proofProfile; + description.proofIntent = renderingIntentToLcmsIntent(d->proofIntent); - description.transformFlags |= cmsFLAGS_SOFTPROOFING; + description.transformFlags |= cmsFLAGS_SOFTPROOFING; if (d->checkGamut) { dkCmsSetAlarmCodes(d->checkGamutColor.red(), d->checkGamutColor.green(), d->checkGamutColor.blue()); description.transformFlags |= cmsFLAGS_GAMUTCHECK; } return description; } bool IccTransform::open(TransformDescription& description) { if (d->handle) { if (d->currentDescription == description) { return true; } else { close(); } } d->currentDescription = description; LcmsLock lock; d->handle = dkCmsCreateTransform(description.inputProfile, description.inputFormat, description.outputProfile, description.outputFormat, description.intent, description.transformFlags); if (!d->handle) { qCDebug(DIGIKAM_DIMG_LOG) << "LCMS internal error: cannot create a color transform instance"; return false; } return true; } bool IccTransform::openProofing(TransformDescription& description) { if (d->handle) { if (d->currentDescription == description) { return true; } else { close(); } } d->currentDescription = description; LcmsLock lock; d->handle = dkCmsCreateProofingTransform(description.inputProfile, description.inputFormat, description.outputProfile, description.outputFormat, description.proofProfile, description.intent, description.proofIntent, description.transformFlags); if (!d->handle) { qCDebug(DIGIKAM_DIMG_LOG) << "LCMS internal error: cannot create a color transform instance"; return false; } return true; } bool IccTransform::checkProfiles() { if (!d->effectiveInputProfile().open()) { qCDebug(DIGIKAM_DIMG_LOG) << "Cannot open embedded profile"; return false; } if (!d->outputProfile.open()) { qCDebug(DIGIKAM_DIMG_LOG) << "Cannot open output profile"; return false; } if (!d->proofProfile.isNull()) { if (!d->proofProfile.open()) { qCDebug(DIGIKAM_DIMG_LOG) << "Cannot open proofing profile"; return false; } } return true; } bool IccTransform::apply(DImg& image, DImgLoaderObserver* const observer) { if (!willHaveEffect()) { if (!d->outputProfile.isNull() && !d->doNotEmbed) { image.setIccProfile(d->outputProfile); } return true; } if (!checkProfiles()) { return false; } TransformDescription description; if (d->proofProfile.isNull()) { description = getDescription(image); if (!open(description)) { return false; } } else { description = getProofingDescription(image); if (!openProofing(description)) { return false; } } if (observer) { observer->progressInfo(0.1F); } transform(image, description, observer); if (!d->doNotEmbed) { image.setIccProfile(d->outputProfile); } // if this was a RAW color image, it is no more image.removeAttribute(QLatin1String("uncalibratedColor")); return true; } bool IccTransform::apply(QImage& qimage) { - if (qimage.format() != QImage::Format_RGB32 && - qimage.format() != QImage::Format_ARGB32 && - qimage.format() != QImage::Format_ARGB32_Premultiplied) + if ((qimage.format() != QImage::Format_RGB32) && + (qimage.format() != QImage::Format_ARGB32) && + (qimage.format() != QImage::Format_ARGB32_Premultiplied)) { qCDebug(DIGIKAM_DIMG_LOG) << "Unsupported QImage format" << qimage.format(); return false; } if (!willHaveEffect()) { return true; } if (!checkProfiles()) { return false; } TransformDescription description; description = getDescription(qimage); if (!open(description)) { return false; } transform(qimage, description); return true; } void IccTransform::transform(DImg& image, const TransformDescription& description, DImgLoaderObserver* const observer) { const int bytesDepth = image.bytesDepth(); const int pixels = image.width() * image.height(); // convert ten scanlines in a batch const int pixelsPerStep = image.width() * 10; uchar* data = image.bits(); // see dimgloader.cpp, granularity(). int granularity = 1; if (observer) { granularity = (int)((pixels / (20 * 0.9)) / observer->granularity()); } int checkPoint = pixels; // it is safe to use the same input and output buffer if the format is the same if (description.inputFormat == description.outputFormat) { - for (int p = pixels; p > 0; p -= pixelsPerStep) + for (int p = pixels ; p > 0 ; p -= pixelsPerStep) { int pixelsThisStep = qMin(p, pixelsPerStep); int size = pixelsThisStep * bytesDepth; LcmsLock lock; dkCmsDoTransform(d->handle, data, data, pixelsThisStep); data += size; - if (observer && p <= checkPoint) + if (observer && (p <= checkPoint)) { checkPoint -= granularity; observer->progressInfo(0.1 + 0.9 * (1.0 - float(p) / float(pixels))); } } } else { QVarLengthArray buffer(pixelsPerStep * bytesDepth); - for (int p = pixels; p > 0; p -= pixelsPerStep) + for (int p = pixels ; p > 0 ; p -= pixelsPerStep) { int pixelsThisStep = qMin(p, pixelsPerStep); int size = pixelsThisStep * bytesDepth; LcmsLock lock; memcpy(buffer.data(), data, size); dkCmsDoTransform(d->handle, buffer.data(), data, pixelsThisStep); data += size; - if (observer && p <= checkPoint) + if (observer && (p <= checkPoint)) { checkPoint -= granularity; observer->progressInfo(0.1 + 0.9 * (1.0 - float(p) / float(pixels))); } } } } void IccTransform::transform(QImage& image, const TransformDescription&) { const int bytesDepth = 4; const int pixels = image.width() * image.height(); // convert ten scanlines in a batch const int pixelsPerStep = image.width() * 10; uchar* data = image.bits(); - for (int p = pixels; p > 0; p -= pixelsPerStep) + for (int p = pixels ; p > 0 ; p -= pixelsPerStep) { int pixelsThisStep = qMin(p, pixelsPerStep); int size = pixelsThisStep * bytesDepth; LcmsLock lock; dkCmsDoTransform(d->handle, data, data, pixelsThisStep); data += size; } } void IccTransform::close() { d->close(); } /* void IccTransform::closeProfiles() { d->inputProfile.close(); d->outputProfile.close(); d->proofProfile.close(); d->embeddedProfile.close(); } */ } // namespace Digikam diff --git a/core/libs/dimg/filters/icc/icctransform.h b/core/libs/dimg/filters/icc/icctransform.h index a773d6cb48..0f2c9ea19c 100644 --- a/core/libs/dimg/filters/icc/icctransform.h +++ b/core/libs/dimg/filters/icc/icctransform.h @@ -1,171 +1,186 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-11-18 * Description : a class to apply ICC color correction to image. * * Copyright (C) 2005-2006 by F.J. Cruz * Copyright (C) 2009 by Marcel Wiesweg * Copyright (C) 2005-2020 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_TRANSFORM_H #define DIGIKAM_ICC_TRANSFORM_H // Qt includes #include #include #include // Local includes #include "dimg.h" #include "digikam_export.h" class QImage; namespace Digikam { class DImgLoaderObserver; class TransformDescription; class DIGIKAM_EXPORT IccTransform { public: enum RenderingIntent { Perceptual = 0, RelativeColorimetric = 1, Saturation = 2, AbsoluteColorimetric = 3 }; public: explicit IccTransform(); ~IccTransform(); IccTransform(const IccTransform& other); IccTransform& operator=(const IccTransform& other); /** * Apply this transform with the set profiles and options to the image. * Optionally pass an observer to get progress information. */ bool apply(DImg& image, DImgLoaderObserver* const observer = nullptr); - /// Apply this transform to the QImage. This has only basic functionality. + /** + * Apply this transform to the QImage. This has only basic functionality. + */ bool apply(QImage& qimage); - /// Closes the transform, not the profiles. Called at desctruction. + /** + * Closes the transform, not the profiles. Called at desctruction. + */ void close(); /** * Sets the input profiles of this transform. * You can call both setEmbeddedProfile and setInputProfile. * If the image contains an embedded profile this profile is used * and takes precedence over the set input profile, which is used * without an embedded profile. If none is set, sRGB is used. */ void setEmbeddedProfile(const DImg& image); void setInputProfile(const IccProfile& profile); - /// Sets the output transform + /** + * Sets the output transform + */ void setOutputProfile(const IccProfile& profile); - /// Makes this transform a proofing transform, if profile is not null + /** + * Makes this transform a proofing transform, if profile is not null + */ void setProofProfile(const IccProfile& profile); /** * Call this with 'true' if you do not want the output profile * to be set as embedded profile after apply() did a transformation. * Default is to set the output profile as embedded profile (false). */ void setDoNotEmbedOutputProfile(bool doNotEmbed); - /// Set options + /** + * Set options + */ void setIntent(RenderingIntent intent); void setIntent(int intent) { setIntent((RenderingIntent)intent); } void setProofIntent(RenderingIntent intent); void setProofIntent(int intent) { setProofIntent((RenderingIntent)intent); } void setUseBlackPointCompensation(bool useBPC); void setCheckGamut(bool checkGamut); void setCheckGamutMaskColor(const QColor& color); - /// Returns the contained profiles + /** + * Returns the contained profiles + */ IccProfile embeddedProfile() const; IccProfile inputProfile() const; IccProfile outputProfile() const; IccProfile proofProfile() const; RenderingIntent intent() const; RenderingIntent proofIntent() const; bool isUsingBlackPointCompensation() const; bool isCheckingGamut() const; QColor checkGamutMaskColor() const; /** * Returns if this transformation will have an effect, i.e. if * effective input profile and output profile are different. */ bool willHaveEffect(); /** - * Returns the embedded profile; if none is set, the input profile; - * if none is set, sRGB. + * Returns the embedded profile; if none is set, the input profile; + * if none is set, sRGB. */ - IccProfile effectiveInputProfile() const; + IccProfile effectiveInputProfile() const; - /// Initialize LittleCMS library + /** + * Initialize LittleCMS library + */ static void init(); private: bool checkProfiles(); TransformDescription getDescription(const DImg& image); TransformDescription getProofingDescription(const DImg& image); TransformDescription getDescription(const QImage& image); bool open(TransformDescription& description); bool openProofing(TransformDescription& description); - void transform(DImg& img, const TransformDescription&, DImgLoaderObserver* const observer = nullptr); + void transform(DImg& img, const TransformDescription&, + DImgLoaderObserver* const observer = nullptr); void transform(QImage& img, const TransformDescription&); public: class Private; private: QSharedDataPointer d; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::IccTransform) #endif // DIGIKAM_ICC_TRANSFORM_H diff --git a/core/libs/dimg/filters/icc/icctransformfilter.cpp b/core/libs/dimg/filters/icc/icctransformfilter.cpp index 29baf294ef..2e4a66fd96 100644 --- a/core/libs/dimg/filters/icc/icctransformfilter.cpp +++ b/core/libs/dimg/filters/icc/icctransformfilter.cpp @@ -1,131 +1,135 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-09-19 * Description : ICC Transform threaded image filter. * * Copyright (C) 2009-2010 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 "icctransformfilter.h" // C++ includes #include // KDE includes #include // Local includes #include "dimg.h" #include "iccsettings.h" namespace Digikam { IccTransformFilter::IccTransformFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } IccTransformFilter::IccTransformFilter(DImg* const orgImage, QObject* const parent, const IccTransform& transform) : DImgThreadedFilter(orgImage, parent, QLatin1String("ICC Transform")), m_transform(transform) { initFilter(); } IccTransformFilter::~IccTransformFilter() { cancelFilter(); } QString IccTransformFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Color Profile Conversion")); } void IccTransformFilter::filterImage() { m_destImage = m_orgImage; m_transform.apply(m_destImage, this); m_destImage.setIccProfile(m_transform.outputProfile()); } void IccTransformFilter::progressInfo(float progress) { postProgress(lround(progress * 100)); } FilterAction IccTransformFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.setParameter(QLatin1String("renderingIntent"), m_transform.intent()); action.setParameter(QLatin1String("blackPointCompensation"), m_transform.isUsingBlackPointCompensation()); action.setParameter(QLatin1String("inputProfileDescription"), m_transform.effectiveInputProfile().description()); action.setParameter(QLatin1String("outputProfileDescription"), m_transform.outputProfile().description()); return action; } void IccTransformFilter::readParameters(const Digikam::FilterAction& action) { m_transform = IccTransform(); m_transform.setIntent((IccTransform::RenderingIntent)action.parameter(QLatin1String("renderingIntent")).toInt()); m_transform.setUseBlackPointCompensation(action.parameter(QLatin1String("blackPointCompensation")).toBool()); QList profiles; - profiles = IccSettings::instance()->profilesForDescription(action.parameter(QLatin1String("inputProfileDescription")).toString()); + profiles = IccSettings::instance()->profilesForDescription(action.parameter(QLatin1String("inputProfileDescription")).toString()); if (!profiles.isEmpty()) { m_transform.setInputProfile(profiles.first()); } profiles = IccSettings::instance()->profilesForDescription(action.parameter(QLatin1String("outputProfileDescription")).toString()); if (!profiles.isEmpty()) { m_transform.setOutputProfile(profiles.first()); } } bool IccTransformFilter::parametersSuccessfullyRead() const { - return !m_transform.inputProfile().isNull() && !m_transform.outputProfile().isNull(); + return (!m_transform.inputProfile().isNull() && !m_transform.outputProfile().isNull()); } QString IccTransformFilter::readParametersError(const FilterAction& actionThatFailed) const { if (m_transform.inputProfile().isNull()) + { return i18n("Input color profile \"%1\" not available", actionThatFailed.parameter(QLatin1String("inputProfileDescription")).toString()); + } if (m_transform.outputProfile().isNull()) + { return i18n("Output color profile \"%1\" not available", actionThatFailed.parameter(QLatin1String("outputProfileDescription")).toString()); + } return QString(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/icc/icctransformfilter.h b/core/libs/dimg/filters/icc/icctransformfilter.h index 7eb6306d9c..6a12ed2b88 100644 --- a/core/libs/dimg/filters/icc/icctransformfilter.h +++ b/core/libs/dimg/filters/icc/icctransformfilter.h @@ -1,86 +1,86 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-09-19 * Description : ICC Transform threaded image filter. * * Copyright (C) 2009-2010 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_TRANSFORM_FILTER_H #define DIGIKAM_ICC_TRANSFORM_FILTER_H // Local includes #include "dimgloaderobserver.h" #include "dimgthreadedfilter.h" #include "icctransform.h" namespace Digikam { class DIGIKAM_EXPORT IccTransformFilter : public DImgThreadedFilter, public DImgLoaderObserver { public: explicit IccTransformFilter(QObject* const parent = nullptr); explicit IccTransformFilter(DImg* const orgImage, QObject* const parent, const IccTransform& transform); ~IccTransformFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:IccTransformFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; void readParameters(const FilterAction& action) override; bool parametersSuccessfullyRead() const override; QString readParametersError(const FilterAction& actionThatFailed) const override; protected: - virtual void progressInfo(float progress) override; - virtual void filterImage() override; + virtual void progressInfo(float progress) override; + virtual void filterImage() override; private: IccTransform m_transform; }; } // namespace Digikam #endif // DIGIKAM_ICC_TRANSFORM_FILTER_H