diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 080a76410..d6fa934e3 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -117,6 +117,7 @@ set(RTENGINESOURCEFILES tmo_fattal02.cc iplocalcontrast.cc histmatching.cc + gamutwarning.cc ) if(LENSFUN_HAS_LOAD_DIRECTORY) diff --git a/rtengine/gamutwarning.cc b/rtengine/gamutwarning.cc new file mode 100644 index 000000000..a52f08429 --- /dev/null +++ b/rtengine/gamutwarning.cc @@ -0,0 +1,125 @@ +/* -*- 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" + +namespace rtengine { + +GamutWarning::GamutWarning(cmsHPROFILE iprof, cmsHPROFILE gamutprof, bool gamutbpc): + lab2ref(nullptr), + lab2softproof(nullptr), + softproof2ref(nullptr) +{ + if (cmsIsMatrixShaper(gamutprof)) { + 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..2e21883d4 --- /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, 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 605a578ea..c50c4f6f5 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -263,15 +263,6 @@ ImProcFunctions::~ImProcFunctions () if (monitorTransform) { cmsDeleteTransform (monitorTransform); } - if (gw_softproof2refTransform) { - cmsDeleteTransform(gw_softproof2refTransform); - } - if (gw_lab2refTransform) { - cmsDeleteTransform(gw_lab2refTransform); - } - if (gw_lab2softproofTransform) { - cmsDeleteTransform(gw_lab2softproofTransform); - } } void ImProcFunctions::setScale (double iscale) @@ -280,33 +271,15 @@ void ImProcFunctions::setScale (double iscale) } -static void cms_log_handler(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) -{ - std::cout << "\n*** LCMS ERROR: " << ErrorCode << ": " << Text << "\n" << std::endl; -} - void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck) { // set up monitor transform if (monitorTransform) { cmsDeleteTransform (monitorTransform); } - if (gw_softproof2refTransform) { - cmsDeleteTransform(gw_softproof2refTransform); - } - if (gw_lab2refTransform) { - cmsDeleteTransform(gw_lab2refTransform); - } - if (gw_lab2softproofTransform) { - cmsDeleteTransform(gw_lab2softproofTransform); - } - - cmsSetLogErrorHandler(&cms_log_handler); + gamutWarning.reset(nullptr); monitorTransform = nullptr; - gw_softproof2refTransform = nullptr; - gw_lab2refTransform = nullptr; - gw_lab2softproofTransform = nullptr; cmsHPROFILE monitor = nullptr; @@ -402,30 +375,7 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, } if (gamutCheck) { - if (cmsIsMatrixShaper(gamutprof)) { - cmsHPROFILE aces = ICCStore::getInstance()->getProfile("ACES"); - if (aces) { - gw_lab2refTransform = cmsCreateTransform(iprof, TYPE_Lab_FLT, aces, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); - gw_lab2softproofTransform = cmsCreateTransform(iprof, TYPE_Lab_FLT, gamutprof, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); - gw_softproof2refTransform = cmsCreateTransform(gamutprof, TYPE_RGB_FLT, aces, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE | gamutbpc); - } - } else { - gw_lab2refTransform = nullptr; - gw_lab2softproofTransform = cmsCreateTransform(iprof, TYPE_Lab_FLT, gamutprof, TYPE_RGB_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE); - gw_softproof2refTransform = cmsCreateTransform(gamutprof, TYPE_RGB_FLT, iprof, TYPE_Lab_FLT, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE | gamutbpc); - } - - if (!gw_softproof2refTransform) { - if (gw_lab2softproofTransform) { - cmsDeleteTransform(gw_lab2softproofTransform); - gw_lab2softproofTransform = nullptr; - } - } else if (!gw_lab2softproofTransform) { - if (gw_softproof2refTransform) { - cmsDeleteTransform(gw_softproof2refTransform); - gw_softproof2refTransform = nullptr; - } - } + gamutWarning.reset(new GamutWarning(iprof, gamutprof, gamutbpc)); } cmsCloseProfile (iprof); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 2af71e005..0627b19f5 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,9 +46,7 @@ class ImProcFunctions cmsHTRANSFORM monitorTransform; - cmsHTRANSFORM gw_lab2refTransform; - cmsHTRANSFORM gw_lab2softproofTransform; - cmsHTRANSFORM gw_softproof2refTransform; + std::unique_ptr gamutWarning; const ProcParams* params; double scale; @@ -198,7 +197,7 @@ public: double lumimul[3]; ImProcFunctions (const ProcParams* iparams, bool imultiThread = true) - : monitorTransform (nullptr), gw_lab2refTransform(nullptr), gw_lab2softproofTransform(nullptr), gw_softproof2refTransform(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 0555053f7..a8e35bd18 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -46,14 +46,6 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) int H = lab->H; unsigned char * data = image->data; - const auto set_gamut_warning = - [](Image8 *image, int y, int x) -> void - { - image->r(y, x) = 0; - image->g(y, x) = 255; - image->b(y, x) = 255; - }; - // cmsDoTransform is relatively expensive #ifdef _OPENMP #pragma omp parallel firstprivate(lab, data, W, H) @@ -63,7 +55,7 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) AlignedBuffer gwBuf1; AlignedBuffer gwBuf2; - if (gw_softproof2refTransform) { + if (gamutWarning) { gwBuf1.resize(3 * lab->W); gwBuf2.resize(3 * lab->W); } @@ -91,38 +83,8 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) cmsDoTransform (monitorTransform, buffer, data + ix, W); - if (gw_softproof2refTransform) { - float delta_max = gw_lab2refTransform ? 0.0001f : 4.9999f; - cmsDoTransform(gw_lab2softproofTransform, buffer, gwBuf2.data, W); - - cmsDoTransform(gw_softproof2refTransform, gwBuf2.data, gwBuf1.data, W); - float *proofdata = gwBuf1.data; - float *refdata = buffer; - if (gw_lab2refTransform) { - cmsDoTransform(gw_lab2refTransform, buffer, gwBuf2.data, W); - refdata = gwBuf2.data; - - int iy = 0; - for (int j = 0; j < W; ++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) { - set_gamut_warning(image, i, j); - } - } - } else { - int iy = 0; - for (int j = 0; j < W; ++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) { - set_gamut_warning(image, i, j); - } - } - } + if (gamutWarning) { + gamutWarning->markLine(image, i, buffer, gwBuf1.data, gwBuf2.data); } } } // End of parallelization