From b83069fd51974a1c5caab2fb50d8aceb5da1670a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 3 Mar 2019 14:52:44 +0100 Subject: [PATCH 01/13] Allow user-defined setting for raw ca correction tiles per thread --- rtdata/languages/default | 2 ++ rtengine/CA_correct_RT.cc | 9 +++++---- rtengine/rawimagesource.cc | 8 ++++---- rtengine/rawimagesource.h | 3 ++- rtgui/options.cc | 7 ++++++- rtgui/options.h | 2 +- rtgui/preferences.cc | 16 ++++++++++++++++ rtgui/preferences.h | 1 + 8 files changed, 37 insertions(+), 11 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 3d74db023..3019f49bd 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1202,6 +1202,8 @@ PREFERENCES_PRTPROFILE;Color profile PREFERENCES_PSPATH;Adobe Photoshop installation directory PREFERENCES_REMEMBERZOOMPAN;Remember zoom % and pan offset PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". +PREFERENCES_RAWCA;Raw CA correction +PREFERENCES_RAWCA_CHUNKSIZE;Tiles per thread PREFERENCES_SAVE_TP_OPEN_NOW;Save tool collapsed/expanded state now PREFERENCES_SELECTLANG;Select language PREFERENCES_SERIALIZE_TIFF_READ;TIFF Read Settings diff --git a/rtengine/CA_correct_RT.cc b/rtengine/CA_correct_RT.cc index e27f894ee..0d150c5c4 100644 --- a/rtengine/CA_correct_RT.cc +++ b/rtengine/CA_correct_RT.cc @@ -27,7 +27,7 @@ #include "rt_math.h" #include "gauss.h" #include "median.h" -//#define BENCHMARK +#define BENCHMARK #include "StopWatch.h" namespace { @@ -121,7 +121,8 @@ float* RawImageSource::CA_correct_RT( bool fitParamsIn, bool fitParamsOut, float* buffer, - bool freeBuffer + bool freeBuffer, + size_t chunkSize ) { BENCHFUN @@ -279,7 +280,7 @@ float* RawImageSource::CA_correct_RT( float blockdenomthr[2][2] = {}; #ifdef _OPENMP - #pragma omp for collapse(2) schedule(dynamic) nowait + #pragma omp for collapse(2) schedule(dynamic, chunkSize) nowait #endif for (int top = -border ; top < height; top += ts - border2) { for (int left = -border; left < width - (W & 1); left += ts - border2) { @@ -821,7 +822,7 @@ float* RawImageSource::CA_correct_RT( //green interpolated to optical sample points for R/B float* gshift = (float (*)) (data + 2 * sizeof(float) * ts * ts + sizeof(float) * ts * tsh + 4 * 64); // there is no overlap in buffer usage => share #ifdef _OPENMP - #pragma omp for schedule(dynamic) collapse(2) + #pragma omp for schedule(dynamic, chunkSize) collapse(2) #endif for (int top = -border; top < height; top += ts - border2) { for (int left = -border; left < width - (W & 1); left += ts - border2) { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index b7f90ba3a..d567ab671 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2031,13 +2031,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } if(numFrames == 4) { double fitParams[64]; - float *buffer = CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[0], fitParams, false, true, nullptr, false); + float *buffer = CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[0], fitParams, false, true, nullptr, false, options.chunkSizeCA); for(int i = 1; i < 3; ++i) { - CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[i], fitParams, true, false, buffer, false); + CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[i], fitParams, true, false, buffer, false, options.chunkSizeCA); } - CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[3], fitParams, true, false, buffer, true); + CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[3], fitParams, true, false, buffer, true, options.chunkSizeCA); } else { - CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, rawData, nullptr, false, false, nullptr, true); + CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, rawData, nullptr, false, false, nullptr, true, options.chunkSizeCA); } } diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index acf2deccb..34ac1cef3 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -249,7 +249,8 @@ protected: bool fitParamsIn, bool fitParamsOut, float* buffer, - bool freeBuffer + bool freeBuffer, + size_t chunkSize = 1 ); void ddct8x8s(int isgn, float a[8][8]); diff --git a/rtgui/options.cc b/rtgui/options.cc index 1dd3040aa..01146111d 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -448,7 +448,7 @@ void Options::setDefaults() maxInspectorBuffers = 2; // a rather conservative value for low specced systems... inspectorDelay = 0; serializeTiffRead = true; - + chunkSizeCA = 1; FileBrowserToolbarSingleRow = false; hideTPVScrollbar = false; whiteBalanceSpotSize = 8; @@ -1079,6 +1079,10 @@ void Options::readFromFile(Glib::ustring fname) serializeTiffRead = keyFile.get_boolean("Performance", "SerializeTiffRead"); } + if (keyFile.has_key("Performance", "ChunkSizeCA")) { + chunkSizeCA = keyFile.get_integer("Performance", "ChunkSizeCA"); + } + if (keyFile.has_key("Performance", "ThumbnailInspectorMode")) { rtSettings.thumbnail_inspector_mode = static_cast(keyFile.get_integer("Performance", "ThumbnailInspectorMode")); } @@ -1949,6 +1953,7 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_integer("Performance", "InspectorDelay", inspectorDelay); keyFile.set_integer("Performance", "PreviewDemosaicFromSidecar", prevdemo); keyFile.set_boolean("Performance", "SerializeTiffRead", serializeTiffRead); + keyFile.set_integer("Performance", "ChunkSizeCA", chunkSizeCA); keyFile.set_integer("Performance", "ThumbnailInspectorMode", int(rtSettings.thumbnail_inspector_mode)); keyFile.set_string("Output", "Format", saveFormat.format); diff --git a/rtgui/options.h b/rtgui/options.h index 5001306ff..f325605ab 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -314,7 +314,7 @@ public: bool filledProfile; // Used as reminder for the ProfilePanel "mode" prevdemo_t prevdemo; // Demosaicing method used for the <100% preview bool serializeTiffRead; - + size_t chunkSizeCA; bool menuGroupRank; bool menuGroupLabel; bool menuGroupFileOperations; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index c10c37b1f..94c9118c3 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -666,6 +666,20 @@ Gtk::Widget* Preferences::getPerformancePanel () fclut->add (*clutCacheSizeHB); vbPerformance->pack_start (*fclut, Gtk::PACK_SHRINK, 4); + Gtk::Frame* fca = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_RAWCA")) ); + Gtk::HBox* cachunkSizeHB = Gtk::manage ( new Gtk::HBox () ); + cachunkSizeHB->set_spacing (4); + Gtk::Label* CALl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_RAWCA_CHUNKSIZE") + ":", Gtk::ALIGN_START)); + chunkSizeCASB = Gtk::manage ( new Gtk::SpinButton () ); + chunkSizeCASB->set_digits (0); + chunkSizeCASB->set_increments (1, 5); + chunkSizeCASB->set_max_length (2); // Will this be sufficient? :) + chunkSizeCASB->set_range (1, 16); + cachunkSizeHB->pack_start (*CALl, Gtk::PACK_SHRINK, 0); + cachunkSizeHB->pack_end (*chunkSizeCASB, Gtk::PACK_SHRINK, 0); + fca->add (*cachunkSizeHB); + vbPerformance->pack_start (*fca, Gtk::PACK_SHRINK, 4); + Gtk::Frame* finspect = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_INSPECT_LABEL")) ); Gtk::HBox* maxIBuffersHB = Gtk::manage ( new Gtk::HBox () ); maxIBuffersHB->set_spacing (4); @@ -1783,6 +1797,7 @@ void Preferences::storePreferences () moptions.rgbDenoiseThreadLimit = threadsSpinBtn->get_value_as_int(); moptions.clutCacheSize = clutCacheSizeSB->get_value_as_int(); + moptions.chunkSizeCA = chunkSizeCASB->get_value_as_int(); moptions.maxInspectorBuffers = maxInspectorBuffersSB->get_value_as_int(); moptions.rtSettings.thumbnail_inspector_mode = static_cast(thumbnailInspectorMode->get_active_row_number()); @@ -1987,6 +2002,7 @@ void Preferences::fillPreferences () threadsSpinBtn->set_value (moptions.rgbDenoiseThreadLimit); clutCacheSizeSB->set_value (moptions.clutCacheSize); + chunkSizeCASB->set_value (moptions.chunkSizeCA); maxInspectorBuffersSB->set_value (moptions.maxInspectorBuffers); thumbnailInspectorMode->set_active(int(moptions.rtSettings.thumbnail_inspector_mode)); diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 3b78c0472..2320209d9 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -161,6 +161,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener Gtk::SpinButton* threadsSpinBtn; Gtk::SpinButton* clutCacheSizeSB; + Gtk::SpinButton* chunkSizeCASB; Gtk::SpinButton* maxInspectorBuffersSB; Gtk::ComboBoxText *thumbnailInspectorMode; From 77475a3827b8362977f0eaa4ed2ba5db107e5e49 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 3 Mar 2019 17:29:19 +0100 Subject: [PATCH 02/13] configurable tiles per thread also for rcd, #5203 --- rtdata/languages/default | 5 +++-- rtengine/amaze_demosaic_RT.cc | 3 ++- rtengine/dual_demosaic_RT.cc | 4 ++-- rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- rtengine/rcd_demosaic.cc | 5 +++-- rtgui/options.cc | 9 +++++++-- rtgui/options.h | 1 + rtgui/preferences.cc | 26 ++++++++++++++++++++++---- rtgui/preferences.h | 1 + 10 files changed, 43 insertions(+), 15 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 3019f49bd..804ae1624 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1064,6 +1064,9 @@ PREFERENCES_CACHECLEAR_SAFETY;Only files in the cache are cleared. Processing pr PREFERENCES_CACHEMAXENTRIES;Maximum number of cache entries PREFERENCES_CACHEOPTS;Cache Options PREFERENCES_CACHETHUMBHEIGHT;Maximum thumbnail height +PREFERENCES_CHUNKSIZES;Tiles per thread +PREFERENCES_CHUNKSIZE_RAW_CA;Raw CA correction +PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demosaic PREFERENCES_CLIPPINGIND;Clipping Indication PREFERENCES_CLUTSCACHE;HaldCLUT Cache PREFERENCES_CLUTSCACHE_LABEL;Maximum number of cached CLUTs @@ -1202,8 +1205,6 @@ PREFERENCES_PRTPROFILE;Color profile PREFERENCES_PSPATH;Adobe Photoshop installation directory PREFERENCES_REMEMBERZOOMPAN;Remember zoom % and pan offset PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Remember the zoom % and pan offset of the current image when opening a new image.\n\nThis option only works in "Single Editor Tab Mode" and when "Demosaicing method used for the preview at <100% zoom" is set to "As in PP3". -PREFERENCES_RAWCA;Raw CA correction -PREFERENCES_RAWCA_CHUNKSIZE;Tiles per thread PREFERENCES_SAVE_TP_OPEN_NOW;Save tool collapsed/expanded state now PREFERENCES_SELECTLANG;Select language PREFERENCES_SERIALIZE_TIFF_READ;TIFF Read Settings diff --git a/rtengine/amaze_demosaic_RT.cc b/rtengine/amaze_demosaic_RT.cc index 31419022d..3aafbd448 100644 --- a/rtengine/amaze_demosaic_RT.cc +++ b/rtengine/amaze_demosaic_RT.cc @@ -33,6 +33,7 @@ #include "sleef.c" #include "opthelper.h" #include "median.h" +#define BENCHMARK #include "StopWatch.h" namespace rtengine @@ -176,7 +177,7 @@ void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh, c // Main algorithm: Tile loop // use collapse(2) to collapse the 2 loops to one large loop, so there is better scaling #ifdef _OPENMP - #pragma omp for schedule(dynamic) collapse(2) nowait + #pragma omp for schedule(dynamic, 8) collapse(2) nowait #endif for (int top = winy - 16; top < winy + height; top += ts - 32) { diff --git a/rtengine/dual_demosaic_RT.cc b/rtengine/dual_demosaic_RT.cc index 790275d7e..e6397fe17 100644 --- a/rtengine/dual_demosaic_RT.cc +++ b/rtengine/dual_demosaic_RT.cc @@ -48,7 +48,7 @@ void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int wi } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4) ) { - rcd_demosaic(); + rcd_demosaic(options.chunkSizeCA); } } else { if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FOUR_PASS) ) { @@ -74,7 +74,7 @@ void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int wi } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4) ) { - rcd_demosaic(); + rcd_demosaic(options.chunkSizeCA); } } else { if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FOUR_PASS) ) { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index d567ab671..d6cffda26 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2103,7 +2103,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::MONO) ) { nodemosaic(true); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCD) ) { - rcd_demosaic (); + rcd_demosaic(options.chunkSizeRCD); } else { nodemosaic(false); } diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 34ac1cef3..5fb233c2b 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -277,7 +277,7 @@ protected: void fast_demosaic();//Emil's code for fast demosaicing void dcb_demosaic(int iterations, bool dcb_enhance); void ahd_demosaic(); - void rcd_demosaic(); + void rcd_demosaic(size_t chunkSize = 1); void border_interpolate(unsigned int border, float (*image)[4], unsigned int start = 0, unsigned int end = 0); void border_interpolate2(int winw, int winh, int lborders, const array2D &rawData, array2D &red, array2D &green, array2D &blue); void dcb_initTileLimits(int &colMin, int &rowMin, int &colMax, int &rowMax, int x0, int y0, int border); diff --git a/rtengine/rcd_demosaic.cc b/rtengine/rcd_demosaic.cc index 01430a894..137c98c18 100644 --- a/rtengine/rcd_demosaic.cc +++ b/rtengine/rcd_demosaic.cc @@ -22,6 +22,7 @@ #include "rt_math.h" #include "../rtgui/multilangmgr.h" #include "opthelper.h" +#define BENCHMARK #include "StopWatch.h" using namespace std; @@ -39,7 +40,7 @@ namespace rtengine * Licensed under the GNU GPL version 3 */ // Tiled version by Ingo Weyrich (heckflosse67@gmx.de) -void RawImageSource::rcd_demosaic() +void RawImageSource::rcd_demosaic(size_t chunkSize) { BENCHFUN @@ -72,7 +73,7 @@ void RawImageSource::rcd_demosaic() float *lpf = PQ_Dir; // reuse buffer, they don't overlap in usage #ifdef _OPENMP - #pragma omp for schedule(dynamic) collapse(2) nowait + #pragma omp for schedule(dynamic, chunkSize) collapse(2) nowait #endif for(int tr = 0; tr < numTh; ++tr) { for(int tc = 0; tc < numTw; ++tc) { diff --git a/rtgui/options.cc b/rtgui/options.cc index 01146111d..8f7fbbc50 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -449,6 +449,7 @@ void Options::setDefaults() inspectorDelay = 0; serializeTiffRead = true; chunkSizeCA = 1; + chunkSizeRCD = 1; FileBrowserToolbarSingleRow = false; hideTPVScrollbar = false; whiteBalanceSpotSize = 8; @@ -1080,7 +1081,11 @@ void Options::readFromFile(Glib::ustring fname) } if (keyFile.has_key("Performance", "ChunkSizeCA")) { - chunkSizeCA = keyFile.get_integer("Performance", "ChunkSizeCA"); + chunkSizeCA = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeCA"))); + } + + if (keyFile.has_key("Performance", "ChunkSizeRCD")) { + chunkSizeRCD = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeRCD"))); } if (keyFile.has_key("Performance", "ThumbnailInspectorMode")) { @@ -1953,7 +1958,7 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_integer("Performance", "InspectorDelay", inspectorDelay); keyFile.set_integer("Performance", "PreviewDemosaicFromSidecar", prevdemo); keyFile.set_boolean("Performance", "SerializeTiffRead", serializeTiffRead); - keyFile.set_integer("Performance", "ChunkSizeCA", chunkSizeCA); + keyFile.set_integer("Performance", "ChunkSizeRCD", chunkSizeRCD); keyFile.set_integer("Performance", "ThumbnailInspectorMode", int(rtSettings.thumbnail_inspector_mode)); keyFile.set_string("Output", "Format", saveFormat.format); diff --git a/rtgui/options.h b/rtgui/options.h index f325605ab..042889337 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -315,6 +315,7 @@ public: prevdemo_t prevdemo; // Demosaicing method used for the <100% preview bool serializeTiffRead; size_t chunkSizeCA; + size_t chunkSizeRCD; bool menuGroupRank; bool menuGroupLabel; bool menuGroupFileOperations; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 94c9118c3..f9724a15c 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -666,10 +666,11 @@ Gtk::Widget* Preferences::getPerformancePanel () fclut->add (*clutCacheSizeHB); vbPerformance->pack_start (*fclut, Gtk::PACK_SHRINK, 4); - Gtk::Frame* fca = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_RAWCA")) ); + Gtk::Frame* fchunksize = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_CHUNKSIZES")) ); + Gtk::VBox* chunkSizeVB = Gtk::manage ( new Gtk::VBox () ); Gtk::HBox* cachunkSizeHB = Gtk::manage ( new Gtk::HBox () ); cachunkSizeHB->set_spacing (4); - Gtk::Label* CALl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_RAWCA_CHUNKSIZE") + ":", Gtk::ALIGN_START)); + Gtk::Label* CALl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CHUNKSIZE_RAW_CA") + ":", Gtk::ALIGN_START)); chunkSizeCASB = Gtk::manage ( new Gtk::SpinButton () ); chunkSizeCASB->set_digits (0); chunkSizeCASB->set_increments (1, 5); @@ -677,8 +678,23 @@ Gtk::Widget* Preferences::getPerformancePanel () chunkSizeCASB->set_range (1, 16); cachunkSizeHB->pack_start (*CALl, Gtk::PACK_SHRINK, 0); cachunkSizeHB->pack_end (*chunkSizeCASB, Gtk::PACK_SHRINK, 0); - fca->add (*cachunkSizeHB); - vbPerformance->pack_start (*fca, Gtk::PACK_SHRINK, 4); + chunkSizeVB->add(*cachunkSizeHB); + + Gtk::HBox* rcdchunkSizeHB = Gtk::manage ( new Gtk::HBox () ); + rcdchunkSizeHB->set_spacing (4); + Gtk::Label* RCDLl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CHUNKSIZE_RAW_RCD") + ":", Gtk::ALIGN_START)); + chunkSizeRCDSB = Gtk::manage ( new Gtk::SpinButton () ); + chunkSizeRCDSB->set_digits (0); + chunkSizeRCDSB->set_increments (1, 5); + chunkSizeRCDSB->set_max_length (2); // Will this be sufficient? :) + chunkSizeRCDSB->set_range (1, 16); + rcdchunkSizeHB->pack_start (*RCDLl, Gtk::PACK_SHRINK, 0); + rcdchunkSizeHB->pack_end (*chunkSizeRCDSB, Gtk::PACK_SHRINK, 0); + chunkSizeVB->add(*rcdchunkSizeHB); + + fchunksize->add (*chunkSizeVB); + + vbPerformance->pack_start (*fchunksize, Gtk::PACK_SHRINK, 4); Gtk::Frame* finspect = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_INSPECT_LABEL")) ); Gtk::HBox* maxIBuffersHB = Gtk::manage ( new Gtk::HBox () ); @@ -1798,6 +1814,7 @@ void Preferences::storePreferences () moptions.rgbDenoiseThreadLimit = threadsSpinBtn->get_value_as_int(); moptions.clutCacheSize = clutCacheSizeSB->get_value_as_int(); moptions.chunkSizeCA = chunkSizeCASB->get_value_as_int(); + moptions.chunkSizeRCD= chunkSizeRCDSB->get_value_as_int(); moptions.maxInspectorBuffers = maxInspectorBuffersSB->get_value_as_int(); moptions.rtSettings.thumbnail_inspector_mode = static_cast(thumbnailInspectorMode->get_active_row_number()); @@ -2003,6 +2020,7 @@ void Preferences::fillPreferences () threadsSpinBtn->set_value (moptions.rgbDenoiseThreadLimit); clutCacheSizeSB->set_value (moptions.clutCacheSize); chunkSizeCASB->set_value (moptions.chunkSizeCA); + chunkSizeRCDSB->set_value (moptions.chunkSizeRCD); maxInspectorBuffersSB->set_value (moptions.maxInspectorBuffers); thumbnailInspectorMode->set_active(int(moptions.rtSettings.thumbnail_inspector_mode)); diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 2320209d9..60ed04a51 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -162,6 +162,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener Gtk::SpinButton* threadsSpinBtn; Gtk::SpinButton* clutCacheSizeSB; Gtk::SpinButton* chunkSizeCASB; + Gtk::SpinButton* chunkSizeRCDSB; Gtk::SpinButton* maxInspectorBuffersSB; Gtk::ComboBoxText *thumbnailInspectorMode; From 2c9dc32022025b806876075854ae17db6b756e5d Mon Sep 17 00:00:00 2001 From: Morgan Hardwood Date: Sat, 9 Mar 2019 16:17:44 +0100 Subject: [PATCH 03/13] Show more meaningful lens make/model #5157 If the lens make/model from the MakerNotes is vague, and if a lens make and model are set in Exif (LensMake, LensModel), then prefer Exif over vague. --- rtengine/imagedata.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index 49023f4bf..d47bcccb4 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -371,6 +371,11 @@ FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } } + // If MakeNotes are vague, fall back to Exif LensMake and LensModel if set + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#LensType + if (lens == "Manual Lens No CPU") { + lens_from_make_and_model(); + } } } @@ -472,8 +477,9 @@ FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } if (mnote->getTag ("LensType")) { lens = mnote->getTag ("LensType")->valueToString(); - if (!mnote->getTag("LensType")->toInt()) { - // try to find something better than "M-42 or No Lens" + // If MakeNotes are vague, fall back to Exif LensMake and LensModel if set + // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html#LensType + if (lens == "M-42 or No Lens" || lens == "K or M Lens" || lens == "A Series Lens" || lens == "Sigma") { lens_from_make_and_model(); } } else { @@ -499,6 +505,9 @@ FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } else if (mnote && (!make.compare (0, 4, "SONY") || !make.compare (0, 6, "KONICA"))) { if (mnote->getTag ("LensID")) { lens = mnote->getTag ("LensID")->valueToString (); + if (lens == "Unknown") { + lens_from_make_and_model(); + } } } else if (!make.compare (0, 7, "OLYMPUS")) { if (mnote->getTag ("Equipment")) { @@ -508,6 +517,9 @@ FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* lens = eq->getTag ("LensType")->valueToString (); } } + if (lens == "Unknown") { + lens_from_make_and_model(); + } } else if (mnote && !make.compare (0, 9, "Panasonic")) { if (mnote->getTag ("LensType")) { std::string panalens = mnote->getTag("LensType")->valueToString(); From b0a7b5fed33d58d7df676a459d03bfa4666ec3a7 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 9 Mar 2019 18:47:02 +0100 Subject: [PATCH 04/13] tiles per thread for some more tiled algorithms, #5203 --- rtdata/languages/default | 5 ++ rtengine/CA_correct_RT.cc | 13 ++++- rtengine/amaze_demosaic_RT.cc | 13 +++-- rtengine/dual_demosaic_RT.cc | 16 +++--- rtengine/improcfun.cc | 17 ++++-- rtengine/improcfun.h | 4 +- rtengine/pixelshift.cc | 8 +-- rtengine/rawimagesource.cc | 18 +++--- rtengine/rawimagesource.h | 11 ++-- rtengine/rcd_demosaic.cc | 10 +++- rtengine/simpleprocess.cc | 2 +- rtengine/xtrans_demosaic.cc | 14 ++++- rtgui/options.cc | 25 +++++++++ rtgui/options.h | 4 ++ rtgui/preferences.cc | 102 +++++++++++++++------------------- rtgui/preferences.h | 4 ++ 16 files changed, 161 insertions(+), 105 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 804ae1624..09f6fe9c1 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1065,8 +1065,11 @@ PREFERENCES_CACHEMAXENTRIES;Maximum number of cache entries PREFERENCES_CACHEOPTS;Cache Options PREFERENCES_CACHETHUMBHEIGHT;Maximum thumbnail height PREFERENCES_CHUNKSIZES;Tiles per thread +PREFERENCES_CHUNKSIZE_RAW_AMAZE;AMaZE demosaic PREFERENCES_CHUNKSIZE_RAW_CA;Raw CA correction PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demosaic +PREFERENCES_CHUNKSIZE_RAW_XT;Xtrans demosaic +PREFERENCES_CHUNKSIZE_RGB;RGB processing PREFERENCES_CLIPPINGIND;Clipping Indication PREFERENCES_CLUTSCACHE;HaldCLUT Cache PREFERENCES_CLUTSCACHE_LABEL;Maximum number of cached CLUTs @@ -1183,6 +1186,8 @@ PREFERENCES_PARSEDEXTADDHINT;Add entered extension to the list. PREFERENCES_PARSEDEXTDELHINT;Delete selected extension from the list. PREFERENCES_PARSEDEXTDOWNHINT;Move selected extension down in the list. PREFERENCES_PARSEDEXTUPHINT;Move selected extension up in the list. +PREFERENCES_PERFORMANCE_MEASURE;Measure +PREFERENCES_PERFORMANCE_MEASURE_HINT;Logs processing times in console PREFERENCES_PERFORMANCE_THREADS;Threads PREFERENCES_PERFORMANCE_THREADS_LABEL;Maximum number of threads for Noise Reduction and Wavelet Levels (0 = Automatic) PREFERENCES_PREVDEMO;Preview Demosaic Method diff --git a/rtengine/CA_correct_RT.cc b/rtengine/CA_correct_RT.cc index 0d150c5c4..76bd31f1e 100644 --- a/rtengine/CA_correct_RT.cc +++ b/rtengine/CA_correct_RT.cc @@ -27,7 +27,6 @@ #include "rt_math.h" #include "gauss.h" #include "median.h" -#define BENCHMARK #include "StopWatch.h" namespace { @@ -122,10 +121,18 @@ float* RawImageSource::CA_correct_RT( bool fitParamsOut, float* buffer, bool freeBuffer, - size_t chunkSize + size_t chunkSize, + bool measure ) { - BENCHFUN + + std::unique_ptr stop; + + if (measure) { + std::cout << "CA correcting " << W << "x" << H << " image with " << chunkSize << " tiles per thread" << std::endl; + stop.reset(new StopWatch("CA correction")); + } + // multithreaded and vectorized by Ingo Weyrich constexpr int ts = 128; constexpr int tsh = ts / 2; diff --git a/rtengine/amaze_demosaic_RT.cc b/rtengine/amaze_demosaic_RT.cc index 3aafbd448..fc36ff8b5 100644 --- a/rtengine/amaze_demosaic_RT.cc +++ b/rtengine/amaze_demosaic_RT.cc @@ -33,15 +33,20 @@ #include "sleef.c" #include "opthelper.h" #include "median.h" -#define BENCHMARK #include "StopWatch.h" namespace rtengine { -void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue) +void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, size_t chunkSize, bool measure) { - BENCHFUN + + std::unique_ptr stop; + + if (measure) { + std::cout << "Demosaicing " << W << "x" << H << " image using AMaZE with " << chunkSize << " Tiles per Thread" << std::endl; + stop.reset(new StopWatch("amaze demosaic")); + } volatile double progress = 0.0; @@ -177,7 +182,7 @@ void RawImageSource::amaze_demosaic_RT(int winx, int winy, int winw, int winh, c // Main algorithm: Tile loop // use collapse(2) to collapse the 2 loops to one large loop, so there is better scaling #ifdef _OPENMP - #pragma omp for schedule(dynamic, 8) collapse(2) nowait + #pragma omp for schedule(dynamic, chunkSize) collapse(2) nowait #endif for (int top = winy - 16; top < winy + height; top += ts - 32) { diff --git a/rtengine/dual_demosaic_RT.cc b/rtengine/dual_demosaic_RT.cc index e6397fe17..d7f3a4c14 100644 --- a/rtengine/dual_demosaic_RT.cc +++ b/rtengine/dual_demosaic_RT.cc @@ -44,17 +44,17 @@ void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int wi // contrast == 0.0 means only first demosaicer will be used if(isBayer) { if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) ) { - amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue); + amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue, options.chunkSizeAMAZE, options.measure); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4) ) { - rcd_demosaic(options.chunkSizeCA); + rcd_demosaic(options.chunkSizeRCD, options.measure); } } else { if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FOUR_PASS) ) { - xtrans_interpolate (3, true); + xtrans_interpolate (3, true, options.chunkSizeXT, options.measure); } else { - xtrans_interpolate (1, false); + xtrans_interpolate (1, false, options.chunkSizeXT, options.measure); } } @@ -70,17 +70,17 @@ void RawImageSource::dual_demosaic_RT(bool isBayer, const RAWParams &raw, int wi vng4_demosaic(rawData, redTmp, greenTmp, blueTmp); if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) || raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT)) { - amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue); + amaze_demosaic_RT(0, 0, winw, winh, rawData, red, green, blue, options.chunkSizeAMAZE, options.measure); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) ) { dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4) ) { - rcd_demosaic(options.chunkSizeCA); + rcd_demosaic(options.chunkSizeRCD, options.measure); } } else { if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FOUR_PASS) ) { - xtrans_interpolate (3, true); + xtrans_interpolate (3, true, options.chunkSizeXT, options.measure); } else { - xtrans_interpolate (1, false); + xtrans_interpolate (1, false, options.chunkSizeXT, options.measure); } fast_xtrans_interpolate(rawData, redTmp, greenTmp, blueTmp); } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 465e3ffe8..ab5260e94 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -2052,17 +2052,24 @@ filmlike_clip (float *r, float *g, float *b) void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer *pipetteBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, float satLimit, float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, bool opautili, LUTf & clToningcurve, LUTf & cl2Toningcurve, - const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve ) + const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve, size_t chunkSize, bool measure) { - rgbProc (working, lab, pipetteBuffer, hltonecurve, shtonecurve, tonecurve, sat, rCurve, gCurve, bCurve, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh, dcpProf, asIn, histToneCurve); + rgbProc (working, lab, pipetteBuffer, hltonecurve, shtonecurve, tonecurve, sat, rCurve, gCurve, bCurve, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh, dcpProf, asIn, histToneCurve, chunkSize, measure); } // Process RGB image and convert to LAB space void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer *pipetteBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, float satLimit, float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, bool opautili, LUTf & clToningcurve, LUTf & cl2Toningcurve, - const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, double expcomp, int hlcompr, int hlcomprthresh, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve ) + const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, double expcomp, int hlcompr, int hlcomprthresh, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve, size_t chunkSize, bool measure) { - BENCHFUN + + std::unique_ptr stop; + + if (measure) { + std::cout << "rgb processing " << working->getWidth() << "x" << working->getHeight() << " image with " << chunkSize << " tiles per thread" << std::endl; + stop.reset(new StopWatch("rgb processing")); + } + Imagefloat *tmpImage = nullptr; Imagefloat* editImgFloat = nullptr; @@ -2432,7 +2439,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } #ifdef _OPENMP - #pragma omp for schedule(dynamic) collapse(2) + #pragma omp for schedule(dynamic, chunkSize) collapse(2) #endif for (int ii = 0; ii < working->getHeight(); ii += TS) diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 3e583958b..129cef533 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -207,11 +207,11 @@ public: void updateColorProfiles(const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck); void rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer *pipetteBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, float satLimit, float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, bool opautili, LUTf & clcurve, LUTf & cl2curve, const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, - const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve); + const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve, size_t chunkSize = 1, bool measure = false); void rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer *pipetteBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, float satLimit, float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, bool opautili, LUTf & clcurve, LUTf & cl2curve, const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1, const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, - double expcomp, int hlcompr, int hlcomprthresh, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve); + double expcomp, int hlcompr, int hlcomprthresh, DCPProfile *dcpProf, const DCPProfile::ApplyState &asIn, LUTu &histToneCurve, size_t chunkSize = 1, bool measure = false); void labtoning(float r, float g, float b, float &ro, float &go, float &bo, int algm, int metchrom, int twoc, float satLimit, float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, LUTf & clToningcurve, LUTf & cl2Toningcurve, float iplow, float iphigh, double wp[3][3], double wip[3][3]); void toning2col(float r, float g, float b, float &ro, float &go, float &bo, float iplow, float iphigh, float rl, float gl, float bl, float rh, float gh, float bh, float SatLow, float SatHigh, float balanS, float balanH, float reducac, int mode, int preser, float strProtect); void toningsmh(float r, float g, float b, float &ro, float &go, float &bo, float RedLow, float GreenLow, float BlueLow, float RedMed, float GreenMed, float BlueMed, float RedHigh, float GreenHigh, float BlueHigh, float reducac, int mode, float strProtect); diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc index 76d1f836c..befa0d4c6 100644 --- a/rtengine/pixelshift.cc +++ b/rtengine/pixelshift.cc @@ -299,7 +299,7 @@ void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const RA { BENCHFUN if(numFrames != 4) { // fallback for non pixelshift files - amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue); + amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue, options.chunkSizeAMAZE, options.measure); return; } @@ -328,7 +328,7 @@ BENCHFUN } else if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZEVNG4)) { dual_demosaic_RT (true, rawParamsIn, winw, winh, *(rawDataFrames[0]), red, green, blue, bayerParams.dualDemosaicContrast, true); } else { - amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[0]), red, green, blue); + amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[0]), red, green, blue, options.chunkSizeAMAZE, options.measure); } multi_array2D redTmp(winw, winh); multi_array2D greenTmp(winw, winh); @@ -340,7 +340,7 @@ BENCHFUN } else if (bayerParams.pixelShiftDemosaicMethod == bayerParams.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZEVNG4)) { dual_demosaic_RT (true, rawParamsIn, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i], bayerParams.dualDemosaicContrast, true); } else { - amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i]); + amaze_demosaic_RT(winx, winy, winw, winh, *(rawDataFrames[i + 1]), redTmp[i], greenTmp[i], blueTmp[i], options.chunkSizeAMAZE, options.measure); } } @@ -369,7 +369,7 @@ BENCHFUN rawParamsTmp.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4); dual_demosaic_RT (true, rawParamsTmp, winw, winh, rawData, red, green, blue, bayerParams.dualDemosaicContrast, true); } else { - amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue); + amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue, options.chunkSizeAMAZE, options.measure); } } } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index d6cffda26..f529d52f2 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2031,13 +2031,13 @@ void RawImageSource::preprocess (const RAWParams &raw, const LensProfParams &le } if(numFrames == 4) { double fitParams[64]; - float *buffer = CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[0], fitParams, false, true, nullptr, false, options.chunkSizeCA); + float *buffer = CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[0], fitParams, false, true, nullptr, false, options.chunkSizeCA, options.measure); for(int i = 1; i < 3; ++i) { - CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[i], fitParams, true, false, buffer, false, options.chunkSizeCA); + CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[i], fitParams, true, false, buffer, false, options.chunkSizeCA, options.measure); } - CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[3], fitParams, true, false, buffer, true, options.chunkSizeCA); + CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, *rawDataFrames[3], fitParams, true, false, buffer, true, options.chunkSizeCA, options.measure); } else { - CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, rawData, nullptr, false, false, nullptr, true, options.chunkSizeCA); + CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, rawData, nullptr, false, false, nullptr, true, options.chunkSizeCA, options.measure); } } @@ -2078,7 +2078,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AHD) ) { ahd_demosaic (); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZE) ) { - amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue); + amaze_demosaic_RT (0, 0, W, H, rawData, red, green, blue, options.chunkSizeAMAZE, options.measure); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::AMAZEVNG4) || raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCBVNG4) || raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCDVNG4)) { @@ -2091,7 +2091,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT) ) { pixelshift(0, 0, W, H, raw, currFrame, ri->get_maker(), ri->get_model(), raw.expos); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCB) ) { - dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); + dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance, options.chunkSizeRCD, options.measure); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::EAHD)) { eahd_demosaic (); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::IGV)) { @@ -2103,7 +2103,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::MONO) ) { nodemosaic(true); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::RCD) ) { - rcd_demosaic(options.chunkSizeRCD); + rcd_demosaic(options.chunkSizeRCD, options.measure); } else { nodemosaic(false); } @@ -2111,9 +2111,9 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST) ) { fast_xtrans_interpolate(rawData, red, green, blue); } else if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::ONE_PASS)) { - xtrans_interpolate(1, false); + xtrans_interpolate(1, false, options.chunkSizeXT, options.measure); } else if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::THREE_PASS) ) { - xtrans_interpolate(3, true); + xtrans_interpolate(3, true, options.chunkSizeXT, options.measure); } else if (raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FOUR_PASS) || raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::TWO_PASS)) { if (!autoContrast) { double threshold = raw.xtranssensor.dualDemosaicContrast; diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 5fb233c2b..6d633c2a9 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -250,7 +250,8 @@ protected: bool fitParamsOut, float* buffer, bool freeBuffer, - size_t chunkSize = 1 + size_t chunkSize = 1, + bool measure = false ); void ddct8x8s(int isgn, float a[8][8]); @@ -272,12 +273,12 @@ protected: void jdl_interpolate_omp(); void igv_interpolate(int winw, int winh); void lmmse_interpolate_omp(int winw, int winh, array2D &rawData, array2D &red, array2D &green, array2D &blue, int iterations); - void amaze_demosaic_RT(int winx, int winy, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue);//Emil's code for AMaZE + void amaze_demosaic_RT(int winx, int winy, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, size_t chunkSize = 1, bool measure = false);//Emil's code for AMaZE void dual_demosaic_RT(bool isBayer, const RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast = false); void fast_demosaic();//Emil's code for fast demosaicing - void dcb_demosaic(int iterations, bool dcb_enhance); + void dcb_demosaic(int iterations, bool dcb_enhance, size_t chunkSize = 1, bool measure = false); void ahd_demosaic(); - void rcd_demosaic(size_t chunkSize = 1); + void rcd_demosaic(size_t chunkSize = 1, bool measure = false); void border_interpolate(unsigned int border, float (*image)[4], unsigned int start = 0, unsigned int end = 0); void border_interpolate2(int winw, int winh, int lborders, const array2D &rawData, array2D &red, array2D &green, array2D &blue); void dcb_initTileLimits(int &colMin, int &rowMin, int &colMax, int &rowMax, int x0, int y0, int border); @@ -296,7 +297,7 @@ protected: void dcb_color_full(float (*image)[3], int x0, int y0, float (*chroma)[2]); void cielab (const float (*rgb)[3], float* l, float* a, float *b, const int width, const int height, const int labWidth, const float xyz_cam[3][3]); void xtransborder_interpolate (int border, array2D &red, array2D &green, array2D &blue); - void xtrans_interpolate (const int passes, const bool useCieLab); + void xtrans_interpolate (const int passes, const bool useCieLab, size_t chunkSize = 1, bool measure = false); void fast_xtrans_interpolate (const array2D &rawData, array2D &red, array2D &green, array2D &blue); void pixelshift(int winx, int winy, int winw, int winh, const RAWParams &rawParams, unsigned int frame, const std::string &make, const std::string &model, float rawWpCorrection); void hflip (Imagefloat* im); diff --git a/rtengine/rcd_demosaic.cc b/rtengine/rcd_demosaic.cc index 137c98c18..5b6e1a164 100644 --- a/rtengine/rcd_demosaic.cc +++ b/rtengine/rcd_demosaic.cc @@ -22,7 +22,6 @@ #include "rt_math.h" #include "../rtgui/multilangmgr.h" #include "opthelper.h" -#define BENCHMARK #include "StopWatch.h" using namespace std; @@ -40,9 +39,14 @@ namespace rtengine * Licensed under the GNU GPL version 3 */ // Tiled version by Ingo Weyrich (heckflosse67@gmx.de) -void RawImageSource::rcd_demosaic(size_t chunkSize) +void RawImageSource::rcd_demosaic(size_t chunkSize, bool measure) { - BENCHFUN + std::unique_ptr stop; + + if (measure) { + std::cout << "Demosaicing " << W << "x" << H << " image using rcd with " << chunkSize << " tiles per thread" << std::endl; + stop.reset(new StopWatch("rcd demosaic")); + } volatile double progress = 0.0; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 8fa79241b..dddc3d6c3 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -991,7 +991,7 @@ private: LUTu histToneCurve; - ipf.rgbProc (baseImg, labView, nullptr, curve1, curve2, curve, params.toneCurve.saturation, rCurve, gCurve, bCurve, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh, dcpProf, as, histToneCurve); + ipf.rgbProc (baseImg, labView, nullptr, curve1, curve2, curve, params.toneCurve.saturation, rCurve, gCurve, bCurve, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh, dcpProf, as, histToneCurve, options.chunkSizeRGB, options.measure); if (settings->verbose) { printf ("Output image / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", autor, autog, autob); diff --git a/rtengine/xtrans_demosaic.cc b/rtengine/xtrans_demosaic.cc index f32969d17..cb65db092 100644 --- a/rtengine/xtrans_demosaic.cc +++ b/rtengine/xtrans_demosaic.cc @@ -43,6 +43,7 @@ void RawImageSource::cielab (const float (*rgb)[3], float* l, float* a, float *b if (!rgb) { if(!cbrtinit) { + #pragma omp parallel for for (int i = 0; i < 0x14000; i++) { double r = i / 65535.0; cbrt[i] = r > Color::eps ? std::cbrt(r) : (Color::kappa * r + 16.0) / 116.0; @@ -173,9 +174,16 @@ void RawImageSource::xtransborder_interpolate (int border, array2D &red, */ // override CLIP function to test unclipped output #define CLIP(x) (x) -void RawImageSource::xtrans_interpolate (const int passes, const bool useCieLab) +void RawImageSource::xtrans_interpolate (const int passes, const bool useCieLab, size_t chunkSize, bool measure) { - BENCHFUN + + std::unique_ptr stop; + + if (measure) { + std::cout << passes << "-pass Xtrans Demosaicing " << W << "x" << H << " image with " << chunkSize << " tiles per thread" << std::endl; + stop.reset(new StopWatch("xtrans demosaic")); + } + constexpr int ts = 114; /* Tile Size */ constexpr int tsh = ts / 2; /* half of Tile Size */ @@ -296,7 +304,7 @@ void RawImageSource::xtrans_interpolate (const int passes, const bool useCieLab) uint8_t (*homosummax)[ts] = (uint8_t (*)[ts]) homo[ndir - 1]; // we can reuse the homo-buffer because they are not used together #ifdef _OPENMP - #pragma omp for collapse(2) schedule(dynamic) nowait + #pragma omp for collapse(2) schedule(dynamic, chunkSize) nowait #endif for (int top = 3; top < height - 19; top += ts - 16) diff --git a/rtgui/options.cc b/rtgui/options.cc index 8f7fbbc50..2819497de 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -448,8 +448,12 @@ void Options::setDefaults() maxInspectorBuffers = 2; // a rather conservative value for low specced systems... inspectorDelay = 0; serializeTiffRead = true; + measure = false; + chunkSizeAMAZE = 1; chunkSizeCA = 1; chunkSizeRCD = 1; + chunkSizeRGB = 1; + chunkSizeXT = 1; FileBrowserToolbarSingleRow = false; hideTPVScrollbar = false; whiteBalanceSpotSize = 8; @@ -1080,6 +1084,14 @@ void Options::readFromFile(Glib::ustring fname) serializeTiffRead = keyFile.get_boolean("Performance", "SerializeTiffRead"); } + if (keyFile.has_key("Performance", "Measure")) { + measure = keyFile.get_boolean("Performance", "Measure"); + } + + if (keyFile.has_key("Performance", "ChunkSizeAMAZE")) { + chunkSizeAMAZE = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeAMAZE"))); + } + if (keyFile.has_key("Performance", "ChunkSizeCA")) { chunkSizeCA = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeCA"))); } @@ -1088,6 +1100,14 @@ void Options::readFromFile(Glib::ustring fname) chunkSizeRCD = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeRCD"))); } + if (keyFile.has_key("Performance", "ChunkSizeRGB")) { + chunkSizeRGB = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeRGB"))); + } + + if (keyFile.has_key("Performance", "ChunkSizeXT")) { + chunkSizeXT = std::min(16, std::max(1, keyFile.get_integer("Performance", "ChunkSizeXT"))); + } + if (keyFile.has_key("Performance", "ThumbnailInspectorMode")) { rtSettings.thumbnail_inspector_mode = static_cast(keyFile.get_integer("Performance", "ThumbnailInspectorMode")); } @@ -1958,7 +1978,12 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_integer("Performance", "InspectorDelay", inspectorDelay); keyFile.set_integer("Performance", "PreviewDemosaicFromSidecar", prevdemo); keyFile.set_boolean("Performance", "SerializeTiffRead", serializeTiffRead); + keyFile.set_integer("Performance", "Measure", measure); + keyFile.set_integer("Performance", "ChunkSizeAMAZE", chunkSizeAMAZE); keyFile.set_integer("Performance", "ChunkSizeRCD", chunkSizeRCD); + keyFile.set_integer("Performance", "ChunkSizeRGB", chunkSizeRGB); + keyFile.set_integer("Performance", "ChunkSizeXT", chunkSizeXT); + keyFile.set_integer("Performance", "ChunkSizeCA", chunkSizeCA); keyFile.set_integer("Performance", "ThumbnailInspectorMode", int(rtSettings.thumbnail_inspector_mode)); keyFile.set_string("Output", "Format", saveFormat.format); diff --git a/rtgui/options.h b/rtgui/options.h index 042889337..06c49ed07 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -314,8 +314,12 @@ public: bool filledProfile; // Used as reminder for the ProfilePanel "mode" prevdemo_t prevdemo; // Demosaicing method used for the <100% preview bool serializeTiffRead; + bool measure; + size_t chunkSizeAMAZE; size_t chunkSizeCA; size_t chunkSizeRCD; + size_t chunkSizeRGB; + size_t chunkSizeXT; bool menuGroupRank; bool menuGroupLabel; bool menuGroupFileOperations; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index f9724a15c..e23775bf9 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -31,6 +31,25 @@ #include #endif +namespace { +void placeSpinBox(Gtk::Container* where, Gtk::SpinButton* &spin, const std::string &labelText, int digits, int inc0, int inc1, int maxLength, int range0, int range1, const std::string &toolTip = "") { + Gtk::HBox* HB = Gtk::manage ( new Gtk::HBox () ); + HB->set_spacing (4); + if (!toolTip.empty()) { + HB->set_tooltip_text (M (toolTip)); + } + Gtk::Label* label = Gtk::manage ( new Gtk::Label (M (labelText) + ":", Gtk::ALIGN_START)); + spin = Gtk::manage ( new Gtk::SpinButton () ); + spin->set_digits (digits); + spin->set_increments (inc0, inc1); + spin->set_max_length (maxLength); // Will this be sufficient? :) + spin->set_range (range0, range1); + HB->pack_start (*label, Gtk::PACK_SHRINK, 0); + HB->pack_end (*spin, Gtk::PACK_SHRINK, 0); + where->add(*HB); +} +} + extern Options options; extern Glib::ustring argv0; Glib::RefPtr themecss; @@ -649,68 +668,36 @@ Gtk::Widget* Preferences::getPerformancePanel () vbPerformance->pack_start (*ftiffserialize, Gtk::PACK_SHRINK, 4); Gtk::Frame* fclut = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_CLUTSCACHE")) ); - Gtk::HBox* clutCacheSizeHB = Gtk::manage ( new Gtk::HBox () ); - clutCacheSizeHB->set_spacing (4); - Gtk::Label* CLUTLl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CLUTSCACHE_LABEL") + ":", Gtk::ALIGN_START)); - clutCacheSizeSB = Gtk::manage ( new Gtk::SpinButton () ); - clutCacheSizeSB->set_digits (0); - clutCacheSizeSB->set_increments (1, 5); - clutCacheSizeSB->set_max_length (2); // Will this be sufficient? :) #ifdef _OPENMP - clutCacheSizeSB->set_range (1, 3 * omp_get_num_procs()); + placeSpinBox(fclut, clutCacheSizeSB, "PREFERENCES_CLUTSCACHE_LABEL", 0, 1, 5, 2, 1, 3 * omp_get_num_procs()); #else - clutCacheSizeSB->set_range (1, 12); + placeSpinBox(fclut, clutCacheSizeSB, "PREFERENCES_CLUTSCACHE_LABEL", 0, 1, 5, 2, 1, 12); #endif - clutCacheSizeHB->pack_start (*CLUTLl, Gtk::PACK_SHRINK, 0); - clutCacheSizeHB->pack_end (*clutCacheSizeSB, Gtk::PACK_SHRINK, 0); - fclut->add (*clutCacheSizeHB); vbPerformance->pack_start (*fclut, Gtk::PACK_SHRINK, 4); Gtk::Frame* fchunksize = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_CHUNKSIZES")) ); Gtk::VBox* chunkSizeVB = Gtk::manage ( new Gtk::VBox () ); - Gtk::HBox* cachunkSizeHB = Gtk::manage ( new Gtk::HBox () ); - cachunkSizeHB->set_spacing (4); - Gtk::Label* CALl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CHUNKSIZE_RAW_CA") + ":", Gtk::ALIGN_START)); - chunkSizeCASB = Gtk::manage ( new Gtk::SpinButton () ); - chunkSizeCASB->set_digits (0); - chunkSizeCASB->set_increments (1, 5); - chunkSizeCASB->set_max_length (2); // Will this be sufficient? :) - chunkSizeCASB->set_range (1, 16); - cachunkSizeHB->pack_start (*CALl, Gtk::PACK_SHRINK, 0); - cachunkSizeHB->pack_end (*chunkSizeCASB, Gtk::PACK_SHRINK, 0); - chunkSizeVB->add(*cachunkSizeHB); - Gtk::HBox* rcdchunkSizeHB = Gtk::manage ( new Gtk::HBox () ); - rcdchunkSizeHB->set_spacing (4); - Gtk::Label* RCDLl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CHUNKSIZE_RAW_RCD") + ":", Gtk::ALIGN_START)); - chunkSizeRCDSB = Gtk::manage ( new Gtk::SpinButton () ); - chunkSizeRCDSB->set_digits (0); - chunkSizeRCDSB->set_increments (1, 5); - chunkSizeRCDSB->set_max_length (2); // Will this be sufficient? :) - chunkSizeRCDSB->set_range (1, 16); - rcdchunkSizeHB->pack_start (*RCDLl, Gtk::PACK_SHRINK, 0); - rcdchunkSizeHB->pack_end (*chunkSizeRCDSB, Gtk::PACK_SHRINK, 0); - chunkSizeVB->add(*rcdchunkSizeHB); + Gtk::HBox* measureHB = Gtk::manage ( new Gtk::HBox () ); + measureHB->set_spacing (4); + measureCB = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_PERFORMANCE_MEASURE")) ); + measureCB->set_tooltip_text (M ("PREFERENCES_PERFORMANCE_MEASURE_HINT")); + measureHB->pack_start(*measureCB, Gtk::PACK_SHRINK, 0); + chunkSizeVB->add(*measureHB); + + placeSpinBox(chunkSizeVB, chunkSizeAMSB, "PREFERENCES_CHUNKSIZE_RAW_AMAZE", 0, 1, 5, 2, 1, 16); + placeSpinBox(chunkSizeVB, chunkSizeCASB, "PREFERENCES_CHUNKSIZE_RAW_CA", 0, 1, 5, 2, 1, 16); + placeSpinBox(chunkSizeVB, chunkSizeRCDSB, "PREFERENCES_CHUNKSIZE_RAW_RCD", 0, 1, 5, 2, 1, 16); + placeSpinBox(chunkSizeVB, chunkSizeRGBSB, "PREFERENCES_CHUNKSIZE_RGB", 0, 1, 5, 2, 1, 16); + placeSpinBox(chunkSizeVB, chunkSizeXTSB, "PREFERENCES_CHUNKSIZE_RAW_XT", 0, 1, 5, 2, 1, 16); fchunksize->add (*chunkSizeVB); vbPerformance->pack_start (*fchunksize, Gtk::PACK_SHRINK, 4); Gtk::Frame* finspect = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_INSPECT_LABEL")) ); - Gtk::HBox* maxIBuffersHB = Gtk::manage ( new Gtk::HBox () ); - maxIBuffersHB->set_spacing (4); - maxIBuffersHB->set_tooltip_text (M ("PREFERENCES_INSPECT_MAXBUFFERS_TOOLTIP")); - Gtk::Label* maxIBufferLbl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_INSPECT_MAXBUFFERS_LABEL") + ":", Gtk::ALIGN_START)); - maxInspectorBuffersSB = Gtk::manage ( new Gtk::SpinButton () ); - maxInspectorBuffersSB->set_digits (0); - maxInspectorBuffersSB->set_increments (1, 5); - maxInspectorBuffersSB->set_max_length (2); - maxInspectorBuffersSB->set_range (1, 12); // ... we have to set a limit, 12 seem to be enough even for systems with tons of RAM - maxIBuffersHB->pack_start (*maxIBufferLbl, Gtk::PACK_SHRINK, 0); - maxIBuffersHB->pack_end (*maxInspectorBuffersSB, Gtk::PACK_SHRINK, 0); - Gtk::VBox *inspectorvb = Gtk::manage(new Gtk::VBox()); - inspectorvb->add(*maxIBuffersHB); + placeSpinBox(inspectorvb, maxInspectorBuffersSB, "PREFERENCES_INSPECT_MAXBUFFERS_LABEL", 0, 1, 5, 2, 1, 12, "PREFERENCES_INSPECT_MAXBUFFERS_TOOLTIP"); Gtk::HBox *insphb = Gtk::manage(new Gtk::HBox()); thumbnailInspectorMode = Gtk::manage(new Gtk::ComboBoxText()); @@ -726,23 +713,14 @@ Gtk::Widget* Preferences::getPerformancePanel () Gtk::Frame* threadsFrame = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_PERFORMANCE_THREADS")) ); Gtk::VBox* threadsVBox = Gtk::manage ( new Gtk::VBox (Gtk::PACK_SHRINK, 4) ); - Gtk::HBox* threadsHBox = Gtk::manage (new Gtk::HBox (Gtk::PACK_SHRINK, 4)); - Gtk::Label* threadsLbl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_PERFORMANCE_THREADS_LABEL") + ":", Gtk::ALIGN_START)); - threadsSpinBtn = Gtk::manage ( new Gtk::SpinButton () ); - threadsSpinBtn->set_digits (0); - threadsSpinBtn->set_increments (1, 5); - threadsSpinBtn->set_max_length (2); // Will this be sufficient? :) #ifdef _OPENMP int maxThreadNumber = omp_get_max_threads(); #else int maxThreadNumber = 10; #endif - threadsSpinBtn->set_range (0, maxThreadNumber); - threadsHBox->pack_start (*threadsLbl, Gtk::PACK_SHRINK, 2); - threadsHBox->pack_end (*threadsSpinBtn, Gtk::PACK_SHRINK, 2); + placeSpinBox(threadsVBox, threadsSpinBtn, "PREFERENCES_PERFORMANCE_THREADS_LABEL", 0, 1, 5, 2, 0, maxThreadNumber); - threadsVBox->pack_start (*threadsHBox, Gtk::PACK_SHRINK); threadsFrame->add (*threadsVBox); vbPerformance->pack_start (*threadsFrame, Gtk::PACK_SHRINK, 4); @@ -1813,8 +1791,12 @@ void Preferences::storePreferences () moptions.rgbDenoiseThreadLimit = threadsSpinBtn->get_value_as_int(); moptions.clutCacheSize = clutCacheSizeSB->get_value_as_int(); + moptions.measure = measureCB->get_active(); + moptions.chunkSizeAMAZE = chunkSizeAMSB->get_value_as_int(); moptions.chunkSizeCA = chunkSizeCASB->get_value_as_int(); - moptions.chunkSizeRCD= chunkSizeRCDSB->get_value_as_int(); + moptions.chunkSizeRCD = chunkSizeRCDSB->get_value_as_int(); + moptions.chunkSizeRGB = chunkSizeRGBSB->get_value_as_int(); + moptions.chunkSizeXT = chunkSizeXTSB->get_value_as_int(); moptions.maxInspectorBuffers = maxInspectorBuffersSB->get_value_as_int(); moptions.rtSettings.thumbnail_inspector_mode = static_cast(thumbnailInspectorMode->get_active_row_number()); @@ -2019,8 +2001,12 @@ void Preferences::fillPreferences () threadsSpinBtn->set_value (moptions.rgbDenoiseThreadLimit); clutCacheSizeSB->set_value (moptions.clutCacheSize); + measureCB->set_active (moptions.measure); + chunkSizeAMSB->set_value (moptions.chunkSizeAMAZE); chunkSizeCASB->set_value (moptions.chunkSizeCA); + chunkSizeRGBSB->set_value (moptions.chunkSizeRGB); chunkSizeRCDSB->set_value (moptions.chunkSizeRCD); + chunkSizeXTSB->set_value (moptions.chunkSizeXT); maxInspectorBuffersSB->set_value (moptions.maxInspectorBuffers); thumbnailInspectorMode->set_active(int(moptions.rtSettings.thumbnail_inspector_mode)); diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 60ed04a51..6f434c477 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -161,8 +161,12 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener Gtk::SpinButton* threadsSpinBtn; Gtk::SpinButton* clutCacheSizeSB; + Gtk::CheckButton* measureCB; + Gtk::SpinButton* chunkSizeAMSB; Gtk::SpinButton* chunkSizeCASB; Gtk::SpinButton* chunkSizeRCDSB; + Gtk::SpinButton* chunkSizeRGBSB; + Gtk::SpinButton* chunkSizeXTSB; Gtk::SpinButton* maxInspectorBuffersSB; Gtk::ComboBoxText *thumbnailInspectorMode; From 6712420a416dbb53cae536316b07ccff8b300853 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sat, 9 Mar 2019 19:28:44 +0100 Subject: [PATCH 05/13] Fix broken build, #5203 --- rtengine/rawimagesource.cc | 2 +- rtengine/rawimagesource.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index f529d52f2..6fe7683eb 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2091,7 +2091,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::PIXELSHIFT) ) { pixelshift(0, 0, W, H, raw, currFrame, ri->get_maker(), ri->get_model(), raw.expos); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::DCB) ) { - dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance, options.chunkSizeRCD, options.measure); + dcb_demosaic(raw.bayersensor.dcb_iterations, raw.bayersensor.dcb_enhance); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::EAHD)) { eahd_demosaic (); } else if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::IGV)) { diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 6d633c2a9..91b78081a 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -276,7 +276,7 @@ protected: void amaze_demosaic_RT(int winx, int winy, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, size_t chunkSize = 1, bool measure = false);//Emil's code for AMaZE void dual_demosaic_RT(bool isBayer, const RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast = false); void fast_demosaic();//Emil's code for fast demosaicing - void dcb_demosaic(int iterations, bool dcb_enhance, size_t chunkSize = 1, bool measure = false); + void dcb_demosaic(int iterations, bool dcb_enhance); void ahd_demosaic(); void rcd_demosaic(size_t chunkSize = 1, bool measure = false); void border_interpolate(unsigned int border, float (*image)[4], unsigned int start = 0, unsigned int end = 0); From d89364fcc0adf2734fa12c5559745e5e65b0a534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sun, 10 Mar 2019 11:40:05 +0100 Subject: [PATCH 06/13] Fix Exif parsing for old Canons (ported by @Beep6581, fixes #4843) --- rtengine/imagedata.cc | 48 ++++++++++++++++++++----------------------- rtexif/rtexif.cc | 1 + 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index d47bcccb4..bb43acd67 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -1293,35 +1293,31 @@ FramesData::FramesData (const Glib::ustring& fname, std::unique_ptrexifBase >= 0; rtexif::ExifManager exifManager (f, std::move(rml), firstFrameOnly); - - if (has_rml_exif_base) { - if (exifManager.f && exifManager.rml) { - if (exifManager.rml->exifBase >= 0) { - exifManager.parseRaw (); - - } else if (exifManager.rml->ciffBase >= 0) { - exifManager.parseCIFF (); - } - } - - // copying roots - roots = exifManager.roots; - - // creating FrameData - for (auto currFrame : exifManager.frames) { - frames.push_back(std::unique_ptr(new FrameData(currFrame, currFrame->getRoot(), roots.at(0)))); - } - for (auto currRoot : roots) { - rtexif::Tag* t = currRoot->getTag(0x83BB); - - if (t && !iptc) { - iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ()); - break; - } + if (exifManager.f && exifManager.rml) { + if (exifManager.rml->exifBase >= 0) { + exifManager.parseRaw (); + } else if (exifManager.rml->ciffBase >= 0) { + exifManager.parseCIFF (); } } + + // copying roots + roots = exifManager.roots; + + // creating FrameData + for (auto currFrame : exifManager.frames) { + frames.push_back(std::unique_ptr(new FrameData(currFrame, currFrame->getRoot(), roots.at(0)))); + } + for (auto currRoot : roots) { + rtexif::Tag* t = currRoot->getTag(0x83BB); + + if (t && !iptc) { + iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ()); + break; + } + } + fclose (f); } } else if (hasJpegExtension(fname)) { diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index f8ec3e5db..82f5e5525 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -2116,6 +2116,7 @@ void ExifManager::parseCIFF () } parseCIFF (rml->ciffLength, root); root->sort (); + parse(true); } Tag* ExifManager::saveCIFFMNTag (TagDirectory* root, int len, const char* name) From c2405585cf048e69e13361f30f8f7f0699b9f3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Sun, 10 Mar 2019 14:56:36 +0100 Subject: [PATCH 07/13] Work around strange side effects when parsing Exif (#5207) --- rtengine/imagedata.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index bb43acd67..a7d2d157f 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -448,8 +448,6 @@ FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* found = true; lens = "Canon " + ldata; } - } else { - found = lens_from_make_and_model(); } } @@ -464,6 +462,10 @@ FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } } + + if (!found) { + lens_from_make_and_model(); + } } else if (!make.compare (0, 6, "PENTAX") || (!make.compare (0, 5, "RICOH") && !model.compare (0, 6, "PENTAX"))) { // ISO at max value supported, check manufacturer specific if (iso_speed == 65535 || iso_speed == 0) { From 9f133fa1d7ee809400f57c4b63c441b77a51d5da Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 10 Mar 2019 16:33:00 +0100 Subject: [PATCH 08/13] Adobe (filmlike) tonecurve: SSE version --- rtengine/curves.h | 94 ++- rtengine/curves.h.save-failed | 1307 +++++++++++++++++++++++++++++++++ rtengine/improcfun.cc | 7 +- 3 files changed, 1396 insertions(+), 12 deletions(-) create mode 100644 rtengine/curves.h.save-failed diff --git a/rtengine/curves.h b/rtengine/curves.h index b88a3bdc4..0d5e6374b 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -887,9 +887,14 @@ class AdobeToneCurve : public ToneCurve { private: void RGBTone(float& r, float& g, float& b) const; // helper for tone curve - +#ifdef __SSE2__ + void RGBTone(vfloat& r, vfloat& g, vfloat& b) const; // helper for tone curve +#endif public: void Apply(float& r, float& g, float& b) const; + void BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const; }; class SatAndValueBlendingToneCurve : public ToneCurve @@ -1022,7 +1027,7 @@ inline void AdobeToneCurve::Apply (float& ir, float& ig, float& ib) const RGBTone (b, r, g); // Case 2: b > r >= g } else if (b > g) { RGBTone (r, b, g); // Case 3: r >= b > g - } else { // Case 4: r >= g == b + } else { // Case 4: r == g == b r = lutToneCurve[r]; g = lutToneCurve[g]; b = g; @@ -1040,15 +1045,88 @@ inline void AdobeToneCurve::Apply (float& ir, float& ig, float& ib) const setUnlessOOG(ir, ig, ib, r, g, b); } -inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const -{ - float rold = r, gold = g, bold = b; +inline void AdobeToneCurve::BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const { + assert (lutToneCurve); + assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); + assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); - r = lutToneCurve[rold]; - b = lutToneCurve[bold]; - g = b + ((r - b) * (gold - bold) / (rold - bold)); + // All pointers must have the same alignment for SSE usage. In the loop body below, + // we will only check `r`, assuming that the same result would hold for `g` and `b`. + assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); + assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); + + size_t i = start; + while (true) { + if (i >= end) { + // If we get to the end before getting to an aligned address, just return. + // (Or, for non-SSE mode, if we get to the end.) + return; +#ifdef __SSE2__ + } else if (reinterpret_cast(&r[i]) % 16 == 0) { + // Otherwise, we get to the first aligned address; go to the SSE part. + break; +#endif + } + Apply(r[i], g[i], b[i]); + i++; + } +#ifdef __SSE2__ + const vfloat upperv = F2V(MAXVALF); + for (; i + 3 < end; i += 4) { + + vfloat rc = vclampf(LVF(r[i]), ZEROV, upperv); + vfloat gc = vclampf(LVF(g[i]), ZEROV, upperv); + vfloat bc = vclampf(LVF(b[i]), ZEROV, upperv); + + vfloat minval = vminf(vminf(rc, gc), bc); + vfloat maxval = vmaxf(vmaxf(rc, gc), bc); + vfloat medval = vmaxf(vminf(rc, gc), vminf(bc, vmaxf(rc, gc))); + + const vfloat minvalold = minval; + const vfloat maxvalold = maxval; + + RGBTone(maxval, medval, minval); + + const vfloat nr = vself(vmaskf_eq(rc, maxvalold), maxval, vself(vmaskf_eq(rc, minvalold), minval, medval)); + const vfloat ng = vself(vmaskf_eq(gc, maxvalold), maxval, vself(vmaskf_eq(gc, minvalold), minval, medval)); + const vfloat nb = vself(vmaskf_eq(bc, maxvalold), maxval, vself(vmaskf_eq(bc, minvalold), minval, medval)); + + rc = LVF(r[i]); + gc = LVF(g[i]); + bc = LVF(b[i]); + setUnlessOOG(rc, gc, bc, nr, ng, nb); + STVF(r[i], rc); + STVF(g[i], gc); + STVF(b[i], bc); + } + // Remainder in non-SSE. + for (; i < end; ++i) { + Apply(r[i], g[i], b[i]); + } +#endif } +inline void AdobeToneCurve::RGBTone (float& maxval, float& medval, float& minval) const +{ + float minvalold = minval, medvalold = medval, maxvalold = maxval; + + maxval = lutToneCurve[maxvalold]; + minval = lutToneCurve[minvalold]; + medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); +} +#ifdef __SSE2__ +inline void AdobeToneCurve::RGBTone (vfloat& maxval, vfloat& medval, vfloat& minval) const +{ + const vfloat minvalold = minval, maxvalold = maxval; + + maxval = lutToneCurve[maxvalold]; + minval = lutToneCurve[minvalold]; + medval = minval + ((maxval - minval) * (medval - minvalold) / (maxvalold - minvalold)); + medval = vself(vmaskf_eq(minvalold, maxvalold), minval, medval); +} +#endif // Modifying the Luminance channel only inline void LuminanceToneCurve::Apply(float &ir, float &ig, float &ib) const { diff --git a/rtengine/curves.h.save-failed b/rtengine/curves.h.save-failed new file mode 100644 index 000000000..a5aad4a98 --- /dev/null +++ b/rtengine/curves.h.save-failed @@ -0,0 +1,1307 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 . + */ +#ifndef __CURVES_H__ +#define __CURVES_H__ + +#include +#include +#include + +#include + +#include "rt_math.h" +#include "../rtgui/mycurve.h" +#include "../rtgui/myflatcurve.h" +#include "../rtgui/mydiagonalcurve.h" +#include "color.h" +#include "procparams.h" +#include "pipettebuffer.h" + +#include "LUT.h" + +#define CURVES_MIN_POLY_POINTS 1000 + +#include "rt_math.h" + +#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0) + +using namespace std; + +namespace rtengine +{ + +class ToneCurve; +class ColorAppearance; + +inline void setUnlessOOG(float &r, float &g, float &b, const float &rr, const float &gg, const float &bb) +{ + if (!OOG(r) || !OOG(g) || !OOG(b)) { + r = rr; + g = gg; + b = bb; + } +} + +#ifdef __SSE2__ +inline vmask OOG(const vfloat val) +{ + return vorm(vmaskf_lt(val, ZEROV), vmaskf_gt(val, F2V(65535.f))); +} + + +inline void setUnlessOOG(vfloat &r, vfloat &g, vfloat &b, const vfloat rr, const vfloat gg, const vfloat bb) +{ + vmask cond = vandm(vandm(OOG(r), OOG(g)), OOG(b)); + r = vself(cond, r, rr); + g = vself(cond, g, gg); + b = vself(cond, b, bb); +} +#endif + +bool sanitizeCurve(std::vector& curve); + +namespace curves { + +inline void setLutVal(const LUTf &lut, float &val) +{ + if (!OOG(val)) { + val = lut[std::max(val, 0.f)]; + } else if (val < 0.f) { + float m = lut[0.f]; + val += m; + } else { + float m = lut[MAXVALF]; + val += (m - MAXVALF); + } +} + + +inline void setLutVal(const LUTf &lut, float &rval, float &gval, float &bval) +{ + if (!OOG(rval) || !OOG(gval) || !OOG(bval)) { + rval = lut[std::max(rval, 0.f)]; + gval = lut[std::max(gval, 0.f)]; + bval = lut[std::max(bval, 0.f)]; + } else { + setLutVal(lut, rval); + setLutVal(lut, gval); + setLutVal(lut, bval); + } +} + + +inline void setLutVal(float &val, float lutval, float maxval) +{ + if (!OOG(val)) { + val = lutval; + } else if (val > 0.f) { + val += maxval - MAXVALF; + } +} + + +} // namespace curves + +class CurveFactory +{ + + friend class Curve; + +protected: + + // functions calculating the parameters of the contrast curve based on the desired slope at the center + static double solve_upper (double m, double c, double deriv); + static double solve_lower (double m, double c, double deriv); + static double dupper (const double b, const double m, const double c); + static double dlower (const double b, const double m, const double c); + + // basic convex function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point + static inline double basel (double x, double m1, double m2) + { + if (x == 0.0) { + return 0.0; + } + + double k = sqrt ((m1 - 1.0) * (m1 - m2) * 0.5) / (1.0 - m2); + double l = (m1 - m2) / (1.0 - m2) + k; + double lx = xlog(x); + return m2 * x + (1.0 - m2) * (2.0 - xexp(k * lx)) * xexp(l * lx); + } + // basic concave function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point + static inline double baseu (double x, double m1, double m2) + { + return 1.0 - basel(1.0 - x, m1, m2); + } + // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery + static inline double cupper (double x, double m, double hr) + { + if (hr > 1.0) { + return baseu (x, m, 2.0 * (hr - 1.0) / m); + } + + double x1 = (1.0 - hr) / m; + double x2 = x1 + hr; + + if (x >= x2) { + return 1.0; + } + + if (x < x1) { + return x * m; + } + + return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0); + } + // concave curve between (0,0) and (1,1) with slope m at (1,1). sr controls the shadow recovery + static inline double clower (double x, double m, double sr) + { + return 1.0 - cupper(1.0 - x, m, sr); + } + // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery + static inline double cupper2 (double x, double m, double hr) + { + double x1 = (1.0 - hr) / m; + double x2 = x1 + hr; + + if (x >= x2) { + return 1.0; + } + + if (x < x1) { + return x * m; + } + + return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0.3 * hr); + } + static inline double clower2 (double x, double m, double sr) + { + //curve for b<0; starts with positive slope and then rolls over toward straight line to x=y=1 + double x1 = sr / 1.5 + 0.00001; + + if (x > x1 || sr < 0.001) { + return 1 - (1 - x) * m; + } else { + double y1 = 1 - (1 - x1) * m; + return y1 + m * (x - x1) - (1 - m) * SQR(SQR(1 - x / x1)); + } + } + // tone curve base. a: slope (from exp.comp.), b: black point normalized by 65535, + // D: max. x value (can be>1), hr,sr: highlight,shadow recovery + static inline double basecurve (double x, double a, double b, double D, double hr, double sr) + { + if (b < 0) { + double m = 0.5;//midpoint + double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) + double y = -b + m * slope; //value at midpoint + + if (x > m) { + return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) + } else { + return y * clower2(x / m, slope * m / y, 2.0 - sr); + } + } else { + double slope = a / (1.0 - b); + double m = a * D > 1.0 ? b / a + (0.25) / slope : b + (1 - b) / 4; + double y = a * D > 1.0 ? 0.25 : (m - b / a) * slope; + + if (x <= m) { + return b == 0 ? x * slope : clower (x / m, slope * m / y, sr) * y; + } else if (a * D > 1.0) { + return y + (1.0 - y) * cupper2((x - m) / (D - m), slope * (D - m) / (1.0 - y), hr); + } else { + return y + (x - m) * slope; + } + } + } + static inline double simplebasecurve (double x, double b, double sr) + { + // a = 1, D = 1, hr = 0 (unused for a = D = 1) + if (b == 0.0) { + return x; + } else if (b < 0) { + double m = 0.5;//midpoint + double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) + double y = -b + m * slope; //value at midpoint + + if (x > m) { + return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) + } else { + return y * clower2(x / m, slope * m / y, 2.0 - sr); + } + } else { + double slope = 1.0 / (1.0 - b); + double m = b + (1 - b) * 0.25; + double y = (m - b) * slope; + + if (x <= m) { + return clower (x / m, slope * m / y, sr) * y; + } else { + return y + (x - m) * slope; + } + } + } + + +public: + const static double sRGBGamma; // standard average gamma + const static double sRGBGammaCurve; // 2.4 in the curve + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // accurately determine value from integer array with float as index + //linearly interpolate from ends of range if arg is out of bounds + static inline float interp(int *array, float f) + { + int index = CLIPI(floor(f)); + float part = (float)((f) - index) * (float)(array[index + 1] - array[index]); + return (float)array[index] + part; + } + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // accurately determine value from float array with float as index + //linearly interpolate from ends of range if arg is out of bounds + static inline float flinterp(float *array, float f) + { + int index = CLIPI(floor(f)); + float part = ((f) - (float)index) * (array[index + 1] - array[index]); + return array[index] + part; + } + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + static inline double centercontrast (double x, double b, double m); + + // standard srgb gamma and its inverse + static inline double gamma2 (double x) + { + return x <= 0.00304 ? x * 12.92310 : 1.055 * exp(log(x) / sRGBGammaCurve) - 0.055; + } + static inline double igamma2 (double x) + { + return x <= 0.03928 ? x / 12.92310 : exp(log((x + 0.055) / 1.055) * sRGBGammaCurve); + } + static inline float gamma2 (float x) + { + return x <= 0.00304 ? x * 12.92310 : 1.055 * expf(logf(x) / sRGBGammaCurve) - 0.055; + } + static inline float igamma2 (float x) + { + return x <= 0.03928 ? x / 12.92310 : expf(logf((x + 0.055) / 1.055) * sRGBGammaCurve); + } + // gamma function with adjustable parameters + static inline double gamma (double x, double gamma, double start, double slope, double mul, double add) + { + return (x <= start ? x*slope : exp(log(x) / gamma) * mul - add); + } + static inline double igamma (double x, double gamma, double start, double slope, double mul, double add) + { + return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma) ); + } + static inline float gamma (float x, float gamma, float start, float slope, float mul, float add) + { + return (x <= start ? x*slope : xexpf(xlogf(x) / gamma) * mul - add); + } + static inline float igamma (float x, float gamma, float start, float slope, float mul, float add) + { + return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); + } +#ifdef __SSE2__ + static inline vfloat igamma (vfloat x, vfloat gamma, vfloat start, vfloat slope, vfloat mul, vfloat add) + { +#if !defined(__clang__) + return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); +#else + return vself(vmaskf_le(x, start * slope), x / slope, xexpf(xlogf((x + add) / mul) * gamma)); +#endif + } +#endif + static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level) + { + if (comp > 0.0) { + float val = level + (hlrange - 65536.0); + + if(val == 0.0f) { // to avoid division by zero + val = 0.000001f; + } + + float Y = val * exp_scale / hlrange; + Y *= comp; + + if(Y <= -1.0) { // to avoid log(<=0) + Y = -.999999f; + } + + float R = hlrange / (val * comp); + return log1p(Y) * R; + } else { + return exp_scale; + } + } + +public: + static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr, + const std::vector& curvePoints, const std::vector& curvePoints2, + LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2, + + int skip = 1); + static void curveBW (const std::vector& curvePointsbw, const std::vector& curvePointsbw2, const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw, + ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip); + + static void curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip); + + static void curveWavContL ( bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve,/* LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip); + static void curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); + static void mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); + + static void curveToning ( const std::vector& curvePoints, LUTf & ToningCurve, int skip); + + static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints, + const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve, + int skip = 1); + static void complexLCurve (double br, double contr, const std::vector& curvePoints, const LUTu & histogram, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili); + + static void curveLightBrightColor ( + const std::vector& curvePoints, + const std::vector& curvePoints2, + const std::vector& curvePoints3, + const LUTu & histogram, LUTu & outBeforeCCurveHistogram, + const LUTu & histogramC, LUTu & outBeforeCCurveHistogramC, + ColorAppearance & outColCurve1, + ColorAppearance & outColCurve2, + ColorAppearance & outColCurve3, + int skip = 1); + static void RGBCurve (const std::vector& curvePoints, LUTf & outCurve, int skip); + +}; + +class Curve +{ + + class HashEntry + { + public: + unsigned short smallerValue; + unsigned short higherValue; + }; +protected: + int N; + int ppn; // targeted polyline point number + double* x; + double* y; + // begin of variables used in Parametric curves only + double mc; + double mfc; + double msc; + double mhc; + // end of variables used in Parametric curves only + std::vector poly_x; // X points of the faceted curve + std::vector poly_y; // Y points of the faceted curve + std::vector dyByDx; + std::vector hash; + unsigned short hashSize; // hash table's size, between [10, 100, 1000] + + double* ypp; + + // Fields for the elementary curve polygonisation + double x1, y1, x2, y2, x3, y3; + bool firstPointIncluded; + double increment; + int nbr_points; + + static inline double p00 (double x, double prot) + { + return CurveFactory::clower (x, 2.0, prot); + } + static inline double p11 (double x, double prot) + { + return CurveFactory::cupper (x, 2.0, prot); + } + static inline double p01 (double x, double prot) + { + return x <= 0.5 ? CurveFactory::clower (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::cupper ((x - 0.5) * 2, 2.0, prot) * 0.5; + } + static inline double p10 (double x, double prot) + { + return x <= 0.5 ? CurveFactory::cupper (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::clower ((x - 0.5) * 2, 2.0, prot) * 0.5; + } + static inline double pfull (double x, double prot, double sh, double hl) + { + return (1 - sh) * (1 - hl) * p00(x, prot) + sh * hl * p11(x, prot) + (1 - sh) * hl * p01(x, prot) + sh * (1 - hl) * p10(x, prot); + } + + void fillHash(); + void fillDyByDx(); + +public: + Curve (); + virtual ~Curve () {}; + void AddPolygons (); + int getSize () const; // return the number of control points + void getControlPoint(int cpNum, double &x, double &y) const; + virtual double getVal (double t) const = 0; + virtual void getVal (const std::vector& t, std::vector& res) const = 0; + + virtual bool isIdentity () const = 0; +}; + +class DiagonalCurve : public Curve +{ + +protected: + DiagonalCurveType kind; + + void spline_cubic_set (); + void catmull_rom_set(); + void NURBS_set (); + +public: + DiagonalCurve (const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); + ~DiagonalCurve () override; + + double getVal (double t) const override; + void getVal (const std::vector& t, std::vector& res) const override; + bool isIdentity () const override + { + return kind == DCT_Empty; + }; +}; + +class FlatCurve : public Curve +{ + +private: + FlatCurveType kind; + double* leftTangent; + double* rightTangent; + double identityValue; + bool periodic; + + void CtrlPoints_set (); + +public: + + FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); + ~FlatCurve () override; + + double getVal (double t) const override; + void getVal (const std::vector& t, std::vector& res) const override; + bool setIdentityValue (double iVal); + bool isIdentity () const override + { + return kind == FCT_Empty; + }; +}; + +class RetinextransmissionCurve +{ +private: + LUTf luttransmission; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~RetinextransmissionCurve() {}; + RetinextransmissionCurve(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return luttransmission[index]; + } + + operator bool (void) const + { + return luttransmission; + } +}; + +class RetinexgaintransmissionCurve +{ +private: + LUTf lutgaintransmission; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~RetinexgaintransmissionCurve() {}; + RetinexgaintransmissionCurve(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutgaintransmission[index]; + } + + operator bool (void) const + { + return lutgaintransmission; + } +}; + + + +class ToneCurve +{ +public: + LUTf lutToneCurve; // 0xffff range + + virtual ~ToneCurve() {}; + + void Reset(); + void Set(const Curve &pCurve, float gamma = 0); + operator bool (void) const + { + return lutToneCurve; + } +}; + +class OpacityCurve +{ +public: + LUTf lutOpacityCurve; // 0xffff range + + virtual ~OpacityCurve() {}; + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints, bool &opautili); + + // TODO: transfer this method to the Color class... + float blend (float x, float lower, float upper) const + { + return (upper - lower) * lutOpacityCurve[x * 500.f] + lower; + } + void blend3f (float x, float lower1, float upper1, float &result1, float lower2, float upper2, float &result2, float lower3, float upper3, float &result3) const + { + float opacity = lutOpacityCurve[x * 500.f]; + result1 = (upper1 - lower1) * opacity + lower1; + result2 = (upper2 - lower2) * opacity + lower2; + result3 = (upper3 - lower3) * opacity + lower3; + } + + operator bool (void) const + { + return lutOpacityCurve; + } +}; + +class WavCurve +{ +private: + LUTf lutWavCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~WavCurve() {}; + WavCurve(); + void Reset(); + void Set(const std::vector &curvePoints); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutWavCurve[index]; + } + operator bool (void) const + { + return lutWavCurve; + } +}; + +class WavOpacityCurveRG +{ +private: + LUTf lutOpacityCurveRG; // 0xffff range + void Set(const Curve &pCurve); +public: + virtual ~WavOpacityCurveRG() {}; + WavOpacityCurveRG(); + + void Reset(); + // void Set(const std::vector &curvePoints, bool &opautili); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveRG[index]; + } + + operator bool (void) const + { + return lutOpacityCurveRG; + } +}; +class WavOpacityCurveBY +{ +private: + LUTf lutOpacityCurveBY; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~WavOpacityCurveBY() {}; + WavOpacityCurveBY(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveBY[index]; + } + + operator bool (void) const + { + return lutOpacityCurveBY; + } +}; +class WavOpacityCurveW +{ +private: + LUTf lutOpacityCurveW; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~WavOpacityCurveW() {}; + WavOpacityCurveW(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveW[index]; + } + + operator bool (void) const + { + return lutOpacityCurveW; + } +}; + +class WavOpacityCurveWL +{ +private: + LUTf lutOpacityCurveWL; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~WavOpacityCurveWL() {}; + WavOpacityCurveWL(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveWL[index]; + } + + operator bool (void) const + { + return lutOpacityCurveWL; + } +}; + +class NoiseCurve +{ +private: + LUTf lutNoiseCurve; // 0xffff range + float sum; + void Set(const Curve &pCurve); + +public: + virtual ~NoiseCurve() {}; + NoiseCurve(); + void Reset(); + void Set(const std::vector &curvePoints); + + float getSum() const + { + return sum; + } + float operator[](float index) const + { + return lutNoiseCurve[index]; + } + operator bool (void) const + { + return lutNoiseCurve; + } +}; + +class ColorGradientCurve +{ +public: + LUTf lut1; // [0.;1.] range (float values) + LUTf lut2; // [0.;1.] range (float values) + LUTf lut3; // [0.;1.] range (float values) + double low; + double high; + + virtual ~ColorGradientCurve() {}; + + void Reset(); + void SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], float satur, float lumin); + void SetXYZ(const std::vector &curvePoints, const double xyz_rgb[3][3], float satur, float lumin); + void SetRGB(const Curve *pCurve); + void SetRGB(const std::vector &curvePoints); + + /** + * @brief Get the value of Red, Green and Blue corresponding to the requested index + * @param index value in the [0 ; 1] range + * @param r corresponding red value [0 ; 65535] (return value) + * @param g corresponding green value [0 ; 65535] (return value) + * @param b corresponding blue value [0 ; 65535] (return value) + */ + void getVal(float index, float &r, float &g, float &b) const; + operator bool (void) const + { + return lut1 && lut2 && lut3; + } +}; + +class ColorAppearance +{ +public: + LUTf lutColCurve; // 0xffff range + + virtual ~ColorAppearance() {}; + + void Reset(); + void Set(const Curve &pCurve); + operator bool (void) const + { + return lutColCurve; + } +}; + +class Lightcurve : public ColorAppearance +{ +public: + void Apply(float& Li) const; +}; + +//lightness curve +inline void Lightcurve::Apply (float& Li) const +{ + + assert (lutColCurve); + + curves::setLutVal(lutColCurve, Li); +} + +class Brightcurve : public ColorAppearance +{ +public: + void Apply(float& Br) const; +}; + +//brightness curve +inline void Brightcurve::Apply (float& Br) const +{ + + assert (lutColCurve); + + curves::setLutVal(lutColCurve, Br); +} + +class Chromacurve : public ColorAppearance +{ +public: + void Apply(float& Cr) const; +}; + +//Chroma curve +inline void Chromacurve::Apply (float& Cr) const +{ + + assert (lutColCurve); + + curves::setLutVal(lutColCurve, Cr); +} +class Saturcurve : public ColorAppearance +{ +public: + void Apply(float& Sa) const; +}; + +//Saturation curve +inline void Saturcurve::Apply (float& Sa) const +{ + + assert (lutColCurve); + + curves::setLutVal(lutColCurve, Sa); +} + +class Colorfcurve : public ColorAppearance +{ +public: + void Apply(float& Cf) const; +}; + +//Colorfullness curve +inline void Colorfcurve::Apply (float& Cf) const +{ + + assert (lutColCurve); + + curves::setLutVal(lutColCurve, Cf); +} + + +class StandardToneCurve : public ToneCurve +{ +public: + void Apply(float& r, float& g, float& b) const; + + // Applies the tone curve to `r`, `g`, `b` arrays, starting at `r[start]` + // and ending at `r[end]` (and respectively for `b` and `g`). Uses SSE + // and requires that `r`, `g`, and `b` pointers have the same alignment. + void BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const; +}; + +class AdobeToneCurve : public ToneCurve +{ +private: + void RGBTone(float& r, float& g, float& b) const; // helper for tone curve +#ifdef __SSE2__ + void RGBTone(vfloat& r, vfloat& g, vfloat& b) const; // helper for tone curve +#endif +public: + void Apply(float& r, float& g, float& b) const; +#ifdef __SSE2__ + void Apply(vfloat& r, vfloat& g, vfloat& b) const; +#endif +}; + +class SatAndValueBlendingToneCurve : public ToneCurve +{ +public: + void Apply(float& r, float& g, float& b) const; +}; + +class WeightedStdToneCurve : public ToneCurve +{ +private: + float Triangle(float refX, float refY, float X2) const; +#ifdef __SSE2__ + vfloat Triangle(vfloat refX, vfloat refY, vfloat X2) const; +#endif +public: + void Apply(float& r, float& g, float& b) const; + void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const; +}; + +class LuminanceToneCurve : public ToneCurve +{ +public: + void Apply(float& r, float& g, float& b) const; +}; + +class PerceptualToneCurveState +{ +public: + float Working2Prophoto[3][3]; + float Prophoto2Working[3][3]; + float cmul_contrast; + bool isProphoto; +}; + +// Tone curve whose purpose is to keep the color appearance constant, that is the curve changes contrast +// but colors appears to have the same hue and saturation as before. As contrast and saturation is tightly +// coupled in human vision saturation is modulated based on the curve's contrast, and that way the appearance +// can be kept perceptually constant (within limits). +class PerceptualToneCurve : public ToneCurve +{ +private: + static float cf_range[2]; + static float cf[1000]; + // for ciecam02 + static float f, c, nc, yb, la, xw, yw, zw; + static float n, d, nbb, ncb, cz, aw, wh, pfl, fl, pow1; + + static void cubic_spline(const float x[], const float y[], const int len, const float out_x[], float out_y[], const int out_len); + static float find_minimum_interval_halving(float (*func)(float x, void *arg), void *arg, float a, float b, float tol, int nmax); + static float find_tc_slope_fun(float k, void *arg); + static float get_curve_val(float x, float range[2], float lut[], size_t lut_size); + float calculateToneCurveContrastValue() const; +public: + static void init(); + void initApplyState(PerceptualToneCurveState & state, Glib::ustring workingSpace) const; + void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b, const PerceptualToneCurveState &state) const; +}; + +// Standard tone curve +inline void StandardToneCurve::Apply (float& r, float& g, float& b) const +{ + + assert (lutToneCurve); + + curves::setLutVal(lutToneCurve, r, g, b); +} + +inline void StandardToneCurve::BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const { + assert (lutToneCurve); + assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); + assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); + + // All pointers must have the same alignment for SSE usage. In the loop body below, + // we will only check `r`, assuming that the same result would hold for `g` and `b`. + assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); + assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); + + size_t i = start; + while (true) { + if (i >= end) { + // If we get to the end before getting to an aligned address, just return. + // (Or, for non-SSE mode, if we get to the end.) + return; +#ifdef __SSE2__ + } else if (reinterpret_cast(&r[i]) % 16 == 0) { + // Otherwise, we get to the first aligned address; go to the SSE part. + break; +#endif + } + setUnlessOOG(r[i], g[i], b[i], lutToneCurve[r[i]], lutToneCurve[g[i]], lutToneCurve[b[i]]); + i++; + } + +#ifdef __SSE2__ + for (; i + 3 < end; i += 4) { + vfloat r_val = LVF(r[i]); + vfloat g_val = LVF(g[i]); + vfloat b_val = LVF(b[i]); + setUnlessOOG(r_val, g_val, b_val, lutToneCurve[r_val], lutToneCurve[g_val], lutToneCurve[b_val]); + STVF(r[i], r_val); + STVF(g[i], g_val); + STVF(b[i], b_val); + } + + // Remainder in non-SSE. + for (; i < end; ++i) { + setUnlessOOG(r[i], g[i], b[i], lutToneCurve[r[i]], lutToneCurve[g[i]], lutToneCurve[b[i]]); + } +#endif +} + +// Tone curve according to Adobe's reference implementation +// values in 0xffff space +// inlined to make sure there will be no cache flush when used +inline void AdobeToneCurve::Apply (float& ir, float& ig, float& ib) const +{ + + assert (lutToneCurve); + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); + + if (r >= g) { + if (g > b) { + RGBTone (r, g, b); // Case 1: r >= g > b + } else if (b > r) { + RGBTone (b, r, g); // Case 2: b > r >= g + } else if (b > g) { + RGBTone (r, b, g); // Case 3: r >= b > g + } else { // Case 4: r >= g == b + r = lutToneCurve[r]; + g = lutToneCurve[g]; + b = g; + } + } else { + if (r >= b) { + RGBTone (g, r, b); // Case 5: g > r >= b + } else if (b > g) { + RGBTone (b, g, r); // Case 6: b > g > r + } else { + RGBTone (g, b, r); // Case 7: g >= b > r + } + } + + setUnlessOOG(ir, ig, ib, r, g, b); +} + +inline void AdobeToneCurve::BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const { + assert (lutToneCurve); + assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); + assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); + + // All pointers must have the same alignment for SSE usage. In the loop body below, + // we will only check `r`, assuming that the same result would hold for `g` and `b`. + assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); + assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); + + size_t i = start; + while (true) { + if (i >= end) { + // If we get to the end before getting to an aligned address, just return. + // (Or, for non-SSE mode, if we get to the end.) + return; +#ifdef __SSE2__ + } else if (reinterpret_cast(&r[i]) % 16 == 0) { + // Otherwise, we get to the first aligned address; go to the SSE part. + break; +#endif + } + Apply(r[i], g[i], b[i]); + i++; + } +#ifdef __SSE2__ + for (; i + 3 < end; i += 4) { + + vfloat rc = vclampf(LVF(r[i]), ZEROV, F2V(MAXVALF)); + vfloat gc = vclampf(LVF(g[i]), ZEROV, F2V(MAXVALF)); + vfloat bc = vclampf(LVF(b[i]), ZEROV, F2V(MAXVALF)); + + vfloat minval = vminf(vminf(rc, gc), bc); + vfloat maxval = vmaxf(vmaxf(rc, gc), bc); + vfloat medval = vself(vmaskf_eq(rc, minval), vself(vmaskf_eq(gc, maxval), bc, gc), vself(vmaskf_eq(gc, minval), vself(vmaskf_eq(rc, maxval), bc, rc), vself(vmaskf_eq(rc, maxval), gc, rc))); + + vfloat minvalold = minval; + vfloat maxvalold = maxval; + RGBTone(maxval, medval, minval); + + vfloat nr = vself(vmaskf_eq(rc, maxvalold), maxval, vself(vmaskf_eq(rc, minvalold), minval, medval)); + vfloat ng = vself(vmaskf_eq(gc, maxvalold), maxval, vself(vmaskf_eq(gc, minvalold), minval, medval)); + vfloat nb = vself(vmaskf_eq(bc, maxvalold), maxval, vself(vmaskf_eq(bc, minvalold), minval, medval)); + + setUnlessOOG(r[i], g[i], b[i], nr, ng, nb); + } + // Remainder in non-SSE. + for (; i < end; ++i) { + Apply(r[i], g[i], b[i]); + } +#endif +} + +inline void AdobeToneCurve::RGBTone (float& maxval, float& medval, float& minval) const +{ + float minvalold = minval, medvalold = medval, maxvalold = maxval; + + maxval = lutToneCurve[maxvalold]; + minval = lutToneCurve[minvalold]; + medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); +} +#ifdef __SSE2__ +inline void AdobeToneCurve::RGBTone (vfloat& maxval, vfloat& medval, vfloat& minval) const +{ + vfloat minvalold = minval, medvalold = medval, maxvalold = maxval; + + maxval = lutToneCurve[maxvalold]; + minval = lutToneCurve[minvalold]; + medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); +} +#endif +// Modifying the Luminance channel only +inline void LuminanceToneCurve::Apply(float &ir, float &ig, float &ib) const +{ + assert (lutToneCurve); + + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); + + float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; + const float newLuminance = lutToneCurve[currLuminance]; + currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; + const float coef = newLuminance / currLuminance; + r = LIM(r * coef, 0.f, 65535.f); + g = LIM(g * coef, 0.f, 65535.f); + b = LIM(b * coef, 0.f, 65535.f); + + setUnlessOOG(ir, ig, ib, r, g, b); +} + +inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const +{ + if (a != b) { + float b1; + float a2 = a1 - a; + + if (b < a) { + b1 = b + a2 * b / a ; + } else { + b1 = b + a2 * (65535.f - b) / (65535.f - a); + } + + return b1; + } + + return a1; +} + +#ifdef __SSE2__ +inline vfloat WeightedStdToneCurve::Triangle(vfloat a, vfloat a1, vfloat b) const +{ + vmask eqmask = vmaskf_eq(b, a); + vfloat a2 = a1 - a; + vmask cmask = vmaskf_lt(b, a); + vfloat b3 = vself(cmask, b, F2V(65535.f) - b); + vfloat a3 = vself(cmask, a, F2V(65535.f) - a); + return vself(eqmask, a1, b + a2 * b3 / a3); +} +#endif + +// Tone curve modifying the value channel only, preserving hue and saturation +// values in 0xffff space +inline void WeightedStdToneCurve::Apply (float& ir, float& ig, float& ib) const +{ + + assert (lutToneCurve); + + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); + float r1 = lutToneCurve[r]; + float g1 = Triangle(r, r1, g); + float b1 = Triangle(r, r1, b); + + float g2 = lutToneCurve[g]; + float r2 = Triangle(g, g2, r); + float b2 = Triangle(g, g2, b); + + float b3 = lutToneCurve[b]; + float r3 = Triangle(b, b3, r); + float g3 = Triangle(b, b3, g); + + r = CLIP(r1 * 0.50f + r2 * 0.25f + r3 * 0.25f); + g = CLIP(g1 * 0.25f + g2 * 0.50f + g3 * 0.25f); + b = CLIP(b1 * 0.25f + b2 * 0.25f + b3 * 0.50f); + + setUnlessOOG(ir, ig, ib, r, g, b); +} + +inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const { + assert (lutToneCurve); + assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); + assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); + + // All pointers must have the same alignment for SSE usage. In the loop body below, + // we will only check `r`, assuming that the same result would hold for `g` and `b`. + assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); + assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); + + size_t i = start; + while (true) { + if (i >= end) { + // If we get to the end before getting to an aligned address, just return. + // (Or, for non-SSE mode, if we get to the end.) + return; +#ifdef __SSE2__ + } else if (reinterpret_cast(&r[i]) % 16 == 0) { + // Otherwise, we get to the first aligned address; go to the SSE part. + break; +#endif + } + Apply(r[i], g[i], b[i]); + i++; + } + +#ifdef __SSE2__ + const vfloat c65535v = F2V(65535.f); + const vfloat zd5v = F2V(0.5f); + const vfloat zd25v = F2V(0.25f); + + for (; i + 3 < end; i += 4) { + vfloat r_val = vclampf(LVF(r[i]), ZEROV, c65535v); + vfloat g_val = vclampf(LVF(g[i]), ZEROV, c65535v); + vfloat b_val = vclampf(LVF(b[i]), ZEROV, c65535v); + vfloat r1 = lutToneCurve[r_val]; + vfloat g1 = Triangle(r_val, r1, g_val); + vfloat b1 = Triangle(r_val, r1, b_val); + + vfloat g2 = lutToneCurve[g_val]; + vfloat r2 = Triangle(g_val, g2, r_val); + vfloat b2 = Triangle(g_val, g2, b_val); + + vfloat b3 = lutToneCurve[b_val]; + vfloat r3 = Triangle(b_val, b3, r_val); + vfloat g3 = Triangle(b_val, b3, g_val); + + vfloat r_old = LVF(r[i]); + vfloat g_old = LVF(g[i]); + vfloat b_old = LVF(b[i]); + vfloat r_new = vclampf(r1 * zd5v + r2 * zd25v + r3 * zd25v, ZEROV, c65535v); + vfloat g_new = vclampf(g1 * zd25v + g2 * zd5v + g3 * zd25v, ZEROV, c65535v); + vfloat b_new = vclampf(b1 * zd25v + b2 * zd25v + b3 * zd5v, ZEROV, c65535v); + setUnlessOOG(r_old, g_old, b_old, r_new, g_new, b_new); + STVF(r[i], r_old); + STVF(g[i], g_old); + STVF(b[i], b_old); + } + + // Remainder in non-SSE. + for (; i < end; ++i) { + Apply(r[i], g[i], b[i]); + } +#endif +} + +// Tone curve modifying the value channel only, preserving hue and saturation +// values in 0xffff space +inline void SatAndValueBlendingToneCurve::Apply (float& ir, float& ig, float& ib) const +{ + + assert (lutToneCurve); + + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); + + const float lum = (r + g + b) / 3.f; + const float newLum = lutToneCurve[lum]; + + if (newLum == lum) { + return; + } + + float h, s, v; + Color::rgb2hsvtc(r, g, b, h, s, v); + + float dV; + if (newLum > lum) { + // Linearly targeting Value = 1 and Saturation = 0 + const float coef = (newLum - lum) / (65535.f - lum); + dV = (1.f - v) * coef; + s *= 1.f - coef; + } else { + // Linearly targeting Value = 0 + const float coef = (newLum - lum) / lum ; + dV = v * coef; + } + Color::hsv2rgbdcp(h, s, v + dV, r, g, b); + + setUnlessOOG(ir, ig, ib, r, g, b); +} + +} + +#undef CLIPI + +#endif diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 465e3ffe8..e751ef9b8 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -40,6 +40,7 @@ #include "improccoordinator.h" #include "clutstore.h" #include "ciecam02.h" +#define BENCHMARK #include "StopWatch.h" #include "../rtgui/ppversion.h" #include "../rtgui/guiutils.h" @@ -215,9 +216,7 @@ void customToneCurve(const ToneCurve &customToneCurve, ToneCurveParams::TcMode c } else if (curveMode == ToneCurveParams::TcMode::FILMLIKE) { // Adobe like const AdobeToneCurve& userToneCurve = static_cast (customToneCurve); for (int i = istart, ti = 0; i < tH; i++, ti++) { - for (int j = jstart, tj = 0; j < tW; j++, tj++) { - userToneCurve.Apply(rtemp[ti * tileSize + tj], gtemp[ti * tileSize + tj], btemp[ti * tileSize + tj]); - } + userToneCurve.BatchApply(0, tW - jstart, &rtemp[ti * tileSize], >emp[ti * tileSize], &btemp[ti * tileSize]); } } else if (curveMode == ToneCurveParams::TcMode::SATANDVALBLENDING) { // apply the curve on the saturation and value channels const SatAndValueBlendingToneCurve& userToneCurve = static_cast (customToneCurve); @@ -1985,7 +1984,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw void ImProcFunctions::moyeqt (Imagefloat* working, float &moyS, float &eqty) { - BENCHFUN +// BENCHFUN int tHh = working->getHeight(); int tWw = working->getWidth(); From 0685e262ba2b1209c594040b0bd303779e02207a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 10 Mar 2019 18:02:57 +0100 Subject: [PATCH 09/13] remove accidently committed file, #5208 --- rtengine/curves.h.save-failed | 1307 --------------------------------- 1 file changed, 1307 deletions(-) delete mode 100644 rtengine/curves.h.save-failed diff --git a/rtengine/curves.h.save-failed b/rtengine/curves.h.save-failed deleted file mode 100644 index a5aad4a98..000000000 --- a/rtengine/curves.h.save-failed +++ /dev/null @@ -1,1307 +0,0 @@ -/* - * This file is part of RawTherapee. - * - * Copyright (c) 2004-2010 Gabor Horvath - * - * 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 . - */ -#ifndef __CURVES_H__ -#define __CURVES_H__ - -#include -#include -#include - -#include - -#include "rt_math.h" -#include "../rtgui/mycurve.h" -#include "../rtgui/myflatcurve.h" -#include "../rtgui/mydiagonalcurve.h" -#include "color.h" -#include "procparams.h" -#include "pipettebuffer.h" - -#include "LUT.h" - -#define CURVES_MIN_POLY_POINTS 1000 - -#include "rt_math.h" - -#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0) - -using namespace std; - -namespace rtengine -{ - -class ToneCurve; -class ColorAppearance; - -inline void setUnlessOOG(float &r, float &g, float &b, const float &rr, const float &gg, const float &bb) -{ - if (!OOG(r) || !OOG(g) || !OOG(b)) { - r = rr; - g = gg; - b = bb; - } -} - -#ifdef __SSE2__ -inline vmask OOG(const vfloat val) -{ - return vorm(vmaskf_lt(val, ZEROV), vmaskf_gt(val, F2V(65535.f))); -} - - -inline void setUnlessOOG(vfloat &r, vfloat &g, vfloat &b, const vfloat rr, const vfloat gg, const vfloat bb) -{ - vmask cond = vandm(vandm(OOG(r), OOG(g)), OOG(b)); - r = vself(cond, r, rr); - g = vself(cond, g, gg); - b = vself(cond, b, bb); -} -#endif - -bool sanitizeCurve(std::vector& curve); - -namespace curves { - -inline void setLutVal(const LUTf &lut, float &val) -{ - if (!OOG(val)) { - val = lut[std::max(val, 0.f)]; - } else if (val < 0.f) { - float m = lut[0.f]; - val += m; - } else { - float m = lut[MAXVALF]; - val += (m - MAXVALF); - } -} - - -inline void setLutVal(const LUTf &lut, float &rval, float &gval, float &bval) -{ - if (!OOG(rval) || !OOG(gval) || !OOG(bval)) { - rval = lut[std::max(rval, 0.f)]; - gval = lut[std::max(gval, 0.f)]; - bval = lut[std::max(bval, 0.f)]; - } else { - setLutVal(lut, rval); - setLutVal(lut, gval); - setLutVal(lut, bval); - } -} - - -inline void setLutVal(float &val, float lutval, float maxval) -{ - if (!OOG(val)) { - val = lutval; - } else if (val > 0.f) { - val += maxval - MAXVALF; - } -} - - -} // namespace curves - -class CurveFactory -{ - - friend class Curve; - -protected: - - // functions calculating the parameters of the contrast curve based on the desired slope at the center - static double solve_upper (double m, double c, double deriv); - static double solve_lower (double m, double c, double deriv); - static double dupper (const double b, const double m, const double c); - static double dlower (const double b, const double m, const double c); - - // basic convex function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point - static inline double basel (double x, double m1, double m2) - { - if (x == 0.0) { - return 0.0; - } - - double k = sqrt ((m1 - 1.0) * (m1 - m2) * 0.5) / (1.0 - m2); - double l = (m1 - m2) / (1.0 - m2) + k; - double lx = xlog(x); - return m2 * x + (1.0 - m2) * (2.0 - xexp(k * lx)) * xexp(l * lx); - } - // basic concave function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point - static inline double baseu (double x, double m1, double m2) - { - return 1.0 - basel(1.0 - x, m1, m2); - } - // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery - static inline double cupper (double x, double m, double hr) - { - if (hr > 1.0) { - return baseu (x, m, 2.0 * (hr - 1.0) / m); - } - - double x1 = (1.0 - hr) / m; - double x2 = x1 + hr; - - if (x >= x2) { - return 1.0; - } - - if (x < x1) { - return x * m; - } - - return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0); - } - // concave curve between (0,0) and (1,1) with slope m at (1,1). sr controls the shadow recovery - static inline double clower (double x, double m, double sr) - { - return 1.0 - cupper(1.0 - x, m, sr); - } - // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery - static inline double cupper2 (double x, double m, double hr) - { - double x1 = (1.0 - hr) / m; - double x2 = x1 + hr; - - if (x >= x2) { - return 1.0; - } - - if (x < x1) { - return x * m; - } - - return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0.3 * hr); - } - static inline double clower2 (double x, double m, double sr) - { - //curve for b<0; starts with positive slope and then rolls over toward straight line to x=y=1 - double x1 = sr / 1.5 + 0.00001; - - if (x > x1 || sr < 0.001) { - return 1 - (1 - x) * m; - } else { - double y1 = 1 - (1 - x1) * m; - return y1 + m * (x - x1) - (1 - m) * SQR(SQR(1 - x / x1)); - } - } - // tone curve base. a: slope (from exp.comp.), b: black point normalized by 65535, - // D: max. x value (can be>1), hr,sr: highlight,shadow recovery - static inline double basecurve (double x, double a, double b, double D, double hr, double sr) - { - if (b < 0) { - double m = 0.5;//midpoint - double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) - double y = -b + m * slope; //value at midpoint - - if (x > m) { - return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) - } else { - return y * clower2(x / m, slope * m / y, 2.0 - sr); - } - } else { - double slope = a / (1.0 - b); - double m = a * D > 1.0 ? b / a + (0.25) / slope : b + (1 - b) / 4; - double y = a * D > 1.0 ? 0.25 : (m - b / a) * slope; - - if (x <= m) { - return b == 0 ? x * slope : clower (x / m, slope * m / y, sr) * y; - } else if (a * D > 1.0) { - return y + (1.0 - y) * cupper2((x - m) / (D - m), slope * (D - m) / (1.0 - y), hr); - } else { - return y + (x - m) * slope; - } - } - } - static inline double simplebasecurve (double x, double b, double sr) - { - // a = 1, D = 1, hr = 0 (unused for a = D = 1) - if (b == 0.0) { - return x; - } else if (b < 0) { - double m = 0.5;//midpoint - double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) - double y = -b + m * slope; //value at midpoint - - if (x > m) { - return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) - } else { - return y * clower2(x / m, slope * m / y, 2.0 - sr); - } - } else { - double slope = 1.0 / (1.0 - b); - double m = b + (1 - b) * 0.25; - double y = (m - b) * slope; - - if (x <= m) { - return clower (x / m, slope * m / y, sr) * y; - } else { - return y + (x - m) * slope; - } - } - } - - -public: - const static double sRGBGamma; // standard average gamma - const static double sRGBGammaCurve; // 2.4 in the curve - - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // accurately determine value from integer array with float as index - //linearly interpolate from ends of range if arg is out of bounds - static inline float interp(int *array, float f) - { - int index = CLIPI(floor(f)); - float part = (float)((f) - index) * (float)(array[index + 1] - array[index]); - return (float)array[index] + part; - } - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // accurately determine value from float array with float as index - //linearly interpolate from ends of range if arg is out of bounds - static inline float flinterp(float *array, float f) - { - int index = CLIPI(floor(f)); - float part = ((f) - (float)index) * (array[index + 1] - array[index]); - return array[index] + part; - } - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - static inline double centercontrast (double x, double b, double m); - - // standard srgb gamma and its inverse - static inline double gamma2 (double x) - { - return x <= 0.00304 ? x * 12.92310 : 1.055 * exp(log(x) / sRGBGammaCurve) - 0.055; - } - static inline double igamma2 (double x) - { - return x <= 0.03928 ? x / 12.92310 : exp(log((x + 0.055) / 1.055) * sRGBGammaCurve); - } - static inline float gamma2 (float x) - { - return x <= 0.00304 ? x * 12.92310 : 1.055 * expf(logf(x) / sRGBGammaCurve) - 0.055; - } - static inline float igamma2 (float x) - { - return x <= 0.03928 ? x / 12.92310 : expf(logf((x + 0.055) / 1.055) * sRGBGammaCurve); - } - // gamma function with adjustable parameters - static inline double gamma (double x, double gamma, double start, double slope, double mul, double add) - { - return (x <= start ? x*slope : exp(log(x) / gamma) * mul - add); - } - static inline double igamma (double x, double gamma, double start, double slope, double mul, double add) - { - return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma) ); - } - static inline float gamma (float x, float gamma, float start, float slope, float mul, float add) - { - return (x <= start ? x*slope : xexpf(xlogf(x) / gamma) * mul - add); - } - static inline float igamma (float x, float gamma, float start, float slope, float mul, float add) - { - return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); - } -#ifdef __SSE2__ - static inline vfloat igamma (vfloat x, vfloat gamma, vfloat start, vfloat slope, vfloat mul, vfloat add) - { -#if !defined(__clang__) - return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); -#else - return vself(vmaskf_le(x, start * slope), x / slope, xexpf(xlogf((x + add) / mul) * gamma)); -#endif - } -#endif - static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level) - { - if (comp > 0.0) { - float val = level + (hlrange - 65536.0); - - if(val == 0.0f) { // to avoid division by zero - val = 0.000001f; - } - - float Y = val * exp_scale / hlrange; - Y *= comp; - - if(Y <= -1.0) { // to avoid log(<=0) - Y = -.999999f; - } - - float R = hlrange / (val * comp); - return log1p(Y) * R; - } else { - return exp_scale; - } - } - -public: - static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr, - const std::vector& curvePoints, const std::vector& curvePoints2, - LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2, - - int skip = 1); - static void curveBW (const std::vector& curvePointsbw, const std::vector& curvePointsbw2, const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw, - ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip); - - static void curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip); - - static void curveWavContL ( bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve,/* LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip); - static void curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); - static void mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); - - static void curveToning ( const std::vector& curvePoints, LUTf & ToningCurve, int skip); - - static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints, - const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve, - int skip = 1); - static void complexLCurve (double br, double contr, const std::vector& curvePoints, const LUTu & histogram, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili); - - static void curveLightBrightColor ( - const std::vector& curvePoints, - const std::vector& curvePoints2, - const std::vector& curvePoints3, - const LUTu & histogram, LUTu & outBeforeCCurveHistogram, - const LUTu & histogramC, LUTu & outBeforeCCurveHistogramC, - ColorAppearance & outColCurve1, - ColorAppearance & outColCurve2, - ColorAppearance & outColCurve3, - int skip = 1); - static void RGBCurve (const std::vector& curvePoints, LUTf & outCurve, int skip); - -}; - -class Curve -{ - - class HashEntry - { - public: - unsigned short smallerValue; - unsigned short higherValue; - }; -protected: - int N; - int ppn; // targeted polyline point number - double* x; - double* y; - // begin of variables used in Parametric curves only - double mc; - double mfc; - double msc; - double mhc; - // end of variables used in Parametric curves only - std::vector poly_x; // X points of the faceted curve - std::vector poly_y; // Y points of the faceted curve - std::vector dyByDx; - std::vector hash; - unsigned short hashSize; // hash table's size, between [10, 100, 1000] - - double* ypp; - - // Fields for the elementary curve polygonisation - double x1, y1, x2, y2, x3, y3; - bool firstPointIncluded; - double increment; - int nbr_points; - - static inline double p00 (double x, double prot) - { - return CurveFactory::clower (x, 2.0, prot); - } - static inline double p11 (double x, double prot) - { - return CurveFactory::cupper (x, 2.0, prot); - } - static inline double p01 (double x, double prot) - { - return x <= 0.5 ? CurveFactory::clower (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::cupper ((x - 0.5) * 2, 2.0, prot) * 0.5; - } - static inline double p10 (double x, double prot) - { - return x <= 0.5 ? CurveFactory::cupper (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::clower ((x - 0.5) * 2, 2.0, prot) * 0.5; - } - static inline double pfull (double x, double prot, double sh, double hl) - { - return (1 - sh) * (1 - hl) * p00(x, prot) + sh * hl * p11(x, prot) + (1 - sh) * hl * p01(x, prot) + sh * (1 - hl) * p10(x, prot); - } - - void fillHash(); - void fillDyByDx(); - -public: - Curve (); - virtual ~Curve () {}; - void AddPolygons (); - int getSize () const; // return the number of control points - void getControlPoint(int cpNum, double &x, double &y) const; - virtual double getVal (double t) const = 0; - virtual void getVal (const std::vector& t, std::vector& res) const = 0; - - virtual bool isIdentity () const = 0; -}; - -class DiagonalCurve : public Curve -{ - -protected: - DiagonalCurveType kind; - - void spline_cubic_set (); - void catmull_rom_set(); - void NURBS_set (); - -public: - DiagonalCurve (const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); - ~DiagonalCurve () override; - - double getVal (double t) const override; - void getVal (const std::vector& t, std::vector& res) const override; - bool isIdentity () const override - { - return kind == DCT_Empty; - }; -}; - -class FlatCurve : public Curve -{ - -private: - FlatCurveType kind; - double* leftTangent; - double* rightTangent; - double identityValue; - bool periodic; - - void CtrlPoints_set (); - -public: - - FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); - ~FlatCurve () override; - - double getVal (double t) const override; - void getVal (const std::vector& t, std::vector& res) const override; - bool setIdentityValue (double iVal); - bool isIdentity () const override - { - return kind == FCT_Empty; - }; -}; - -class RetinextransmissionCurve -{ -private: - LUTf luttransmission; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~RetinextransmissionCurve() {}; - RetinextransmissionCurve(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return luttransmission[index]; - } - - operator bool (void) const - { - return luttransmission; - } -}; - -class RetinexgaintransmissionCurve -{ -private: - LUTf lutgaintransmission; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~RetinexgaintransmissionCurve() {}; - RetinexgaintransmissionCurve(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutgaintransmission[index]; - } - - operator bool (void) const - { - return lutgaintransmission; - } -}; - - - -class ToneCurve -{ -public: - LUTf lutToneCurve; // 0xffff range - - virtual ~ToneCurve() {}; - - void Reset(); - void Set(const Curve &pCurve, float gamma = 0); - operator bool (void) const - { - return lutToneCurve; - } -}; - -class OpacityCurve -{ -public: - LUTf lutOpacityCurve; // 0xffff range - - virtual ~OpacityCurve() {}; - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints, bool &opautili); - - // TODO: transfer this method to the Color class... - float blend (float x, float lower, float upper) const - { - return (upper - lower) * lutOpacityCurve[x * 500.f] + lower; - } - void blend3f (float x, float lower1, float upper1, float &result1, float lower2, float upper2, float &result2, float lower3, float upper3, float &result3) const - { - float opacity = lutOpacityCurve[x * 500.f]; - result1 = (upper1 - lower1) * opacity + lower1; - result2 = (upper2 - lower2) * opacity + lower2; - result3 = (upper3 - lower3) * opacity + lower3; - } - - operator bool (void) const - { - return lutOpacityCurve; - } -}; - -class WavCurve -{ -private: - LUTf lutWavCurve; // 0xffff range - void Set(const Curve &pCurve); - -public: - float sum; - - virtual ~WavCurve() {}; - WavCurve(); - void Reset(); - void Set(const std::vector &curvePoints); - float getSum() const - { - return sum; - } - - float operator[](float index) const - { - return lutWavCurve[index]; - } - operator bool (void) const - { - return lutWavCurve; - } -}; - -class WavOpacityCurveRG -{ -private: - LUTf lutOpacityCurveRG; // 0xffff range - void Set(const Curve &pCurve); -public: - virtual ~WavOpacityCurveRG() {}; - WavOpacityCurveRG(); - - void Reset(); - // void Set(const std::vector &curvePoints, bool &opautili); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveRG[index]; - } - - operator bool (void) const - { - return lutOpacityCurveRG; - } -}; -class WavOpacityCurveBY -{ -private: - LUTf lutOpacityCurveBY; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~WavOpacityCurveBY() {}; - WavOpacityCurveBY(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveBY[index]; - } - - operator bool (void) const - { - return lutOpacityCurveBY; - } -}; -class WavOpacityCurveW -{ -private: - LUTf lutOpacityCurveW; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~WavOpacityCurveW() {}; - WavOpacityCurveW(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveW[index]; - } - - operator bool (void) const - { - return lutOpacityCurveW; - } -}; - -class WavOpacityCurveWL -{ -private: - LUTf lutOpacityCurveWL; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~WavOpacityCurveWL() {}; - WavOpacityCurveWL(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveWL[index]; - } - - operator bool (void) const - { - return lutOpacityCurveWL; - } -}; - -class NoiseCurve -{ -private: - LUTf lutNoiseCurve; // 0xffff range - float sum; - void Set(const Curve &pCurve); - -public: - virtual ~NoiseCurve() {}; - NoiseCurve(); - void Reset(); - void Set(const std::vector &curvePoints); - - float getSum() const - { - return sum; - } - float operator[](float index) const - { - return lutNoiseCurve[index]; - } - operator bool (void) const - { - return lutNoiseCurve; - } -}; - -class ColorGradientCurve -{ -public: - LUTf lut1; // [0.;1.] range (float values) - LUTf lut2; // [0.;1.] range (float values) - LUTf lut3; // [0.;1.] range (float values) - double low; - double high; - - virtual ~ColorGradientCurve() {}; - - void Reset(); - void SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], float satur, float lumin); - void SetXYZ(const std::vector &curvePoints, const double xyz_rgb[3][3], float satur, float lumin); - void SetRGB(const Curve *pCurve); - void SetRGB(const std::vector &curvePoints); - - /** - * @brief Get the value of Red, Green and Blue corresponding to the requested index - * @param index value in the [0 ; 1] range - * @param r corresponding red value [0 ; 65535] (return value) - * @param g corresponding green value [0 ; 65535] (return value) - * @param b corresponding blue value [0 ; 65535] (return value) - */ - void getVal(float index, float &r, float &g, float &b) const; - operator bool (void) const - { - return lut1 && lut2 && lut3; - } -}; - -class ColorAppearance -{ -public: - LUTf lutColCurve; // 0xffff range - - virtual ~ColorAppearance() {}; - - void Reset(); - void Set(const Curve &pCurve); - operator bool (void) const - { - return lutColCurve; - } -}; - -class Lightcurve : public ColorAppearance -{ -public: - void Apply(float& Li) const; -}; - -//lightness curve -inline void Lightcurve::Apply (float& Li) const -{ - - assert (lutColCurve); - - curves::setLutVal(lutColCurve, Li); -} - -class Brightcurve : public ColorAppearance -{ -public: - void Apply(float& Br) const; -}; - -//brightness curve -inline void Brightcurve::Apply (float& Br) const -{ - - assert (lutColCurve); - - curves::setLutVal(lutColCurve, Br); -} - -class Chromacurve : public ColorAppearance -{ -public: - void Apply(float& Cr) const; -}; - -//Chroma curve -inline void Chromacurve::Apply (float& Cr) const -{ - - assert (lutColCurve); - - curves::setLutVal(lutColCurve, Cr); -} -class Saturcurve : public ColorAppearance -{ -public: - void Apply(float& Sa) const; -}; - -//Saturation curve -inline void Saturcurve::Apply (float& Sa) const -{ - - assert (lutColCurve); - - curves::setLutVal(lutColCurve, Sa); -} - -class Colorfcurve : public ColorAppearance -{ -public: - void Apply(float& Cf) const; -}; - -//Colorfullness curve -inline void Colorfcurve::Apply (float& Cf) const -{ - - assert (lutColCurve); - - curves::setLutVal(lutColCurve, Cf); -} - - -class StandardToneCurve : public ToneCurve -{ -public: - void Apply(float& r, float& g, float& b) const; - - // Applies the tone curve to `r`, `g`, `b` arrays, starting at `r[start]` - // and ending at `r[end]` (and respectively for `b` and `g`). Uses SSE - // and requires that `r`, `g`, and `b` pointers have the same alignment. - void BatchApply( - const size_t start, const size_t end, - float *r, float *g, float *b) const; -}; - -class AdobeToneCurve : public ToneCurve -{ -private: - void RGBTone(float& r, float& g, float& b) const; // helper for tone curve -#ifdef __SSE2__ - void RGBTone(vfloat& r, vfloat& g, vfloat& b) const; // helper for tone curve -#endif -public: - void Apply(float& r, float& g, float& b) const; -#ifdef __SSE2__ - void Apply(vfloat& r, vfloat& g, vfloat& b) const; -#endif -}; - -class SatAndValueBlendingToneCurve : public ToneCurve -{ -public: - void Apply(float& r, float& g, float& b) const; -}; - -class WeightedStdToneCurve : public ToneCurve -{ -private: - float Triangle(float refX, float refY, float X2) const; -#ifdef __SSE2__ - vfloat Triangle(vfloat refX, vfloat refY, vfloat X2) const; -#endif -public: - void Apply(float& r, float& g, float& b) const; - void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const; -}; - -class LuminanceToneCurve : public ToneCurve -{ -public: - void Apply(float& r, float& g, float& b) const; -}; - -class PerceptualToneCurveState -{ -public: - float Working2Prophoto[3][3]; - float Prophoto2Working[3][3]; - float cmul_contrast; - bool isProphoto; -}; - -// Tone curve whose purpose is to keep the color appearance constant, that is the curve changes contrast -// but colors appears to have the same hue and saturation as before. As contrast and saturation is tightly -// coupled in human vision saturation is modulated based on the curve's contrast, and that way the appearance -// can be kept perceptually constant (within limits). -class PerceptualToneCurve : public ToneCurve -{ -private: - static float cf_range[2]; - static float cf[1000]; - // for ciecam02 - static float f, c, nc, yb, la, xw, yw, zw; - static float n, d, nbb, ncb, cz, aw, wh, pfl, fl, pow1; - - static void cubic_spline(const float x[], const float y[], const int len, const float out_x[], float out_y[], const int out_len); - static float find_minimum_interval_halving(float (*func)(float x, void *arg), void *arg, float a, float b, float tol, int nmax); - static float find_tc_slope_fun(float k, void *arg); - static float get_curve_val(float x, float range[2], float lut[], size_t lut_size); - float calculateToneCurveContrastValue() const; -public: - static void init(); - void initApplyState(PerceptualToneCurveState & state, Glib::ustring workingSpace) const; - void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b, const PerceptualToneCurveState &state) const; -}; - -// Standard tone curve -inline void StandardToneCurve::Apply (float& r, float& g, float& b) const -{ - - assert (lutToneCurve); - - curves::setLutVal(lutToneCurve, r, g, b); -} - -inline void StandardToneCurve::BatchApply( - const size_t start, const size_t end, - float *r, float *g, float *b) const { - assert (lutToneCurve); - assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); - assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); - - // All pointers must have the same alignment for SSE usage. In the loop body below, - // we will only check `r`, assuming that the same result would hold for `g` and `b`. - assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); - assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); - - size_t i = start; - while (true) { - if (i >= end) { - // If we get to the end before getting to an aligned address, just return. - // (Or, for non-SSE mode, if we get to the end.) - return; -#ifdef __SSE2__ - } else if (reinterpret_cast(&r[i]) % 16 == 0) { - // Otherwise, we get to the first aligned address; go to the SSE part. - break; -#endif - } - setUnlessOOG(r[i], g[i], b[i], lutToneCurve[r[i]], lutToneCurve[g[i]], lutToneCurve[b[i]]); - i++; - } - -#ifdef __SSE2__ - for (; i + 3 < end; i += 4) { - vfloat r_val = LVF(r[i]); - vfloat g_val = LVF(g[i]); - vfloat b_val = LVF(b[i]); - setUnlessOOG(r_val, g_val, b_val, lutToneCurve[r_val], lutToneCurve[g_val], lutToneCurve[b_val]); - STVF(r[i], r_val); - STVF(g[i], g_val); - STVF(b[i], b_val); - } - - // Remainder in non-SSE. - for (; i < end; ++i) { - setUnlessOOG(r[i], g[i], b[i], lutToneCurve[r[i]], lutToneCurve[g[i]], lutToneCurve[b[i]]); - } -#endif -} - -// Tone curve according to Adobe's reference implementation -// values in 0xffff space -// inlined to make sure there will be no cache flush when used -inline void AdobeToneCurve::Apply (float& ir, float& ig, float& ib) const -{ - - assert (lutToneCurve); - float r = CLIP(ir); - float g = CLIP(ig); - float b = CLIP(ib); - - if (r >= g) { - if (g > b) { - RGBTone (r, g, b); // Case 1: r >= g > b - } else if (b > r) { - RGBTone (b, r, g); // Case 2: b > r >= g - } else if (b > g) { - RGBTone (r, b, g); // Case 3: r >= b > g - } else { // Case 4: r >= g == b - r = lutToneCurve[r]; - g = lutToneCurve[g]; - b = g; - } - } else { - if (r >= b) { - RGBTone (g, r, b); // Case 5: g > r >= b - } else if (b > g) { - RGBTone (b, g, r); // Case 6: b > g > r - } else { - RGBTone (g, b, r); // Case 7: g >= b > r - } - } - - setUnlessOOG(ir, ig, ib, r, g, b); -} - -inline void AdobeToneCurve::BatchApply( - const size_t start, const size_t end, - float *r, float *g, float *b) const { - assert (lutToneCurve); - assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); - assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); - - // All pointers must have the same alignment for SSE usage. In the loop body below, - // we will only check `r`, assuming that the same result would hold for `g` and `b`. - assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); - assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); - - size_t i = start; - while (true) { - if (i >= end) { - // If we get to the end before getting to an aligned address, just return. - // (Or, for non-SSE mode, if we get to the end.) - return; -#ifdef __SSE2__ - } else if (reinterpret_cast(&r[i]) % 16 == 0) { - // Otherwise, we get to the first aligned address; go to the SSE part. - break; -#endif - } - Apply(r[i], g[i], b[i]); - i++; - } -#ifdef __SSE2__ - for (; i + 3 < end; i += 4) { - - vfloat rc = vclampf(LVF(r[i]), ZEROV, F2V(MAXVALF)); - vfloat gc = vclampf(LVF(g[i]), ZEROV, F2V(MAXVALF)); - vfloat bc = vclampf(LVF(b[i]), ZEROV, F2V(MAXVALF)); - - vfloat minval = vminf(vminf(rc, gc), bc); - vfloat maxval = vmaxf(vmaxf(rc, gc), bc); - vfloat medval = vself(vmaskf_eq(rc, minval), vself(vmaskf_eq(gc, maxval), bc, gc), vself(vmaskf_eq(gc, minval), vself(vmaskf_eq(rc, maxval), bc, rc), vself(vmaskf_eq(rc, maxval), gc, rc))); - - vfloat minvalold = minval; - vfloat maxvalold = maxval; - RGBTone(maxval, medval, minval); - - vfloat nr = vself(vmaskf_eq(rc, maxvalold), maxval, vself(vmaskf_eq(rc, minvalold), minval, medval)); - vfloat ng = vself(vmaskf_eq(gc, maxvalold), maxval, vself(vmaskf_eq(gc, minvalold), minval, medval)); - vfloat nb = vself(vmaskf_eq(bc, maxvalold), maxval, vself(vmaskf_eq(bc, minvalold), minval, medval)); - - setUnlessOOG(r[i], g[i], b[i], nr, ng, nb); - } - // Remainder in non-SSE. - for (; i < end; ++i) { - Apply(r[i], g[i], b[i]); - } -#endif -} - -inline void AdobeToneCurve::RGBTone (float& maxval, float& medval, float& minval) const -{ - float minvalold = minval, medvalold = medval, maxvalold = maxval; - - maxval = lutToneCurve[maxvalold]; - minval = lutToneCurve[minvalold]; - medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); -} -#ifdef __SSE2__ -inline void AdobeToneCurve::RGBTone (vfloat& maxval, vfloat& medval, vfloat& minval) const -{ - vfloat minvalold = minval, medvalold = medval, maxvalold = maxval; - - maxval = lutToneCurve[maxvalold]; - minval = lutToneCurve[minvalold]; - medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); -} -#endif -// Modifying the Luminance channel only -inline void LuminanceToneCurve::Apply(float &ir, float &ig, float &ib) const -{ - assert (lutToneCurve); - - float r = CLIP(ir); - float g = CLIP(ig); - float b = CLIP(ib); - - float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; - const float newLuminance = lutToneCurve[currLuminance]; - currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; - const float coef = newLuminance / currLuminance; - r = LIM(r * coef, 0.f, 65535.f); - g = LIM(g * coef, 0.f, 65535.f); - b = LIM(b * coef, 0.f, 65535.f); - - setUnlessOOG(ir, ig, ib, r, g, b); -} - -inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const -{ - if (a != b) { - float b1; - float a2 = a1 - a; - - if (b < a) { - b1 = b + a2 * b / a ; - } else { - b1 = b + a2 * (65535.f - b) / (65535.f - a); - } - - return b1; - } - - return a1; -} - -#ifdef __SSE2__ -inline vfloat WeightedStdToneCurve::Triangle(vfloat a, vfloat a1, vfloat b) const -{ - vmask eqmask = vmaskf_eq(b, a); - vfloat a2 = a1 - a; - vmask cmask = vmaskf_lt(b, a); - vfloat b3 = vself(cmask, b, F2V(65535.f) - b); - vfloat a3 = vself(cmask, a, F2V(65535.f) - a); - return vself(eqmask, a1, b + a2 * b3 / a3); -} -#endif - -// Tone curve modifying the value channel only, preserving hue and saturation -// values in 0xffff space -inline void WeightedStdToneCurve::Apply (float& ir, float& ig, float& ib) const -{ - - assert (lutToneCurve); - - float r = CLIP(ir); - float g = CLIP(ig); - float b = CLIP(ib); - float r1 = lutToneCurve[r]; - float g1 = Triangle(r, r1, g); - float b1 = Triangle(r, r1, b); - - float g2 = lutToneCurve[g]; - float r2 = Triangle(g, g2, r); - float b2 = Triangle(g, g2, b); - - float b3 = lutToneCurve[b]; - float r3 = Triangle(b, b3, r); - float g3 = Triangle(b, b3, g); - - r = CLIP(r1 * 0.50f + r2 * 0.25f + r3 * 0.25f); - g = CLIP(g1 * 0.25f + g2 * 0.50f + g3 * 0.25f); - b = CLIP(b1 * 0.25f + b2 * 0.25f + b3 * 0.50f); - - setUnlessOOG(ir, ig, ib, r, g, b); -} - -inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const { - assert (lutToneCurve); - assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); - assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); - - // All pointers must have the same alignment for SSE usage. In the loop body below, - // we will only check `r`, assuming that the same result would hold for `g` and `b`. - assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); - assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); - - size_t i = start; - while (true) { - if (i >= end) { - // If we get to the end before getting to an aligned address, just return. - // (Or, for non-SSE mode, if we get to the end.) - return; -#ifdef __SSE2__ - } else if (reinterpret_cast(&r[i]) % 16 == 0) { - // Otherwise, we get to the first aligned address; go to the SSE part. - break; -#endif - } - Apply(r[i], g[i], b[i]); - i++; - } - -#ifdef __SSE2__ - const vfloat c65535v = F2V(65535.f); - const vfloat zd5v = F2V(0.5f); - const vfloat zd25v = F2V(0.25f); - - for (; i + 3 < end; i += 4) { - vfloat r_val = vclampf(LVF(r[i]), ZEROV, c65535v); - vfloat g_val = vclampf(LVF(g[i]), ZEROV, c65535v); - vfloat b_val = vclampf(LVF(b[i]), ZEROV, c65535v); - vfloat r1 = lutToneCurve[r_val]; - vfloat g1 = Triangle(r_val, r1, g_val); - vfloat b1 = Triangle(r_val, r1, b_val); - - vfloat g2 = lutToneCurve[g_val]; - vfloat r2 = Triangle(g_val, g2, r_val); - vfloat b2 = Triangle(g_val, g2, b_val); - - vfloat b3 = lutToneCurve[b_val]; - vfloat r3 = Triangle(b_val, b3, r_val); - vfloat g3 = Triangle(b_val, b3, g_val); - - vfloat r_old = LVF(r[i]); - vfloat g_old = LVF(g[i]); - vfloat b_old = LVF(b[i]); - vfloat r_new = vclampf(r1 * zd5v + r2 * zd25v + r3 * zd25v, ZEROV, c65535v); - vfloat g_new = vclampf(g1 * zd25v + g2 * zd5v + g3 * zd25v, ZEROV, c65535v); - vfloat b_new = vclampf(b1 * zd25v + b2 * zd25v + b3 * zd5v, ZEROV, c65535v); - setUnlessOOG(r_old, g_old, b_old, r_new, g_new, b_new); - STVF(r[i], r_old); - STVF(g[i], g_old); - STVF(b[i], b_old); - } - - // Remainder in non-SSE. - for (; i < end; ++i) { - Apply(r[i], g[i], b[i]); - } -#endif -} - -// Tone curve modifying the value channel only, preserving hue and saturation -// values in 0xffff space -inline void SatAndValueBlendingToneCurve::Apply (float& ir, float& ig, float& ib) const -{ - - assert (lutToneCurve); - - float r = CLIP(ir); - float g = CLIP(ig); - float b = CLIP(ib); - - const float lum = (r + g + b) / 3.f; - const float newLum = lutToneCurve[lum]; - - if (newLum == lum) { - return; - } - - float h, s, v; - Color::rgb2hsvtc(r, g, b, h, s, v); - - float dV; - if (newLum > lum) { - // Linearly targeting Value = 1 and Saturation = 0 - const float coef = (newLum - lum) / (65535.f - lum); - dV = (1.f - v) * coef; - s *= 1.f - coef; - } else { - // Linearly targeting Value = 0 - const float coef = (newLum - lum) / lum ; - dV = v * coef; - } - Color::hsv2rgbdcp(h, s, v + dV, r, g, b); - - setUnlessOOG(ir, ig, ib, r, g, b); -} - -} - -#undef CLIPI - -#endif From 88b5d538063fcc17fa92e4ec82a9aee588c490b8 Mon Sep 17 00:00:00 2001 From: Beep6581 Date: Sun, 10 Mar 2019 19:53:28 +0100 Subject: [PATCH 10/13] Forward-declare info from #5197 --- CONTRIBUTING.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85776d557..a101c39ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,3 +25,94 @@ The most useful feedback is based on the latest development code, and in the cas - *Enum values* should be named with `UPPER_UNDERSCORES`. - Be consistent, even when not sticking to the rules. - Code may be run through astyle version 3 or newer. If using astyle, it is important that the astyle changes go into their own commit, so that style changes are not mixed with actual code changes. Command: `astyle --options=rawtherapee.astylerc code.cc` +- Forward-declare instead of including, to avoid dependency chains where a single header change results in a full tree compilation. + Forward declarations are sufficient for: + - Parameters or members passed or stored as references or pointers. + - Return types. + - Template parameters if the instances are passed or stored as references or pointer. + + You can forward declare: + - Classes + - Structs + - Enums with an underlying type + - Templates + + You can't forward declare: + - Typedefs + - Inner classes/structs/enums + + When you write a header file, forward the types your interface needs instead of including the corresponding headers. STL types are hard to get right, so forward declaring them is usually not recommended. The same might be true for glibmm types. + + A basic opaque pointer implementation: + ```c++ + // Header + + #include + + class OtherClass; + + class MyClass final + { + public: + MyClass(); + ~MyClass(); + + void consume(const OtherClass& other); + OtherClass returnOther() const; + + private: + class Implementation; + + const std::unique_ptr implementation; + }; + + + // Implemenation + + #include "OtherClass.h" + #include "ALotOfOtherStuff.h" + + class MyClass::Implementation final + { + public: + Implementation() : + init_everything() + { + } + + ~Implementation() + { + cleanEverythingUp(); + } + + void consume(const OtherClass& other) + { + // Do everything that needs to be done + } + + OtherClass returnOther() const + { + return {}; // Or do more ;) + } + + private: + // Arbitrary members + }; + + MyClass::MyClass() : + implementation(new Implementation) + { + } + + MyClass::~MyClass() = default; + + void MyClass::consume(const OtherClass& other) + { + implementation->consume(other); + } + + OtherClass MyClass::returnOther() const + { + return implementation->returnOther(); + } + ``` From a5b021c2bb41d078087d9a5014ea586aabbc1884 Mon Sep 17 00:00:00 2001 From: Beep6581 Date: Sun, 10 Mar 2019 21:43:46 +0100 Subject: [PATCH 11/13] Removed forward-declare info Info was already added more concisely in cd566da --- CONTRIBUTING.md | 91 ------------------------------------------------- 1 file changed, 91 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a101c39ac..85776d557 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,94 +25,3 @@ The most useful feedback is based on the latest development code, and in the cas - *Enum values* should be named with `UPPER_UNDERSCORES`. - Be consistent, even when not sticking to the rules. - Code may be run through astyle version 3 or newer. If using astyle, it is important that the astyle changes go into their own commit, so that style changes are not mixed with actual code changes. Command: `astyle --options=rawtherapee.astylerc code.cc` -- Forward-declare instead of including, to avoid dependency chains where a single header change results in a full tree compilation. - Forward declarations are sufficient for: - - Parameters or members passed or stored as references or pointers. - - Return types. - - Template parameters if the instances are passed or stored as references or pointer. - - You can forward declare: - - Classes - - Structs - - Enums with an underlying type - - Templates - - You can't forward declare: - - Typedefs - - Inner classes/structs/enums - - When you write a header file, forward the types your interface needs instead of including the corresponding headers. STL types are hard to get right, so forward declaring them is usually not recommended. The same might be true for glibmm types. - - A basic opaque pointer implementation: - ```c++ - // Header - - #include - - class OtherClass; - - class MyClass final - { - public: - MyClass(); - ~MyClass(); - - void consume(const OtherClass& other); - OtherClass returnOther() const; - - private: - class Implementation; - - const std::unique_ptr implementation; - }; - - - // Implemenation - - #include "OtherClass.h" - #include "ALotOfOtherStuff.h" - - class MyClass::Implementation final - { - public: - Implementation() : - init_everything() - { - } - - ~Implementation() - { - cleanEverythingUp(); - } - - void consume(const OtherClass& other) - { - // Do everything that needs to be done - } - - OtherClass returnOther() const - { - return {}; // Or do more ;) - } - - private: - // Arbitrary members - }; - - MyClass::MyClass() : - implementation(new Implementation) - { - } - - MyClass::~MyClass() = default; - - void MyClass::consume(const OtherClass& other) - { - implementation->consume(other); - } - - OtherClass MyClass::returnOther() const - { - return implementation->returnOther(); - } - ``` From e35122040e334705574fac4668cba7c67f99faad Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 10 Mar 2019 21:47:50 +0100 Subject: [PATCH 12/13] Disabled timing code, #5208 --- rtengine/improcfun.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index e751ef9b8..cec81d520 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -40,7 +40,6 @@ #include "improccoordinator.h" #include "clutstore.h" #include "ciecam02.h" -#define BENCHMARK #include "StopWatch.h" #include "../rtgui/ppversion.h" #include "../rtgui/guiutils.h" @@ -1984,7 +1983,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw void ImProcFunctions::moyeqt (Imagefloat* working, float &moyS, float &eqty) { -// BENCHFUN + BENCHFUN int tHh = working->getHeight(); int tWw = working->getWidth(); From 88a8c9829d912cb454c8060c72be8fd7f0e71a8e Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 10 Mar 2019 22:15:09 +0100 Subject: [PATCH 13/13] Set default tiles per thread value to 2, #5203 --- rtgui/options.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rtgui/options.cc b/rtgui/options.cc index 2819497de..2755c8888 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -449,11 +449,11 @@ void Options::setDefaults() inspectorDelay = 0; serializeTiffRead = true; measure = false; - chunkSizeAMAZE = 1; - chunkSizeCA = 1; - chunkSizeRCD = 1; - chunkSizeRGB = 1; - chunkSizeXT = 1; + chunkSizeAMAZE = 2; + chunkSizeCA = 2; + chunkSizeRCD = 2; + chunkSizeRGB = 2; + chunkSizeXT = 2; FileBrowserToolbarSingleRow = false; hideTPVScrollbar = false; whiteBalanceSpotSize = 8;