diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index db0411e6c..0693f919f 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -119,6 +119,7 @@ set(RTENGINESOURCEFILES iplocalcontrast.cc histmatching.cc pdaflinesfilter.cc + gamutwarning.cc ) if(LENSFUN_HAS_LOAD_DIRECTORY) diff --git a/rtengine/gamutwarning.cc b/rtengine/gamutwarning.cc new file mode 100644 index 000000000..dde784db5 --- /dev/null +++ b/rtengine/gamutwarning.cc @@ -0,0 +1,126 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2018 Alberto Griggio + * + * RawTherapee 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 3 of the License, or + * (at your option) any later version. + * + * RawTherapee 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. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +/** + * Adapted from PhotoFlow, Copyright (C) 2014 Ferrero Andrea + * also distributed under the GPL V3+ + */ + +#include "gamutwarning.h" +#include + +namespace rtengine { + +GamutWarning::GamutWarning(cmsHPROFILE iprof, cmsHPROFILE gamutprof, RenderingIntent intent, bool gamutbpc): + lab2ref(nullptr), + lab2softproof(nullptr), + softproof2ref(nullptr) +{ + if (cmsIsMatrixShaper(gamutprof) && !cmsIsCLUT(gamutprof, intent, LCMS_USED_AS_OUTPUT)) { + cmsHPROFILE aces = ICCStore::getInstance()->getProfile("ACES"); + if (aces) { + lab2ref = cmsCreateTransform(iprof, TYPE_Lab_FLT, aces, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); + lab2softproof = cmsCreateTransform(iprof, TYPE_Lab_FLT, gamutprof, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); + softproof2ref = cmsCreateTransform(gamutprof, TYPE_RGB_FLT, aces, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE | gamutbpc); + } + } else { + lab2ref = nullptr; + lab2softproof = cmsCreateTransform(iprof, TYPE_Lab_FLT, gamutprof, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); + softproof2ref = cmsCreateTransform(gamutprof, TYPE_RGB_FLT, iprof, TYPE_Lab_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE | gamutbpc); + } + + if (!softproof2ref) { + if (lab2softproof) { + cmsDeleteTransform(lab2softproof); + lab2softproof = nullptr; + } + } else if (!lab2softproof) { + if (softproof2ref) { + cmsDeleteTransform(softproof2ref); + softproof2ref = nullptr; + } + } +} + + +GamutWarning::~GamutWarning() +{ + if (softproof2ref) { + cmsDeleteTransform(softproof2ref); + } + if (lab2ref) { + cmsDeleteTransform(lab2ref); + } + if (lab2softproof) { + cmsDeleteTransform(lab2softproof); + } +} + + +void GamutWarning::markLine(Image8 *image, int y, float *srcbuf, float *buf1, float *buf2) +{ + if (softproof2ref) { + const int width = image->getWidth(); + + float delta_max = lab2ref ? 0.0001f : 4.9999f; + cmsDoTransform(lab2softproof, srcbuf, buf2, width); + cmsDoTransform(softproof2ref, buf2, buf1, width); + + float *proofdata = buf1; + float *refdata = srcbuf; + + if (lab2ref) { + cmsDoTransform(lab2ref, srcbuf, buf2, width); + refdata = buf2; + + int iy = 0; + for (int j = 0; j < width; ++j) { + float delta = max(std::abs(proofdata[iy] - refdata[iy]), std::abs(proofdata[iy+1] - refdata[iy+1]), std::abs(proofdata[iy+2] - refdata[iy+2])); + iy += 3; + + if (delta > delta_max) { + mark(image, y, j); + } + } + } else { + int iy = 0; + for (int j = 0; j < width; ++j) { + cmsCIELab lab1 = { proofdata[iy], proofdata[iy+1], proofdata[iy+2] }; + cmsCIELab lab2 = { refdata[iy], refdata[iy+1], refdata[iy+2] }; + iy += 3; + float delta = cmsDeltaE(&lab1, &lab2); + if (delta > delta_max) { + mark(image, y, j); + } + } + } + } +} + + +inline void GamutWarning::mark(Image8 *image, int y, int x) +{ + image->r(y, x) = 0; + image->g(y, x) = 255; + image->b(y, x) = 255; +} + + +} // namespace rtengine diff --git a/rtengine/gamutwarning.h b/rtengine/gamutwarning.h new file mode 100644 index 000000000..be6d7b61b --- /dev/null +++ b/rtengine/gamutwarning.h @@ -0,0 +1,48 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2018 Alberto Griggio + * + * RawTherapee 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 3 of the License, or + * (at your option) any later version. + * + * RawTherapee 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. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +/** + * Adapted from PhotoFlow, Copyright (C) 2014 Ferrero Andrea + * also distributed under the GPL V3+ + */ + +#pragma once + +#include "iccstore.h" +#include "noncopyable.h" +#include "image8.h" + +namespace rtengine { + +class GamutWarning: public NonCopyable { +public: + GamutWarning(cmsHPROFILE iprof, cmsHPROFILE gamutprof, RenderingIntent intent, bool bpc); + ~GamutWarning(); + void markLine(Image8 *image, int y, float *srcbuf, float *buf1, float *buf2); + +private: + void mark(Image8 *image, int i, int j); + + cmsHTRANSFORM lab2ref; + cmsHTRANSFORM lab2softproof; + cmsHTRANSFORM softproof2ref; +}; + +} // namespace rtengine diff --git a/rtengine/iccstore.h b/rtengine/iccstore.h index bdb15d2c0..4604404fb 100644 --- a/rtengine/iccstore.h +++ b/rtengine/iccstore.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 27d7e8f2e..a55624218 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -260,7 +260,6 @@ extern const Settings* settings; ImProcFunctions::~ImProcFunctions () { - if (monitorTransform) { cmsDeleteTransform (monitorTransform); } @@ -271,12 +270,14 @@ void ImProcFunctions::setScale (double iscale) scale = iscale; } + void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck) { // set up monitor transform if (monitorTransform) { cmsDeleteTransform (monitorTransform); } + gamutWarning.reset(nullptr); monitorTransform = nullptr; @@ -295,6 +296,9 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, cmsUInt32Number flags; cmsHPROFILE iprof = cmsCreateLab4Profile (nullptr); + cmsHPROFILE gamutprof = nullptr; + cmsUInt32Number gamutbpc = 0; + RenderingIntent gamutintent = RI_RELATIVE; bool softProofCreated = false; @@ -321,9 +325,9 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, if (oprof) { // NOCACHE is for thread safety, NOOPTIMIZE for precision - if (gamutCheck) { - flags |= cmsFLAGS_GAMUTCHECK; - } + // if (gamutCheck) { + // flags |= cmsFLAGS_GAMUTCHECK; + // } monitorTransform = cmsCreateProofingTransform ( iprof, TYPE_Lab_FLT, @@ -336,18 +340,31 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, if (monitorTransform) { softProofCreated = true; } + + if (gamutCheck) { + gamutprof = oprof; + if (params->icm.outputBPC) { + gamutbpc = cmsFLAGS_BLACKPOINTCOMPENSATION; + } + gamutintent = outIntent; + } } } else if (gamutCheck) { - flags = cmsFLAGS_GAMUTCHECK | cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE; + // flags = cmsFLAGS_GAMUTCHECK | cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE; + // if (settings->monitorBPC) { + // flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; + // } + + // monitorTransform = cmsCreateProofingTransform(iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_8, monitor, monitorIntent, monitorIntent, flags); + + // if (monitorTransform) { + // softProofCreated = true; + // } + gamutprof = monitor; if (settings->monitorBPC) { - flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; - } - - monitorTransform = cmsCreateProofingTransform(iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_8, monitor, monitorIntent, monitorIntent, flags); - - if (monitorTransform) { - softProofCreated = true; + gamutbpc = cmsFLAGS_BLACKPOINTCOMPENSATION; } + gamutintent = monitorIntent; } if (!softProofCreated) { @@ -360,6 +377,10 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, monitorTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_8, monitorIntent, flags); } + if (gamutCheck && gamutprof) { + gamutWarning.reset(new GamutWarning(iprof, gamutprof, gamutintent, gamutbpc)); + } + cmsCloseProfile (iprof); } } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 115270090..6fc98ef3f 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -34,6 +34,7 @@ #include "curves.h" #include "cplx_wavelet_dec.h" #include "pipettebuffer.h" +#include "gamutwarning.h" namespace rtengine { @@ -45,8 +46,7 @@ class ImProcFunctions cmsHTRANSFORM monitorTransform; - cmsHTRANSFORM lab2outputTransform; - cmsHTRANSFORM output2monitorTransform; + std::unique_ptr gamutWarning; const ProcParams* params; double scale; @@ -197,7 +197,7 @@ public: double lumimul[3]; ImProcFunctions (const ProcParams* iparams, bool imultiThread = true) - : monitorTransform (nullptr), lab2outputTransform (nullptr), output2monitorTransform (nullptr), params (iparams), scale (1), multiThread (imultiThread), lumimul{} {} + : monitorTransform (nullptr), params (iparams), scale (1), multiThread (imultiThread), lumimul{} {} ~ImProcFunctions (); bool needsLuminanceOnly() { return !(needsCA() || needsDistortion() || needsRotation() || needsPerspective() || needsLCP() || needsLensfun()) && (needsVignetting() || needsPCVignetting() || needsGradient());} void setScale (double iscale); diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index 16eb4f50e..a8e35bd18 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -52,6 +52,14 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) #endif { AlignedBuffer pBuf(3 * lab->W); + + AlignedBuffer gwBuf1; + AlignedBuffer gwBuf2; + if (gamutWarning) { + gwBuf1.resize(3 * lab->W); + gwBuf2.resize(3 * lab->W); + } + float *buffer = pBuf.data; #ifdef _OPENMP @@ -74,6 +82,10 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) } cmsDoTransform (monitorTransform, buffer, data + ix, W); + + if (gamutWarning) { + gamutWarning->markLine(image, i, buffer, gwBuf1.data, gwBuf2.data); + } } } // End of parallelization } else {