From 54cc02eea971c7002f19b16b1552729ee8acd4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Mon, 17 Jun 2019 08:03:46 +0200 Subject: [PATCH] Filmenegative core cleanup --- rtengine/filmnegativeproc.cc | 338 ++++++++++++++++++++-------------- rtengine/rawimage.cc | 2 +- rtengine/rawimage.h | 2 +- rtengine/rawimagesource.h | 1 - rtgui/filmnegative.cc | 343 ++++++++++++++++++----------------- rtgui/filmnegative.h | 80 ++++---- 6 files changed, 422 insertions(+), 344 deletions(-) diff --git a/rtengine/filmnegativeproc.cc b/rtengine/filmnegativeproc.cc index 871b7a1d9..51bd52365 100644 --- a/rtengine/filmnegativeproc.cc +++ b/rtengine/filmnegativeproc.cc @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2019 rom9 * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,15 +19,17 @@ #include #include -#include "rtengine.h" -#include "rawimagesource.h" -#include "mytime.h" -#include "procparams.h" #ifdef _OPENMP #include #endif + +#include "rawimagesource.h" + +#include "mytime.h" #include "opthelper.h" +#include "procparams.h" #include "rt_algo.h" +#include "rtengine.h" //#define BENCHMARK #include "StopWatch.h" @@ -37,168 +39,221 @@ namespace rtengine extern const Settings* settings; -bool RawImageSource::channelsAvg(Coord spotPos, int spotSize, float avgs[3], const FilmNegativeParams ¶ms) +} + +namespace { - avgs[0] = avgs[1] = avgs[2] = 0.f; // Channel averages - if(ri->getSensorType() != ST_BAYER && ri->getSensorType() != ST_FUJI_XTRANS) +bool channelsAvg( + const rtengine::RawImage* ri, + int width, + int height, + array2D& rawData, + rtengine::Coord spotPos, + int spotSize, + const rtengine::procparams::FilmNegativeParams& params, + std::array& avgs +) +{ + avgs = {}; // Channel averages + + if (ri->getSensorType() != rtengine::ST_BAYER && ri->getSensorType() != rtengine::ST_FUJI_XTRANS) { return false; + } - if (settings->verbose) + if (rtengine::settings->verbose) { printf("Spot coord: x=%d y=%d\n", spotPos.x, spotPos.y); + } - int x1 = spotPos.x - spotSize / 2; - int x2 = spotPos.x + spotSize / 2; - int y1 = spotPos.y - spotSize / 2; - int y2 = spotPos.y + spotSize / 2; + const int half_spot_size = spotSize / 2; - if(x1<0 || x2>W || y1<0 || y2>H) + const int& x1 = spotPos.x - half_spot_size; + const int& x2 = spotPos.x + half_spot_size; + const int& y1 = spotPos.y - half_spot_size; + const int& y2 = spotPos.y + half_spot_size; + + if (x1 < 0 || x2 > width || y1 < 0 || y2 > height) { return false; // Spot goes outside bounds, bail out. + } - int pxCount[3] = {0}; // Per-channel sample counts - for(int c=spotPos.x-spotSize; c pxCount = {}; // Per-channel sample counts + for (int c = spotPos.x - spotSize; c < spotPos.x + spotSize; ++c) { + for (int r = spotPos.y - spotSize; r < spotPos.y + spotSize; ++r) { + const int ch = ri->getSensorType() == rtengine::ST_BAYER ? ri->FC(r,c) : ri->XTRANSFC(r,c); - int ch = (ri->getSensorType() == ST_BAYER) ? FC(r,c) : ri->XTRANSFC(r,c); + ++pxCount[ch]; - pxCount[ch]++; // If film negative is currently enabled, undo the effect by elevating to 1/exp, // in order to sample the original, linear value - if(params.enabled) - avgs[ch] += powf(rawData[r][c], -1 / (ch==0 ? params.redExp : ch==1 ? params.greenExp : params.blueExp)); - else + if (params.enabled) { + avgs[ch] += powf(rawData[r][c], -1.f / (ch == 0 ? params.redExp : ch == 1 ? params.greenExp : params.blueExp)); + } else { avgs[ch] += rawData[r][c]; + } } } - for(int ch=0; ch<3; ch++) - avgs[ch] = avgs[ch] / (pxCount[ch]); + for (int ch = 0; ch < 3; ++ch) { + avgs[ch] /= pxCount[ch]; + } return true; } -// Calculate logarithms in arbitrary base -float logBase(float base, float num) { - return log(num) / log(base); } -bool RawImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams ¤tParams, std::array& newExps) +bool rtengine::RawImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D spotB, int tran, const FilmNegativeParams ¤tParams, std::array& newExps) { - float clearVals[3], denseVals[3]; + newExps = { + static_cast(currentParams.redExp), + static_cast(currentParams.greenExp), + static_cast(currentParams.blueExp) + }; - newExps[0] = currentParams.redExp; - newExps[1] = currentParams.greenExp; - newExps[2] = currentParams.blueExp; + constexpr int spotSize = 32; // TODO: Make this configurable? - int spotSize = 32; // TODO : make this confugurable ? Coord spot; + std::array clearVals; + std::array denseVals; + // Sample first spot - transformPosition (spotA.x, spotA.y, tran, spot.x, spot.y); - if(!channelsAvg(spot, spotSize, clearVals, currentParams)) - return false; - - // Sample second spot - transformPosition (spotB.x, spotB.y, tran, spot.x, spot.y); - if(!channelsAvg(spot, spotSize, denseVals, currentParams)) - return false; - - // Detect which one is the dense spot, based on green channel - if(clearVals[1] < denseVals[1]) - std::swap(clearVals, denseVals); - - if (settings->verbose) { - printf("Clear film values: R=%g G=%g B=%g\n", clearVals[0], clearVals[1], clearVals[2]); - printf("Dense film values: R=%g G=%g B=%g\n", denseVals[0], denseVals[1], denseVals[2]); + transformPosition(spotA.x, spotA.y, tran, spot.x, spot.y); + if (!channelsAvg(ri, W, H, rawData, spot, spotSize, currentParams, clearVals)) { + return false; } - float denseGreenRatio = clearVals[1] / denseVals[1]; + // Sample second spot + transformPosition(spotB.x, spotB.y, tran, spot.x, spot.y); + if (!channelsAvg(ri, W, H, rawData, spot, spotSize, currentParams, denseVals)) { + return false; + } + + // Detect which one is the dense spot, based on green channel + if (clearVals[1] < denseVals[1]) { + std::swap(clearVals, denseVals); + } + + if (settings->verbose) { + printf("Clear film values: R=%g G=%g B=%g\n", clearVals[0], clearVals[1], clearVals[2]); + printf("Dense film values: R=%g G=%g B=%g\n", denseVals[0], denseVals[1], denseVals[2]); + } + + const float denseGreenRatio = clearVals[1] / denseVals[1]; + + // Calculate logarithms in arbitrary base + const auto logBase = + [](float base, float num) -> float + { + return std::log(num) / std::log(base); + }; // Calculate exponents for each channel, based on the ratio between the bright and dark values, // compared to the ratio in the reference channel (green) - for(int ch=0; ch<3; ch++) - if(ch==1) + for (int ch = 0; ch < 3; ++ch) { + if (ch == 1) { newExps[ch] = 2.f; // Green is the reference channel - else + } else { newExps[ch] = CLAMP(2.f * logBase(clearVals[ch] / denseVals[ch], denseGreenRatio), 0.3f, 6.f); + } + } - if (settings->verbose) + if (settings->verbose) { printf("New exponents: R=%g G=%g B=%g\n", newExps[0], newExps[1], newExps[2]); + } return true; } -void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams ¶ms) +void rtengine::RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams ¶ms) { // BENCHFUNMICRO - if(!params.enabled) + if (!params.enabled) { return; + } - float exps[3] = { (float)params.redExp, (float)params.greenExp, (float)params.blueExp }; + const std::array exps = { + static_cast(params.redExp), + static_cast(params.greenExp), + static_cast(params.blueExp) + }; - MyTime t1, t2, t3,t4, t5, t6; + MyTime t1, t2, t3,t4, t5; + t1.set(); // Channel vectors to calculate medians - std::vector cvs[3] = { - std::vector(), - std::vector(), - std::vector() - }; + std::array, 3> cvs; // Sample one every 5 pixels, and push the value in the appropriate channel vector. - // Chose an odd step, not multiple of the CFA size, to get a chance to visit each channel. - if(ri->getSensorType() == ST_BAYER) { - for (int row = 0; row < H; row+=5) { - for (int col = 0; col < W; col+=5) { - int c = FC(row, col); // three colors, 0=R, 1=G, 2=B + // Choose an odd step, not a multiple of the CFA size, to get a chance to visit each channel. + if (ri->getSensorType() == ST_BAYER) { + for (int row = 0; row < H; row += 5) { + for (int col = 0; col < W; col += 5) { + const int c = FC(row, col); // three colors: 0=R, 1=G, 2=B cvs[c].push_back(rawData[row][col]); } } - } else if(ri->getSensorType() == ST_FUJI_XTRANS) { - for (int row = 0; row < H; row+=5) { - for (int col = 0; col < W; col+=5) { - int c = ri->XTRANSFC(row, col); // three colors, 0=R, 1=G, 2=B + } + else if (ri->getSensorType() == ST_FUJI_XTRANS) { + for (int row = 0; row < H; row += 5) { + for (int col = 0; col < W; col += 5) { + const int c = ri->XTRANSFC(row, col); // three colors: 0=R, 1=G, 2=B cvs[c].push_back(rawData[row][col]); } } } - const float MAX_OUT_VALUE = 65000.f; + constexpr float MAX_OUT_VALUE = 65000.f; t2.set(); - if (settings->verbose) + + if (settings->verbose) { printf("Median vector fill loop time us: %d\n", t2.etime(t1)); + } - float medians[3]; // Channel median values - float mults[3] = { 1.f }; // Channel normalization multipliers + t2.set(); - for (int c=0; c<3; c++) { + std::array medians; // Channel median values + std::array mults = { + 1.f, + 1.f, + 1.f + }; // Channel normalization multipliers + + for (int c = 0; c < 3; ++c) { // Find median values for each channel - if(cvs[c].size() > 0) { - findMinMaxPercentile(&cvs[c][0], cvs[c].size(), 0.5f, medians[c], 0.5f, medians[c], true); - medians[c] = pow_F(max(medians[c], 1.f), -exps[c]); + if (!cvs[c].empty()) { + findMinMaxPercentile(cvs[c].data(), cvs[c].size(), 0.5f, medians[c], 0.5f, medians[c], true); + medians[c] = pow_F(rtengine::max(medians[c], 1.f), -exps[c]); // Determine the channel multipler so that N times the median becomes 65k. This clips away // the values in the dark border surrounding the negative (due to the film holder, for example), // the reciprocal of which have blown up to stellar values. - mults[c] = MAX_OUT_VALUE / (medians[c] * 24); + mults[c] = MAX_OUT_VALUE / (medians[c] * 24.f); } } t3.set(); + if (settings->verbose) { - printf("Sample count : %lu, %lu, %lu\n", cvs[0].size(), cvs[1].size(), cvs[2].size()); - printf("Medians : %g %g %g\n", medians[0], medians[1], medians[2] ); - printf("Computed multipliers : %g %g %g\n", mults[0], mults[1], mults[2] ); + printf("Sample count: %zu, %zu, %zu\n", cvs[0].size(), cvs[1].size(), cvs[2].size()); + printf("Medians: %g %g %g\n", medians[0], medians[1], medians[2] ); + printf("Computed multipliers: %g %g %g\n", mults[0], mults[1], mults[2] ); printf("Median calc time us: %d\n", t3.etime(t2)); } + t3.set(); + + if (ri->getSensorType() == ST_BAYER) { +#ifdef __SSE2__ + const vfloat onev = F2V(1.f); + const vfloat c65535v = F2V(65535.f); +#endif - if(ri->getSensorType() == ST_BAYER) { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) #endif - for (int row = 0; row < H; row ++) { + for (int row = 0; row < H; ++row) { int col = 0; // Exponents are expressed as positive in the parameters, so negate them in order // to get the reciprocals. Avoid trouble with zeroes, minimum pixel value is 1. @@ -209,21 +264,24 @@ void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &p #ifdef __SSE2__ const vfloat expsv = _mm_setr_ps(exps0, exps1, exps0, exps1); const vfloat multsv = _mm_setr_ps(mult0, mult1, mult0, mult1); - const vfloat onev = F2V(1.f); - const vfloat c65535v = F2V(65535.f); - for (; col < W - 3; col+=4) { + for (; col < W - 3; col += 4) { STVFU(rawData[row][col], vminf(multsv * pow_F(vmaxf(LVFU(rawData[row][col]), onev), expsv), c65535v)); } #endif // __SSE2__ - for (; col < W - 1; col+=2) { - rawData[row][col] = rtengine::min(mult0 * pow_F(max(rawData[row][col], 1.f), exps0), 65535.f); - rawData[row][col + 1] = rtengine::min(mult1 * pow_F(max(rawData[row][col + 1], 1.f), exps1), 65535.f); + for (; col < W - 1; col += 2) { + rawData[row][col] = rtengine::min(mult0 * pow_F(rtengine::max(rawData[row][col], 1.f), exps0), 65535.f); + rawData[row][col + 1] = rtengine::min(mult1 * pow_F(rtengine::max(rawData[row][col + 1], 1.f), exps1), 65535.f); } if (col < W) { - rawData[row][col] = rtengine::min(mult0 * pow_F(max(rawData[row][col], 1.f), exps0), 65535.f); + rawData[row][col] = rtengine::min(mult0 * pow_F(rtengine::max(rawData[row][col], 1.f), exps0), 65535.f); } } - } else if(ri->getSensorType() == ST_FUJI_XTRANS) { + } else if (ri->getSensorType() == ST_FUJI_XTRANS) { +#ifdef __SSE2__ + const vfloat onev = F2V(1.f); + const vfloat c65535v = F2V(65535.f); +#endif + #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) #endif @@ -231,8 +289,22 @@ void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &p int col = 0; // Exponents are expressed as positive in the parameters, so negate them in order // to get the reciprocals. Avoid trouble with zeroes, minimum pixel value is 1. - const float expsc[6] = {-exps[ri->XTRANSFC(row, 0)], -exps[ri->XTRANSFC(row, 1)], -exps[ri->XTRANSFC(row, 2)], -exps[ri->XTRANSFC(row, 3)], -exps[ri->XTRANSFC(row, 4)], -exps[ri->XTRANSFC(row, 5)]}; - const float multsc[6] = {mults[ri->XTRANSFC(row, 0)], mults[ri->XTRANSFC(row, 1)], mults[ri->XTRANSFC(row, 2)], mults[ri->XTRANSFC(row, 3)], mults[ri->XTRANSFC(row, 4)], mults[ri->XTRANSFC(row, 5)]}; + const std::array expsc = { + -exps[ri->XTRANSFC(row, 0)], + -exps[ri->XTRANSFC(row, 1)], + -exps[ri->XTRANSFC(row, 2)], + -exps[ri->XTRANSFC(row, 3)], + -exps[ri->XTRANSFC(row, 4)], + -exps[ri->XTRANSFC(row, 5)] + }; + const std::array multsc = { + mults[ri->XTRANSFC(row, 0)], + mults[ri->XTRANSFC(row, 1)], + mults[ri->XTRANSFC(row, 2)], + mults[ri->XTRANSFC(row, 3)], + mults[ri->XTRANSFC(row, 4)], + mults[ri->XTRANSFC(row, 5)] + }; #ifdef __SSE2__ const vfloat expsv0 = _mm_setr_ps(expsc[0], expsc[1], expsc[2], expsc[3]); const vfloat expsv1 = _mm_setr_ps(expsc[4], expsc[5], expsc[0], expsc[1]); @@ -240,81 +312,75 @@ void RawImageSource::filmNegativeProcess(const procparams::FilmNegativeParams &p const vfloat multsv0 = _mm_setr_ps(multsc[0], multsc[1], multsc[2], multsc[3]); const vfloat multsv1 = _mm_setr_ps(multsc[4], multsc[5], multsc[0], multsc[1]); const vfloat multsv2 = _mm_setr_ps(multsc[2], multsc[3], multsc[4], multsc[5]); - const vfloat onev = F2V(1.f); - const vfloat c65535v = F2V(65535.f); - for (; col < W - 11; col+=12) { + for (; col < W - 11; col += 12) { STVFU(rawData[row][col], vminf(multsv0 * pow_F(vmaxf(LVFU(rawData[row][col]), onev), expsv0), c65535v)); STVFU(rawData[row][col + 4], vminf(multsv1 * pow_F(vmaxf(LVFU(rawData[row][col + 4]), onev), expsv1), c65535v)); STVFU(rawData[row][col + 8], vminf(multsv2 * pow_F(vmaxf(LVFU(rawData[row][col + 8]), onev), expsv2), c65535v)); } #endif // __SSE2__ - for (; col < W - 5; col+=6) { + for (; col < W - 5; col += 6) { for (int c = 0; c < 6; ++c) { - rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(max(rawData[row][col + c], 1.f), expsc[c]), 65535.f); + rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(rtengine::max(rawData[row][col + c], 1.f), expsc[c]), 65535.f); } } for (int c = 0; col < W; col++, c++) { - rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(max(rawData[row][col + c], 1.f), expsc[c]), 65535.f); + rawData[row][col + c] = rtengine::min(multsc[c] * pow_F(rtengine::max(rawData[row][col + c], 1.f), expsc[c]), 65535.f); } } } + t4.set(); + + if (settings->verbose) { + printf("Pow loop time us: %d\n", t4.etime(t3)); + } t4.set(); - if (settings->verbose) - printf("Pow loop time us: %d\n", t4.etime(t3)); - - - t5.set(); PixelsMap bitmapBads(W, H); int totBP = 0; // Hold count of bad pixels to correct - if(ri->getSensorType() == ST_BAYER) { - - + if (ri->getSensorType() == ST_BAYER) { #ifdef _OPENMP #pragma omp parallel for reduction(+:totBP) schedule(dynamic,16) #endif - - for(int i = 0; i < H; i++) - for(int j = 0; j < W; j++) { + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { if (rawData[i][j] >= MAX_OUT_VALUE) { bitmapBads.set(j, i); - totBP++; + ++totBP; } } - - if (totBP > 0) { - interpolateBadPixelsBayer( bitmapBads, rawData ); } - } else if(ri->getSensorType() == ST_FUJI_XTRANS) { - -#ifdef _OPENMP - #pragma omp parallel for reduction(+:totBP) schedule(dynamic,16) -#endif - - for(int i = 0; i < H; i++) - for(int j = 0; j < W; j++) { - if (rawData[i][j] >= MAX_OUT_VALUE) { - bitmapBads.set(j, i); - totBP++; - } - } - if (totBP > 0) { - interpolateBadPixelsXtrans( bitmapBads ); + interpolateBadPixelsBayer(bitmapBads, rawData); } } + else if (ri->getSensorType() == ST_FUJI_XTRANS) { +#ifdef _OPENMP + #pragma omp parallel for reduction(+:totBP) schedule(dynamic,16) +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + if (rawData[i][j] >= MAX_OUT_VALUE) { + bitmapBads.set(j, i); + totBP++; + } + } + } + + if (totBP > 0) { + interpolateBadPixelsXtrans(bitmapBads); + } + } + + t5.set(); - t6.set(); if (settings->verbose) { printf("Bad pixels count: %d\n", totBP); - printf("Bad pixels interpolation time us: %d\n", t6.etime(t5)); + printf("Bad pixels interpolation time us: %d\n", t5.etime(t4)); } } - -} diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index 1fa1630ab..0134d4d16 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -67,7 +67,7 @@ RawImage::~RawImage() } } -eSensorType RawImage::getSensorType() +eSensorType RawImage::getSensorType() const { if (isBayer()) { return ST_BAYER; diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 0dabfef0d..4ff6d79c9 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -185,7 +185,7 @@ public: return float_raw_image; } - eSensorType getSensorType(); + eSensorType getSensorType() const; void getRgbCam (float rgbcam[3][4]); void getXtransMatrix ( int xtransMatrix[6][6]); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 9bfeb0c67..c04da5b66 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -43,7 +43,6 @@ private: static LUTf initInvGrad (); static void colorSpaceConversion_ (Imagefloat* im, const procparams::ColorManagementParams& cmp, const ColorTemp &wb, double pre_mul[3], cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName); int defTransform (int tran); - bool channelsAvg(Coord spotPos, int spotSize, float avgs[3], const FilmNegativeParams ¶ms); protected: MyMutex getImageMutex; // locks getImage diff --git a/rtgui/filmnegative.cc b/rtgui/filmnegative.cc index 72141ecfb..fef4fc6c6 100644 --- a/rtgui/filmnegative.cc +++ b/rtgui/filmnegative.cc @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2019 rom9 * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,52 +16,52 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ -#include "filmnegative.h" - #include -#include "rtimage.h" -#include "options.h" +#include "filmnegative.h" + #include "editwidgets.h" #include "eventmapper.h" +#include "options.h" +#include "rtimage.h" +#include "../rtengine/procparams.h" -using namespace rtengine; -using namespace rtengine::procparams; - - -FilmNegative::FilmNegative () : FoldableToolPanel(this, "filmnegative", M("TP_FILMNEGATIVE_LABEL"), false, true), EditSubscriber(ET_OBJECTS) +namespace { - auto mkExponentAdjuster = [this](Glib::ustring label, double defaultVal) { - Adjuster *adj = Gtk::manage(new Adjuster (label, 0.3, 6, 0.001, defaultVal)); //exponent - adj->setAdjusterListener (this); - - if (adj->delay < options.adjusterMaxDelay) { - adj->delay = options.adjusterMaxDelay; - } - - adj->show(); - return adj; - }; +Adjuster* createExponentAdjuster(AdjusterListener* listener, const Glib::ustring& label, double defaultVal) +{ + Adjuster* const adj = Gtk::manage(new Adjuster(label, 0.3, 6, 0.001, defaultVal)); // exponent + adj->setAdjusterListener(listener); - redExp = mkExponentAdjuster(M("TP_FILMNEGATIVE_RED"), 2.72); - greenExp = mkExponentAdjuster(M("TP_FILMNEGATIVE_GREEN"), 2.0); - blueExp = mkExponentAdjuster(M("TP_FILMNEGATIVE_BLUE"), 1.72); + if (adj->delay < options.adjusterMaxDelay) { + adj->delay = options.adjusterMaxDelay; + } - redRatio = redExp->getValue() / greenExp->getValue(); - blueRatio = blueExp->getValue() / greenExp->getValue(); + adj->show(); + return adj; +} - auto m = ProcEventMapper::getInstance(); - EvFilmNegativeEnabled = m->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_ENABLED"); - EvFilmNegativeExponents = m->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_EXPONENTS"); +} - - spotgrid = Gtk::manage(new Gtk::Grid()); +FilmNegative::FilmNegative() : + FoldableToolPanel(this, "filmnegative", M("TP_FILMNEGATIVE_LABEL"), false, true), + EditSubscriber(ET_OBJECTS), + evFilmNegativeExponents(ProcEventMapper::getInstance()->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_EXPONENTS")), + evFilmNegativeEnabled(ProcEventMapper::getInstance()->newEvent(FIRST, "HISTORY_MSG_FILMNEGATIVE_ENABLED")), + fnp(nullptr), + redExp(createExponentAdjuster(this, M("TP_FILMNEGATIVE_RED"), 2.72)), + greenExp(createExponentAdjuster(this, M("TP_FILMNEGATIVE_GREEN"), 2.0)), + blueExp(createExponentAdjuster(this, M("TP_FILMNEGATIVE_BLUE"), 1.72)), + spotgrid(Gtk::manage(new Gtk::Grid())), + spotbutton(Gtk::manage(new Gtk::ToggleButton(M("TP_FILMNEGATIVE_PICK")))), + redRatio(redExp->getValue() / greenExp->getValue()), + blueRatio(blueExp->getValue() / greenExp->getValue()) +{ spotgrid->get_style_context()->add_class("grid-spacing"); setExpandAlignProperties(spotgrid, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - spotbutton = Gtk::manage (new Gtk::ToggleButton (M("TP_FILMNEGATIVE_PICK"))); setExpandAlignProperties(spotbutton, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); spotbutton->get_style_context()->add_class("independent"); spotbutton->set_tooltip_text(M("TP_FILMNEGATIVE_GUESS_TOOLTIP")); @@ -83,120 +83,47 @@ FilmNegative::FilmNegative () : FoldableToolPanel(this, "filmnegative", M("TP_FI // spotsize->append ("4"); spotgrid->attach (*spotbutton, 0, 1, 1, 1); -// spotgrid->attach (*slab, 1, 0, 1, 1); + // spotgrid->attach (*slab, 1, 0, 1, 1); // spotgrid->attach (*wbsizehelper, 2, 0, 1, 1); - pack_start (*redExp, Gtk::PACK_SHRINK, 0); - pack_start (*greenExp, Gtk::PACK_SHRINK, 0); - pack_start (*blueExp, Gtk::PACK_SHRINK, 0); - pack_start (*spotgrid, Gtk::PACK_SHRINK, 0 ); - - spotbutton->signal_toggled().connect( sigc::mem_fun(*this, &FilmNegative::editToggled) ); -// spotsize->signal_changed().connect( sigc::mem_fun(*this, &WhiteBalance::spotSizeChanged) ); + pack_start(*redExp, Gtk::PACK_SHRINK, 0); + pack_start(*greenExp, Gtk::PACK_SHRINK, 0); + pack_start(*blueExp, Gtk::PACK_SHRINK, 0); + pack_start(*spotgrid, Gtk::PACK_SHRINK, 0); + spotbutton->signal_toggled().connect(sigc::mem_fun(*this, &FilmNegative::editToggled)); + // spotsize->signal_changed().connect( sigc::mem_fun(*this, &WhiteBalance::spotSizeChanged) ); // Editing geometry; create the spot rectangle - Rectangle *spotRect = new Rectangle(); + Rectangle* const spotRect = new Rectangle(); spotRect->filled = false; - EditSubscriber::visibleGeometry.push_back( spotRect ); + visibleGeometry.push_back(spotRect); // Stick a dummy rectangle over the whole image in mouseOverGeometry. - // This is to make sure the getCursor call is fired everywhere. - Rectangle *imgRect = new Rectangle(); + // This is to make sure the getCursor() call is fired everywhere. + Rectangle* imgRect = new Rectangle(); imgRect->filled = true; - EditSubscriber::mouseOverGeometry.push_back( imgRect ); - + mouseOverGeometry.push_back(imgRect); } FilmNegative::~FilmNegative() { -// idle_register.destroy(); - - for (std::vector::const_iterator i = visibleGeometry.begin(); i != visibleGeometry.end(); ++i) { - delete *i; + for (auto geometry : visibleGeometry) { + delete geometry; } - for (std::vector::const_iterator i = mouseOverGeometry.begin(); i != mouseOverGeometry.end(); ++i) { - delete *i; - } - -} - - - -void FilmNegative::enabledChanged() -{ - if (listener) { - if (get_inconsistent()) { - listener->panelChanged(EvFilmNegativeEnabled, M("GENERAL_UNCHANGED")); - } else if (getEnabled()) { - listener->panelChanged(EvFilmNegativeEnabled, M("GENERAL_ENABLED")); - } else { - listener->panelChanged(EvFilmNegativeEnabled, M("GENERAL_DISABLED")); - } + for (auto geometry : mouseOverGeometry) { + delete geometry; } } - -void FilmNegative::adjusterChanged(Adjuster* a, double newval) +void FilmNegative::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) { - if (listener) { - if(a == redExp || a == greenExp || a == blueExp) { - disableListener(); - if(a == greenExp) { - redExp->setValue(a->getValue() * redRatio); - blueExp->setValue(a->getValue() * blueRatio); - } else if(a == redExp) { - redRatio = newval / greenExp->getValue(); - } else if(a == blueExp) { - blueRatio = newval / greenExp->getValue(); - } - enableListener(); + disableListener(); - if(getEnabled()) { - listener->panelChanged (EvFilmNegativeExponents, Glib::ustring::compose ( - "R=%1 ; G=%2 ; B=%3", redExp->getTextValue(), greenExp->getTextValue(), blueExp->getTextValue())); - } - } - } -} - -void FilmNegative::adjusterAutoToggled(Adjuster* a, bool newval) -{ -} - -void FilmNegative::setEditProvider (EditDataProvider* provider) -{ - EditSubscriber::setEditProvider(provider); -} - -void FilmNegative::editToggled () -{ - if (spotbutton->get_active()) { - subscribe(); - - int w, h; - getEditProvider()->getImageSize(w, h); - - // Stick a dummy rectangle over the whole image in mouseOverGeometry. - // This is to make sure the getCursor call is fired everywhere. - const auto imgRect = static_cast(mouseOverGeometry.at(0)); - imgRect->setXYWH(0, 0, w, h); - - } else { - this->refSpotCoords.clear(); - unsubscribe(); - } -} - - -void FilmNegative::read (const ProcParams* pp, const ParamsEdited* pedited) -{ - disableListener (); - - if(pedited) { + if (pedited) { redExp->setEditedState(pedited->filmNegative.redExp ? Edited : UnEdited); greenExp->setEditedState(pedited->filmNegative.greenExp ? Edited : UnEdited); blueExp->setEditedState(pedited->filmNegative.blueExp ? Edited : UnEdited); @@ -208,10 +135,10 @@ void FilmNegative::read (const ProcParams* pp, const ParamsEdited* pedited) greenExp->setValue(pp->filmNegative.greenExp); blueExp->setValue(pp->filmNegative.blueExp); - enableListener (); + enableListener(); } -void FilmNegative::write (ProcParams* pp, ParamsEdited* pedited) +void FilmNegative::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) { pp->filmNegative.redExp = redExp->getValue(); pp->filmNegative.greenExp = greenExp->getValue(); @@ -226,37 +153,103 @@ void FilmNegative::write (ProcParams* pp, ParamsEdited* pedited) } } -void FilmNegative::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) +void FilmNegative::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) { redExp->setValue(defParams->filmNegative.redExp); greenExp->setValue(defParams->filmNegative.greenExp); blueExp->setValue(defParams->filmNegative.blueExp); if (pedited) { - redExp->setDefaultEditedState (pedited->filmNegative.redExp ? Edited : UnEdited); - greenExp->setDefaultEditedState (pedited->filmNegative.greenExp ? Edited : UnEdited); - blueExp->setDefaultEditedState (pedited->filmNegative.blueExp ? Edited : UnEdited); + redExp->setDefaultEditedState(pedited->filmNegative.redExp ? Edited : UnEdited); + greenExp->setDefaultEditedState(pedited->filmNegative.greenExp ? Edited : UnEdited); + blueExp->setDefaultEditedState(pedited->filmNegative.blueExp ? Edited : UnEdited); } else { - redExp->setDefaultEditedState (Irrelevant); - greenExp->setDefaultEditedState (Irrelevant); - blueExp->setDefaultEditedState (Irrelevant); + redExp->setDefaultEditedState(Irrelevant); + greenExp->setDefaultEditedState(Irrelevant); + blueExp->setDefaultEditedState(Irrelevant); } } -void FilmNegative::setBatchMode (bool batchMode) +void FilmNegative::setBatchMode(bool batchMode) { spotConn.disconnect(); removeIfThere(this, spotgrid, false); - ToolPanel::setBatchMode (batchMode); - redExp->showEditedCB (); - greenExp->showEditedCB (); - blueExp->showEditedCB (); + ToolPanel::setBatchMode(batchMode); + redExp->showEditedCB(); + greenExp->showEditedCB(); + blueExp->showEditedCB(); +} + +void FilmNegative::adjusterChanged(Adjuster* a, double newval) +{ + if (listener) { + if (a == redExp || a == greenExp || a == blueExp) { + disableListener(); + if (a == greenExp) { + redExp->setValue(a->getValue() * redRatio); + blueExp->setValue(a->getValue() * blueRatio); + } + else if (a == redExp) { + redRatio = newval / greenExp->getValue(); + } + else if (a == blueExp) { + blueRatio = newval / greenExp->getValue(); + } + enableListener(); + + if (getEnabled()) { + listener->panelChanged( + evFilmNegativeExponents, + Glib::ustring::compose( + "R=%1 ; G=%2 ; B=%3", + redExp->getTextValue(), + greenExp->getTextValue(), + blueExp->getTextValue() + ) + ); + } + } + } +} + +void FilmNegative::adjusterAutoToggled(Adjuster* a, bool newval) +{ +} + +void FilmNegative::enabledChanged() +{ + if (listener) { + if (get_inconsistent()) { + listener->panelChanged(evFilmNegativeEnabled, M("GENERAL_UNCHANGED")); + } + else if (getEnabled()) { + listener->panelChanged(evFilmNegativeEnabled, M("GENERAL_ENABLED")); + } + else { + listener->panelChanged(evFilmNegativeEnabled, M("GENERAL_DISABLED")); + } + } +} + +void FilmNegative::setFilmNegProvider(FilmNegProvider* p) +{ + fnp = p; +} + +void FilmNegative::setEditProvider(EditDataProvider* provider) +{ + EditSubscriber::setEditProvider(provider); +} + +CursorShape FilmNegative::getCursor(int objectID) const +{ + return CSSpotWB; } bool FilmNegative::mouseOver(int modifierKey) { - EditDataProvider *provider = getEditProvider(); - const auto spotRect = static_cast(visibleGeometry.at(0)); + EditDataProvider* const provider = getEditProvider(); + Rectangle* const spotRect = static_cast(visibleGeometry.at(0)); spotRect->setXYWH(provider->posImage.x - 16, provider->posImage.y - 16, 32, 32); return true; @@ -264,20 +257,18 @@ bool FilmNegative::mouseOver(int modifierKey) bool FilmNegative::button1Pressed(int modifierKey) { - EditDataProvider *provider = getEditProvider(); + EditDataProvider* const provider = getEditProvider(); - if(provider) { // debug. remove me + if (provider) { // TODO: Remove me (rom9) printf("x=%d y=%d pv1=%f pv2=%f pv3=%f\n", provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3()); } EditSubscriber::action = EditSubscriber::Action::NONE; if (listener) { - refSpotCoords.push_back(provider->posImage); - if(refSpotCoords.size() == 2) { - + if (refSpotCoords.size() == 2) { // User has selected 2 reference gray spots. Calculating new exponents // from channel values and updating parameters. @@ -292,8 +283,15 @@ bool FilmNegative::button1Pressed(int modifierKey) enableListener(); if (listener && getEnabled()) { - listener->panelChanged (EvFilmNegativeExponents, Glib::ustring::compose ( - "R=%1 ; G=%2 ; B=%3", redExp->getTextValue(), greenExp->getTextValue(), blueExp->getTextValue())); + listener->panelChanged( + evFilmNegativeExponents, + Glib::ustring::compose( + "R=%1 ; G=%2 ; B=%3", + redExp->getTextValue(), + greenExp->getTextValue(), + blueExp->getTextValue() + ) + ); } } @@ -304,35 +302,50 @@ bool FilmNegative::button1Pressed(int modifierKey) return true; } -bool FilmNegative::button1Released () +bool FilmNegative::button1Released() { - EditDataProvider *provider = getEditProvider(); + EditDataProvider* const provider = getEditProvider(); - if(provider) { // debug. remove me + if (provider) { // TODO: Remove me (rom9) printf("x=%d y=%d pv1=%f pv2=%f pv3=%f\n", provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3()); } EditSubscriber::action = EditSubscriber::Action::NONE; + return true; } -// TODO remove me ; couldn't make Action::PICKING work -bool FilmNegative::pick1 (bool picked) { - EditDataProvider *provider = getEditProvider(); - if(provider) { // debug. remove me - printf("Picked pick=%d x=%d y=%d pv1=%f pv2=%f pv3=%f\n", picked, provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3()); - } - return true; -} - -CursorShape FilmNegative::getCursor(int objectID) const -{ - return CSSpotWB; -} - -void FilmNegative::switchOffEditMode () +void FilmNegative::switchOffEditMode() { refSpotCoords.clear(); unsubscribe(); spotbutton->set_active(false); } + +// TODO: Remove me ; couldn't make Action::PICKING work (rom9) +bool FilmNegative::pick1(bool picked) +{ + EditDataProvider* const provider = getEditProvider(); + if (provider) { // TODO: Remove me (rom9) + printf("Picked pick=%d x=%d y=%d pv1=%f pv2=%f pv3=%f\n", picked, provider->posImage.x, provider->posImage.y, provider->getPipetteVal1(), provider->getPipetteVal2(), provider->getPipetteVal3()); + } + return true; +} + +void FilmNegative::editToggled() +{ + if (spotbutton->get_active()) { + subscribe(); + + int w, h; + getEditProvider()->getImageSize(w, h); + + // Stick a dummy rectangle over the whole image in mouseOverGeometry. + // This is to make sure the getCursor() call is fired everywhere. + Rectangle* const imgRect = static_cast(mouseOverGeometry.at(0)); + imgRect->setXYWH(0, 0, w, h); + } else { + refSpotCoords.clear(); + unsubscribe(); + } +} diff --git a/rtgui/filmnegative.h b/rtgui/filmnegative.h index b9dc15378..31d561127 100644 --- a/rtgui/filmnegative.h +++ b/rtgui/filmnegative.h @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2019 rom9 * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,66 +28,66 @@ #include "toolpanel.h" #include "wbprovider.h" -#include "../rtengine/procparams.h" +#include "../rtengine/noncopyable.h" class FilmNegProvider { public: virtual ~FilmNegProvider() = default; + virtual bool getFilmNegativeExponents(rtengine::Coord spotA, rtengine::Coord spotB, std::array& newExps) = 0; }; -class FilmNegative : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public EditSubscriber +class FilmNegative : + public rtengine::NonCopyable, + public ToolParamBlock, + public AdjusterListener, + public FoldableToolPanel, + public EditSubscriber { - -private: - rtengine::ProcEvent EvFilmNegativeExponents; - rtengine::ProcEvent EvFilmNegativeEnabled; - - std::vector refSpotCoords; - - FilmNegProvider *fnp; - - Adjuster* redExp; - Adjuster* greenExp; - Adjuster* blueExp; - - Gtk::Grid* spotgrid; - Gtk::ToggleButton* spotbutton; - sigc::connection spotConn; - - double redRatio, blueRatio; - - void editToggled (); - public: + FilmNegative(); + ~FilmNegative() override; - FilmNegative (); - ~FilmNegative () override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void setBatchMode(bool batchMode) override; - void setFilmNegProvider(FilmNegProvider* p) - { - fnp = p; - }; - - void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; - void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; - void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; - void setBatchMode (bool batchMode) override; - - void adjusterChanged (Adjuster* a, double newval) override; + void adjusterChanged(Adjuster* a, double newval) override; void adjusterAutoToggled(Adjuster* a, bool newval) override; - void spotPressed (); void enabledChanged() override; - void setEditProvider (EditDataProvider* provider) override; + void setFilmNegProvider(FilmNegProvider* p); + + void setEditProvider(EditDataProvider* provider) override; // EditSubscriber interface CursorShape getCursor(int objectID) const override; bool mouseOver(int modifierKey) override; bool button1Pressed(int modifierKey) override; bool button1Released() override; - void switchOffEditMode () override; + void switchOffEditMode() override; bool pick1(bool picked) override; +private: + void editToggled(); + + const rtengine::ProcEvent evFilmNegativeExponents; + const rtengine::ProcEvent evFilmNegativeEnabled; + + std::vector refSpotCoords; + + FilmNegProvider* fnp; + + Adjuster* const redExp; + Adjuster* const greenExp; + Adjuster* const blueExp; + + Gtk::Grid* const spotgrid; + Gtk::ToggleButton* const spotbutton; + sigc::connection spotConn; + + double redRatio; + double blueRatio; };