diff --git a/AUTHORS.txt b/AUTHORS.txt index 227390faa..aae24ff63 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -7,6 +7,7 @@ Development contributors, in last name alphabetical order: Roel Baars Martin Burri + Pierre Cabrera Javier Celaya Jacques Desmis Pavlov Dmitry @@ -20,6 +21,7 @@ Development contributors, in last name alphabetical order: Steve Herrell Philippe Hupé Wolfgang Kuehnel + Lawrence Lee Guokai Ma Emil Martinec Wyatt Olson @@ -36,8 +38,9 @@ Development contributors, in last name alphabetical order: Ingo Weyrich Makoto Yoshida -Other contributors (profiles, ideas, mockups, testing, forum activity, translations, etc.), in last name alphabetical order: +Other contributors (profiles, ideas, mockups, testing, forum activity, translations, tutorials etc.), in last name alphabetical order: + Andy Astbury Marcin Bajor Javier Bartol Thorsten Bartolomäus diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d66109e0..b7cf35099 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,13 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION "Building RawTherapee requires using GCC version 4.9 or higher!") endif() +# Warning for GCC 10, which causes problems #5749: +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "10.0") + message(STATUS "WARNING: gcc ${CMAKE_CXX_COMPILER_VERSION} is known to miscompile RawTherapee when using -ftree-loop-vectorize, forcing the option to be off") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-tree-loop-vectorize") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-tree-loop-vectorize") +endif() + # We might want to build using the old C++ ABI, even when using a new GCC # version: if(USE_OLD_CXX_ABI) @@ -68,6 +75,11 @@ set(CACHE_NAME_SUFFIX "" CACHE STRING "RawTherapee's cache folder suffix") +# For macOS only, OSX_DEV_BUILD option allows using relative paths instead of absolute +# paths. Consequently, for development builds, application can be launching without +# being bundled. However, file access can be restricted for some folder. +option(OSX_DEV_BUILD "Generate macOS development builds" OFF) + # By default we don't use a specific processor target, so PROC_TARGET_NUMBER is # set to 0. Specify other values to optimize for specific processor architecture # as listed in ProcessorTargets.cmake: diff --git a/rtdata/images/svg/perspective-horizontal-vertical.svg b/rtdata/images/svg/perspective-horizontal-vertical.svg new file mode 100644 index 000000000..0c5046879 --- /dev/null +++ b/rtdata/images/svg/perspective-horizontal-vertical.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + image/svg+xml + + + + + Maciej Dworak + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + diff --git a/rtdata/languages/Czech b/rtdata/languages/Czech index 19182a1e5..b6006cd36 100644 --- a/rtdata/languages/Czech +++ b/rtdata/languages/Czech @@ -40,10 +40,12 @@ #39 2017-07-21 updated by mkyral #40 2017-12-13 updated by mkyral #41 2018-03-03 updated by mkyral -#42 2018-04-28 updated by mkyral -#43 2018-12-13 updated by mkyral -#44 2019-04-17 updated by mkyral - +#42 2018-10-24 updated by mkyral +#43 2018-12-04 updated by mkyral +#44 2018-12-13 updated by mkyral +#45 2020-04-20 updated by mkyral +#46 2020-04-21 updated by mkyral +#47 2020-06-02 updated by mkyral ABOUT_TAB_BUILD;Verze ABOUT_TAB_CREDITS;Zásluhy ABOUT_TAB_LICENSE;Licence @@ -153,7 +155,7 @@ FILEBROWSER_APPLYPROFILE;Použít FILEBROWSER_APPLYPROFILE_PARTIAL;Aplikovat - částečně FILEBROWSER_AUTODARKFRAME;Automatický tmavý snímek FILEBROWSER_AUTOFLATFIELD;Auto Flat Field -FILEBROWSER_BROWSEPATHBUTTONHINT;Klikněte pro otevření zadané cesty, obnovte složku a aplikujte klíčové slovo "find". +FILEBROWSER_BROWSEPATHBUTTONHINT;Klikněte pro otevření zadané cesty, obnovte složku a aplikujte klíčová slova z pole "Najít:". FILEBROWSER_BROWSEPATHHINT;Vložte cestu pro procházení.\n\nKlávesové zkratky:\nCtrl-o pro přepnutí do adresního řádku.\nEnter/ Ctrl-Enter pro procházení ;\nEsc pro zrušení změn.\nShift-Esc pro zrušení přepnutí.\n\nZkratky pro cesty:\n~\t- domácí složka uživatele.\n!\t- složka s obrázky uživatele. FILEBROWSER_CACHE;Mezipaměť FILEBROWSER_CACHECLEARFROMFULL;Smazat vše včetně profilů zpracování v mezipaměti @@ -163,8 +165,12 @@ FILEBROWSER_COLORLABEL_TOOLTIP;Barevný štítek.\n\nPoužijte výběr ze seznam FILEBROWSER_COPYPROFILE;Kopírovat FILEBROWSER_CURRENT_NAME;Současné jméno: FILEBROWSER_DARKFRAME;Tmavý snímek -FILEBROWSER_DELETEDIALOG_HEADER;Potvrzení smazání souboru +FILEBROWSER_DELETEDIALOG_ALL;Jste si jisti, že chcete trvale vymazat %1 vybraných souborů? +FILEBROWSER_DELETEDIALOG_HEADER;Potvrzení smazání souboru: +FILEBROWSER_DELETEDIALOG_SELECTED;Jste si jisti, že chcete trvale vymazat %1 vybraných souborů? +FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Jste si jisti, že chcete trvale vymazat %1 vybraných souborů včetně výstupů dávkového zpracování? FILEBROWSER_EMPTYTRASH;Vysypat koš +FILEBROWSER_EMPTYTRASHHINT;Trvale smaže všechny soubory z koše. FILEBROWSER_EXTPROGMENU;Otevřít pomocí FILEBROWSER_FLATFIELD;Flat Field FILEBROWSER_MOVETODARKFDIR;Přesunout do složky tmavých snímků @@ -198,6 +204,8 @@ FILEBROWSER_POPUPRANK2;Hodnocení 2 ** FILEBROWSER_POPUPRANK3;Hodnocení 3 *** FILEBROWSER_POPUPRANK4;Hodnocení 4 **** FILEBROWSER_POPUPRANK5;Hodnocení 5 ***** +FILEBROWSER_POPUPREMOVE;Trvale smazat +FILEBROWSER_POPUPREMOVEINCLPROC;Trvale smazat, včetně dávkově zpracovaných verzí FILEBROWSER_POPUPRENAME;Přejmenovat FILEBROWSER_POPUPSELECTALL;Vybrat vše FILEBROWSER_POPUPTRASH;Přesunout do koše @@ -224,6 +232,7 @@ FILEBROWSER_SHOWDIRHINT;Smazat všechny filtry.\nZkratka: d FILEBROWSER_SHOWEDITEDHINT;Ukázat upravené obrázky.\nZkratka: 7 FILEBROWSER_SHOWEDITEDNOTHINT;Ukázat neupravené obrázky.\nZkratka: 6 FILEBROWSER_SHOWEXIFINFO;Zobrazit Exif informace.\n\nZkratky:\ni - režim více karet editoru,\nAlt-i - režim jedné karty editoru. +FILEBROWSER_SHOWNOTTRASHHINT;Zobrazit pouze snímky které nejsou v koši. FILEBROWSER_SHOWORIGINALHINT;Zobrazí pouze originální obrázky.\n\nPokud existuje několik obrázků se stejným názvem, ale rozdílnými příponami, bude jako originál vybrán ten, jehož přípona je nejvýše v seznamu přípon veVolby > Prohlížeč souborů > Analyzované přípony. FILEBROWSER_SHOWRANK1HINT;Ukázat obrázky hodnocené jednou hvězdičkou.\nZkratka: 1 FILEBROWSER_SHOWRANK2HINT;Ukázat obrázky hodnocené dvěma hvězdičkami.\nZkratka: 2 @@ -260,6 +269,7 @@ GENERAL_DISABLED;Vypnuto GENERAL_ENABLE;Zapnout GENERAL_ENABLED;Zapnuto GENERAL_FILE;Soubor +GENERAL_HELP;Nápověda GENERAL_LANDSCAPE;Na šířku GENERAL_NA;n/a GENERAL_NO;Ne @@ -402,11 +412,11 @@ HISTORY_MSG_113;L*a*b* - Ochrana červ. a pleť. tónů HISTORY_MSG_114;Průchody DCB HISTORY_MSG_115;Potlačení chybných barev HISTORY_MSG_116;Vylepšení DCB -HISTORY_MSG_117;Raw korekce CA - červená -HISTORY_MSG_118;Raw korekce CA - modrá +HISTORY_MSG_117;Raw korekce ChA - červená +HISTORY_MSG_118;Raw korekce ChA - modrá HISTORY_MSG_119;Filtrovat linkové rušení HISTORY_MSG_120;Vyrovnání zelené -HISTORY_MSG_121;Raw korekce CA - automatická +HISTORY_MSG_121;Raw korekce ChA - automatická HISTORY_MSG_122;Tmavé snímky - Automatický výběr HISTORY_MSG_123;Tmavé snímky - Soubor HISTORY_MSG_124;Korekce bílého bodu @@ -601,10 +611,10 @@ HISTORY_MSG_314;Vlnka - Gamut - Omezení artefaktů HISTORY_MSG_315;Vlnka - Zůstatek - Kontrast HISTORY_MSG_316;Vlnka - Gamut - Ochrana a zaměření pleťových tónů HISTORY_MSG_317;Vlnka - Gamut - Odstín pleti -HISTORY_MSG_318;Vlnka - Kontrast - Úrovně světel -HISTORY_MSG_319;Vlnka - Kontrast - - rozsah světel -HISTORY_MSG_320;Vlnka - Kontrast - Rozsah stínů -HISTORY_MSG_321;Vlnka - Kontrast - Úrovně stínů +HISTORY_MSG_318;Vlnka - Kontrast - Jemnější úrovně +HISTORY_MSG_319;Vlnka - Kontrast - Jemnější rozsah +HISTORY_MSG_320;Vlnka - Kontrast - Hrubší rozsah +HISTORY_MSG_321;Vlnka - Kontrast - Hrubší úrovně HISTORY_MSG_322;Vlnka - Gamut - Zabránit posunu barev HISTORY_MSG_323;Vlnka - DH - Místní kontrast HISTORY_MSG_324;Vlnka - Barevnost - Pastelové @@ -761,6 +771,12 @@ HISTORY_MSG_490;DRC - Míra HISTORY_MSG_491;Vyvážení bílé HISTORY_MSG_492;RGB křivky HISTORY_MSG_493;L*a*b* úpravy +HISTORY_MSG_494;Doostření vstupu +HISTORY_MSG_BLSHAPE;Rozmazat dle úrovně +HISTORY_MSG_BLURCWAV;Rozmazat barevnost +HISTORY_MSG_BLURWAV;Rozmazat jas +HISTORY_MSG_BLUWAV;Útlum +HISTORY_MSG_CAT02PRESET;Automatické přednastavení Cat02 HISTORY_MSG_CLAMPOOG;Oříznout barvy mimo gamut HISTORY_MSG_COLORTONING_LABGRID_VALUE;Barevné tónování - Korekce barev HISTORY_MSG_COLORTONING_LABREGION_AB;Barevné tónování - Korekce barev @@ -769,7 +785,7 @@ HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;BT -oblast C masky HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;Barevné tónování - H maska HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;BT - Světlost HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;BT - L maska -HISTORY_MSG_COLORTONING_LABREGION_LIST;BT - +HISTORY_MSG_COLORTONING_LABREGION_LIST;BT - HISTORY_MSG_COLORTONING_LABREGION_MASKBLUR;BT - oblast masky rozostření HISTORY_MSG_COLORTONING_LABREGION_OFFSET;BT - oblast posunu HISTORY_MSG_COLORTONING_LABREGION_POWER;BT - oblast síly @@ -778,10 +794,15 @@ HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;BT - oblast zobrazené masky HISTORY_MSG_COLORTONING_LABREGION_SLOPE;BT - oblast sklonu HISTORY_MSG_DEHAZE_DEPTH;Závoj - Hloubka HISTORY_MSG_DEHAZE_ENABLED;Odstranění závoje +HISTORY_MSG_DEHAZE_LUMINANCE;Závoj - Pouze jas HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Závoj - Ukázat hloubkovou mapu HISTORY_MSG_DEHAZE_STRENGTH;Závoj - Síla HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dvojité demozajkování - automatický práh HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dvojité demozajkování - Práh kontrastu +HISTORY_MSG_EDGEFFECT;Útlum hrany +HISTORY_MSG_FILMNEGATIVE_ENABLED;Negativní film +HISTORY_MSG_FILMNEGATIVE_FILMBASE;Barva podkladu filmu +HISTORY_MSG_FILMNEGATIVE_VALUES;Film negativní hodnoty HISTORY_MSG_HISTMATCHING;Automaticky nalezená tónová křivka HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Výstup - Základní barvy HISTORY_MSG_ICM_OUTPUT_TEMP;Výstup - ICC-v4 světelný zdroj D @@ -789,6 +810,7 @@ HISTORY_MSG_ICM_OUTPUT_TYPE;Výstup - Typ HISTORY_MSG_ICM_WORKING_GAMMA;Pracovní - Gama HISTORY_MSG_ICM_WORKING_SLOPE;Pracovní - sklon HISTORY_MSG_ICM_WORKING_TRC_METHOD;Pracovní - Metoda TRC +HISTORY_MSG_ILLUM;Osvětlení HISTORY_MSG_LOCALCONTRAST_AMOUNT;Místní kontrast - Míra HISTORY_MSG_LOCALCONTRAST_DARKNESS;Místní kontrast - Tmavé HISTORY_MSG_LOCALCONTRAST_ENABLED;Místní kontrast @@ -796,20 +818,56 @@ HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Místní kontrast - Světlé HISTORY_MSG_LOCALCONTRAST_RADIUS;Místní kontrast - Poloměr HISTORY_MSG_METADATA_MODE;Režim kopírování metadat HISTORY_MSG_MICROCONTRAST_CONTRAST;Mikrokontrast - Práh kontrastu +HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;DV - Automatický práh +HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;DV - Automatický poloměr +HISTORY_MSG_PDSHARPEN_CHECKITER;DV- Automatický počet průchodů +HISTORY_MSG_PDSHARPEN_CONTRAST;DV - Práh kontrastu +HISTORY_MSG_PDSHARPEN_ITERATIONS;DV - Průchody +HISTORY_MSG_PDSHARPEN_RADIUS;DV - Poloměr +HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;DV - Zvýšení poloměru rohu HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Metoda demozajkování pohybu HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Směr filtru linkového rušení HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;Filtr PDAF linek +HISTORY_MSG_PREPROCWB_MODE;Režim předzpracování VB +HISTORY_MSG_PROTAB;Ochrana HISTORY_MSG_PRSHARPEN_CONTRAST;Doostření - Práh kontrastu -HISTORY_MSG_RAWCACORR_AUTOIT;Raw korekce CA - Iterace -HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw korekce CA - Zabránit posunu barev +HISTORY_MSG_RANGEAB;Rozsah ab +HISTORY_MSG_RAWCACORR_AUTOIT;Raw korekce ChA - Iterace +HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw korekce ChA - Zabránit posunu barev HISTORY_MSG_RAW_BORDER;Okraj Raw HISTORY_MSG_RESIZE_ALLOWUPSCALING;Změna rozměrů - Povolit zvětšení HISTORY_MSG_SHARPENING_BLUR;Doostření - Poloměr rozmazání HISTORY_MSG_SHARPENING_CONTRAST;Doostření - Práh kontrastu HISTORY_MSG_SH_COLORSPACE;S/S - Barevný prostor +HISTORY_MSG_SIGMACOL;Útlum barevnosti +HISTORY_MSG_SIGMADIR;Útlum směru +HISTORY_MSG_SIGMAFIN;Finální útlum kontrastu +HISTORY_MSG_SIGMATON;Útlum tónování HISTORY_MSG_SOFTLIGHT_ENABLED;Měkké světlo HISTORY_MSG_SOFTLIGHT_STRENGTH;Měkká světla - Síla +HISTORY_MSG_TEMPOUT;CAM02 - Automatická teplota +HISTORY_MSG_THRESWAV;Práh vyvážení HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Kotva +HISTORY_MSG_TRANS_Method;Geometrie - Metoda +HISTORY_MSG_WAVBALCHROM;Vyvážení barevnosti +HISTORY_MSG_WAVBALLUM;Vyvážení jasu +HISTORY_MSG_WAVBL;Úrovně rozmazání +HISTORY_MSG_WAVCHROMCO;Hrubá barevnost +HISTORY_MSG_WAVCHROMFI;Jemné barevnost +HISTORY_MSG_WAVCLARI;Čirost +HISTORY_MSG_WAVEDGS;Zachování hran +HISTORY_MSG_WAVLOWTHR;Práh nízkého kontrastu +HISTORY_MSG_WAVMERGEC;Sloučení barevnosti +HISTORY_MSG_WAVMERGEL;Sloučení jasu +HISTORY_MSG_WAVOFFSET;Posun +HISTORY_MSG_WAVOLDSH;Starý algoritmus +HISTORY_MSG_WAVRADIUS;Poloměr Stíny-Světla +HISTORY_MSG_WAVSCALE;Měřítko +HISTORY_MSG_WAVSHOWMASK;Ukázat masku vlnky +HISTORY_MSG_WAVSIGMA;Útlum +HISTORY_MSG_WAVSOFTRAD;Čirost jemný poloměr +HISTORY_MSG_WAVSOFTRADEND;Konečný jemný poloměr +HISTORY_MSG_WAVUSHAMET;Metoda čirosti HISTORY_NEWSNAPSHOT;Přidat HISTORY_NEWSNAPSHOT_TOOLTIP;Zkratka: Alt-s HISTORY_SNAPSHOT;Snímek @@ -939,15 +997,15 @@ MAIN_TAB_COLOR;Barvy MAIN_TAB_COLOR_TOOLTIP;Zkratka: Alt-c MAIN_TAB_DETAIL;Detaily MAIN_TAB_DETAIL_TOOLTIP;Zkratka: Alt-d -MAIN_TAB_DEVELOP; Dávková editace +MAIN_TAB_DEVELOP; Dávková editace MAIN_TAB_EXIF;Exif -MAIN_TAB_EXPORT; Rychlý export +MAIN_TAB_EXPORT; Rychlý export MAIN_TAB_EXPOSURE;Expozice MAIN_TAB_EXPOSURE_TOOLTIP;Zkratka: Alt-e MAIN_TAB_FAVORITES;Oblíbené MAIN_TAB_FAVORITES_TOOLTIP;Zkratka: Alt-u -MAIN_TAB_FILTER; Filtr -MAIN_TAB_INSPECT; Prohlížení +MAIN_TAB_FILTER; Filtr +MAIN_TAB_INSPECT; Prohlížení MAIN_TAB_IPTC;IPTC MAIN_TAB_METADATA;Metadata MAIN_TAB_METADATA_TOOLTIP;Zkratka: Alt-m @@ -961,8 +1019,8 @@ MAIN_TOOLTIP_BACKCOLOR2;Barva pozadí náhledu: bílá\nZkratka: 9 MAIN_TOOLTIP_BACKCOLOR3;Barva pozadí náhledu: středně šedá\nZkratka: 9 MAIN_TOOLTIP_BEFOREAFTERLOCK;Zamknout / Odemknout pohled Před\n\nZamknout: ponechá pohled Před nezměněn.\nUžitečné pro posouzení výsledného efektu po použití více nástrojů.\nNavíc může být porovnání provedeno proti kterémukoli stavu v historii.\n\nOdemknout: pohled Před bude následovat pohled Poté, vždy jen o jeden krok zpět, představí vliv právě použitého nástroje. MAIN_TOOLTIP_HIDEHP;Zobrazit či schovat levý panel (obsahující historii).\nZkratka: l -MAIN_TOOLTIP_INDCLIPPEDH;Zvýraznit oříznutá světla.\nZkratka: > -MAIN_TOOLTIP_INDCLIPPEDS;Zvýraznit oříznuté stíny.\nZkratka: < +MAIN_TOOLTIP_INDCLIPPEDH;Zvýraznění oříznutých světel.\nZkratka: > +MAIN_TOOLTIP_INDCLIPPEDS;Zvýraznění oříznutých stínů.\nZkratka: < MAIN_TOOLTIP_PREVIEWB;Náhled modrého kanálu.\nZkratka: b MAIN_TOOLTIP_PREVIEWFOCUSMASK;Náhled masky zaostření.\nZkratka: Shift-f\n\nVíce přesné u snímků s nízkou hloubkou ostrosti, nízkým šumem a na vyšších úrovních zvětšení.\n\nPoužijte přiblížení v rozsahu 10 až 30% pro zlepšení přesnosti detekce u zašuměných snímků. MAIN_TOOLTIP_PREVIEWG;Náhled zeleného kanálu.\nZkratka: g @@ -1017,6 +1075,7 @@ PARTIALPASTE_EQUALIZER;Úrovně vlnky PARTIALPASTE_EVERYTHING;Vše PARTIALPASTE_EXIFCHANGES;Exif PARTIALPASTE_EXPOSURE;Expozice +PARTIALPASTE_FILMNEGATIVE;Negativní film PARTIALPASTE_FILMSIMULATION;Simulace filmu PARTIALPASTE_FLATFIELDAUTOSELECT;Automatický výběr Flat Field PARTIALPASTE_FLATFIELDBLURRADIUS;Poloměr rozostření Flat Field @@ -1041,6 +1100,7 @@ PARTIALPASTE_PREPROCESS_GREENEQUIL;Vyrovnání zelené PARTIALPASTE_PREPROCESS_HOTPIXFILT;Filtr vypálených pixelů PARTIALPASTE_PREPROCESS_LINEDENOISE;Filtrovat linkové rušení PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;Filtr PDAF linek +PARTIALPASTE_PREPROCWB;Předzpracování Vyvážení bílé PARTIALPASTE_PRSHARPENING;Doostření po změně velikosti PARTIALPASTE_RAWCACORR_AUTO;Automatická korekce CA PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA zabránit posunu barev @@ -1075,6 +1135,7 @@ PREFERENCES_APPEARANCE_COLORPICKERFONT;Písmo Průzkumníka barev PREFERENCES_APPEARANCE_CROPMASKCOLOR;Barva masky ořezu PREFERENCES_APPEARANCE_MAINFONT;Hlavní písmo PREFERENCES_APPEARANCE_NAVGUIDECOLOR;Barva vodítek navigátoru +PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI režim PREFERENCES_APPEARANCE_THEME;Motiv PREFERENCES_APPLNEXTSTARTUP;vyžaduje restart aplikace PREFERENCES_AUTOMONPROFILE;Použít barevný profil hlavního monitoru z operačního systému @@ -1094,11 +1155,11 @@ PREFERENCES_CACHEMAXENTRIES;Maximální počet záznamů v mezipaměti PREFERENCES_CACHEOPTS;Vlastnosti mezipaměti PREFERENCES_CACHETHUMBHEIGHT;Maximální výška náhledu PREFERENCES_CHUNKSIZES;Dlaždic na vlákno -PREFERENCES_CHUNKSIZE_RAW_AMAZE;AMaZE demozajkování -PREFERENCES_CHUNKSIZE_RAW_CA;Raw korekce CA +PREFERENCES_CHUNKSIZE_RAW_AMAZE;Demozajkování AMaZE +PREFERENCES_CHUNKSIZE_RAW_CA;Raw korekce ChA PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demozajkování -PREFERENCES_CHUNKSIZE_RAW_XT;Xtrans demozajkování -PREFERENCES_CHUNKSIZE_RGB;RGB zpracování +PREFERENCES_CHUNKSIZE_RAW_XT;Demozajkování Xtrans +PREFERENCES_CHUNKSIZE_RGB;Zpracování barev PREFERENCES_CLIPPINGIND;Indikace oříznutí PREFERENCES_CLUTSCACHE;Mezipaměť HaldCLUT PREFERENCES_CLUTSCACHE_LABEL;Maximální počet přednačtených CLUTů @@ -1186,8 +1247,8 @@ PREFERENCES_PARSEDEXTADDHINT;Vymazat označenou příponu ze seznamu. PREFERENCES_PARSEDEXTDELHINT;Vymazat označenou příponu ze seznamu. PREFERENCES_PARSEDEXTDOWNHINT;Vybranou příponu posunout na seznamu níže. PREFERENCES_PARSEDEXTUPHINT;Vybranou příponu posunout na seznamu výše. -PREFERENCES_PERFORMANCE_MEASURE;Měřit -PREFERENCES_PERFORMANCE_MEASURE_HINT;Vypisovat časy zpracování v konzoli +PREFERENCES_PERFORMANCE_MEASURE;Měření +PREFERENCES_PERFORMANCE_MEASURE_HINT;Vypisuje doby zpracování do konzole PREFERENCES_PERFORMANCE_THREADS;Vlákna PREFERENCES_PERFORMANCE_THREADS_LABEL;Maximální počet vláken pro Redukci šumu a Úrovně vlnky (0 = Automaticky) PREFERENCES_PREVDEMO;Metoda demozajkování náhledu @@ -1262,11 +1323,11 @@ PROFILEPANEL_TOOLTIPCOPY;Kopírovat současný profil do schránky.\nCtrl-kli PROFILEPANEL_TOOLTIPLOAD;Nahrát profil ze souboru.\nCtrl-klik umožní vybrat parametry pro nahrání. PROFILEPANEL_TOOLTIPPASTE;Vložit profil ze schránky.\nCtrl-klik umožní vybrat parametry pro vložení. PROFILEPANEL_TOOLTIPSAVE;Uložit současný profil.\nCtrl-klik umožní vybrat parametry pro uložení. -PROGRESSBAR_DECODING;Dekodování... +PROGRESSBAR_DECODING;Dekódování… PROGRESSBAR_GREENEQUIL;Vyrovnání zelené... -PROGRESSBAR_HLREC;Rekonstrukce světel... -PROGRESSBAR_HOTDEADPIXELFILTER;Filtr vypálených/mrtvých pixelů... -PROGRESSBAR_LINEDENOISE;Filtr linkového rušení... +PROGRESSBAR_HLREC;Rekonstrukce světel… +PROGRESSBAR_HOTDEADPIXELFILTER;Filtrování vypálených/mrtvých pixelů… +PROGRESSBAR_LINEDENOISE;Filtr linkového rušení… PROGRESSBAR_LOADING;Načítání obrázku... PROGRESSBAR_LOADINGTHUMBS;Načítání náhledů... PROGRESSBAR_LOADJPEG;Načítání JPEG... @@ -1275,7 +1336,7 @@ PROGRESSBAR_LOADTIFF;Načítání TIFF... PROGRESSBAR_NOIMAGES;Složka neobsahuje obrázky PROGRESSBAR_PROCESSING;Zpracovávaní obrázku... PROGRESSBAR_PROCESSING_PROFILESAVED;Profil zpracování uložen -PROGRESSBAR_RAWCACORR;RAW korekce chromatické aberace... +PROGRESSBAR_RAWCACORR;Korekce ChA… PROGRESSBAR_READY;Připraven PROGRESSBAR_SAVEJPEG;Ukládání JPEG souboru... PROGRESSBAR_SAVEPNG;Ukládání PNG souboru... @@ -1293,8 +1354,8 @@ QUEUE_DESTFILENAME;Cesta a název souboru QUEUE_FORMAT_TITLE;Formát souboru QUEUE_LOCATION_FOLDER;Ulož do souboru QUEUE_LOCATION_TEMPLATE;Použít šablonu -QUEUE_LOCATION_TEMPLATE_TOOLTIP;Specifikujte kam se mají uložit výstupy. Lze použít umístění zdrojových souborů, pořadí, stav koše nebo pozice ve frontě.\n\nNapříklad pokud má zpracovávaná fotografie následující cestu:\n/home/tomas/fotky/2010-10-31/dsc0042.nef,\nmají jednotlivé formátovací řetězce tento význam:\n%d4 = home\n%d3 = tomas\n%d2 = fotky\n%d1 = 2010-10-31\n%f = dsc0042\n%p1 = /home/tomas/fotky/2010-10-31/\n%p2 = /home/tomas/fotky/\n%p3 = /home/tomas/\n%p4 = /home/\n\n%r bude nahrazeno hodnocením fotografie.Pokud není fotografie ohodnocena, bude %r nahrazeno '0'.Pokud je fotografie v koši, bude %r nahrazeno 'x'.\n\nPokud si přejete uložit výstupní obrázek vedle originálu, napište:\n%p1/%f\n\nPokud si jej ale přejete uložit do adresáře "converted" ve stejném adresáři jako otevřený obrázek, napište:\n%p1/converted/%f\n\nPro uložení výstupního obrázku do adresáře"/home/tom/photos/converted/2010-10-31", napište:\n%p2/converted/%d1/%f -QUEUE_LOCATION_TITLE;Výstupní umístění +QUEUE_LOCATION_TEMPLATE_TOOLTIP;Lze použít následující formátovací řetězce:\n%f, %d1, %d2, ..., %p1, %p2, ..., %r\n\nTyto formátovací řetězce reprezentují různé části cesty k uložené fotografii.\n\nNapříklad pokud má zpracovávaná fotografie následující cestu:\n/home/tomas/fotky/2010-10-31/fotka1.raw,\nmají jednotlivé formátovací řetězce tento význam:\n%d4 = home\n%d3 = tomas\n%d2 = fotky\n%d1 = 2010-10-31\n%f = fotka1\n%p1 = /home/tomas/fotky/2010-10-31/\n%p2 = /home/tomas/fotky/\n%p3 = /home/tomas/\n%p4 = /home/\n\n%r bude nahrazeno hodnocením fotografie.Pokud není fotografie ohodnocena, bude %r nahrazeno '0'.Pokud je fotografie v koši, bude %r nahrazeno 'x'.\n\nPokud si přejete uložit výstupní obrázek vedle originálu, napište:\n%p1/%f\n\nPokud si jej ale přejete uložit do adresáře "converted" ve stejném adresáři jako otevřený obrázek, napište:\n%p1/converted/%f\n\nPro uložení výstupního obrázku do adresáře"/home/tom/photos/converted/2010-10-31", napište:\n%p2/converted/%d1/%f +QUEUE_LOCATION_TITLE;Umístění výstupu QUEUE_STARTSTOP_TOOLTIP;Spustit nebo zastavit zpracování obrázků ve frontě.\n\nZkratka: Ctrl+s SAMPLEFORMAT_0;Neznámý datový formát SAMPLEFORMAT_1;8-bitový neznaménkový @@ -1331,7 +1392,7 @@ THRESHOLDSELECTOR_HINT;Držte klávesu Shift pro přesun individuálních THRESHOLDSELECTOR_T;Nahoře THRESHOLDSELECTOR_TL;Nahoře vlevo THRESHOLDSELECTOR_TR;Nahoře vpravo -TOOLBAR_TOOLTIP_COLORPICKER;Uzamykatelný Průzkumník barev\n\nPokud je nástroj aktivní:\n- Přidání sondy: levý-klik.\n- Posunutí sondy: levý-klik a posunutí.\n- Smazání sondy: pravý-klik.\n- Smazání všech sond: Ctrl+Shift+pravý-klik.\n- Návrat k nástroji posunu: pravý-klik mimo průzkumníky. +TOOLBAR_TOOLTIP_COLORPICKER;Uzamykatelný Průzkumník barev\n\nPokud je nástroj aktivní:\n- Přidání sondy: levý-klik.\n- Posunutí sondy: levý-klik a posunutí.\n- Smazání sondy: pravý-klik.\n- Smazání všech sond: Ctrl+Shift+pravý-klik.\n- Návrat k nástroji posunu: pravý-klik mimo jakoukoli sondu. TOOLBAR_TOOLTIP_CROP;Oříznutí výběru.\nZkratka: c\nVýřez posunete pomocí Shift + tažení myši TOOLBAR_TOOLTIP_HAND;Posun.\nZkratka: h TOOLBAR_TOOLTIP_STRAIGHTEN;Vyznačení roviny / rotace.\nZkratka: s\n\nZobrazení míry rotace pomocí vodící linky na náhledu snímky. Úhel rotace je zobrazen vedle vodící linky. Střed rotace je geometrický střed snímku. @@ -1417,7 +1478,7 @@ TP_COLORAPP_ALGO_JS;Světlost + Nasycení (JS) TP_COLORAPP_ALGO_QM;Jas a pestrobarevnost (QM) TP_COLORAPP_ALGO_TOOLTIP;Umožňuje vybrat mezi podmnožinou nebo všemi parametry. TP_COLORAPP_BADPIXSL;Filtr vypálených/mrtvých pixelů -TP_COLORAPP_BADPIXSL_TOOLTIP;Potlačení vypálených/mrtvých (jasně zabarvených) pixelů.\n0 = Bez efektu\n1 = Medián\n2 = Gaussův.\nPopřípadě obrázek upravte tak, aby jste se vyhnuli velmi tmavým stínům.\n\nTyto artefakty vznikají díky omezením CIECAM02. +TP_COLORAPP_BADPIXSL_TOOLTIP;Potlačení vypálených/mrtvých (jasně zabarvených) pixelů.\n0 = Bez efektu\n1 = Medián\n2 = Gaussův.\nPopřípadě obrázek upravte tak, aby jste se vyhnuli velmi tmavým stínům.\n\nTyto artefakty vznikají díky omezením CIECAM02. TP_COLORAPP_BRIGHT;Jas (O) TP_COLORAPP_BRIGHT_TOOLTIP;Jas v CIECAM02 bere v potaz svítivost bílé a rozdíly jasů mezi L*a*b* a RGB. TP_COLORAPP_CAT02ADAPTATION_TOOLTIP;U ručního nastavení jsou doporučeny hodnoty nad 65. @@ -1428,7 +1489,7 @@ TP_COLORAPP_CHROMA_S;Nasycení (S) TP_COLORAPP_CHROMA_S_TOOLTIP;Nasycení se v CIECAM02 liší od nasycení L*a*b* a RGB. TP_COLORAPP_CHROMA_TOOLTIP;Barevnost se v CIECAM02 liší od barevnosti L*a*b* a RGB. TP_COLORAPP_CIECAT_DEGREE;CAT02 přizpůsobení -TP_COLORAPP_CONTRAST;Kontrast (I) +TP_COLORAPP_CONTRAST;Kontrast (J) TP_COLORAPP_CONTRAST_Q;Kontrast (O) TP_COLORAPP_CONTRAST_Q_TOOLTIP;Liší se od kontrastu L*a*b* a RGB. TP_COLORAPP_CONTRAST_TOOLTIP;Liší se od kontrastu L*a*b* a RGB. @@ -1445,17 +1506,29 @@ TP_COLORAPP_GAMUT;Kontrola gamutu (L*a*b*) TP_COLORAPP_GAMUT_TOOLTIP;Povolí kontrolu gamutu v L*a*b* režimu. TP_COLORAPP_HUE;Odstín (h) TP_COLORAPP_HUE_TOOLTIP;Odstín (h) - úhel mezi 0° a 360°. +TP_COLORAPP_IL41;D41 +TP_COLORAPP_IL50;D50 +TP_COLORAPP_IL55;D55 +TP_COLORAPP_IL60;D60 +TP_COLORAPP_IL65;D65 +TP_COLORAPP_IL75;D75 +TP_COLORAPP_ILA;Žárovka StdA 2856K +TP_COLORAPP_ILFREE;Volná +TP_COLORAPP_ILLUM;Osvětlení +TP_COLORAPP_ILLUM_TOOLTIP;Vyberte osvětlení nejvíce se blížící podmínkám v době pořízení snímku.\nObecně D50, to se ale může lišit v závislosti na denní době a zeměpisné šířce. TP_COLORAPP_LABEL;CIE model přizpůsobení barev 2002 TP_COLORAPP_LABEL_CAM02;Úpravy obrázku TP_COLORAPP_LABEL_SCENE;Podmínky scény TP_COLORAPP_LABEL_VIEWING;Podmínky zobrazení -TP_COLORAPP_LIGHT;Světlost (I) +TP_COLORAPP_LIGHT;Světlost (J) TP_COLORAPP_LIGHT_TOOLTIP;Světlost v CIECAM02 se liší od světlosti v L*a*b* a RGB. TP_COLORAPP_MEANLUMINANCE;Střední jas (Yb%) TP_COLORAPP_MODEL;VB - Model TP_COLORAPP_MODEL_TOOLTIP;Model bílého bodu.\n\nWB [RT] + [výstup]: Pro scénu je použito vyvážení bílé RawTherapee , CIECAM02 je nastaven na D50 a vyvážení bílé výstupního zařízení je nastaveno v Podmínkách prohlížení.\n\nWB [RT+CAT02] + [výstup]: CAT02 používá RawTherapee nastavení vyvážení bílé a vyvážení bílé výstupního zařízení je nastaveno v Podmínkách prohlížení.\n\nVolná teplota+zelená + CAT02 + [výstup]: teplota a zelená je vybrána uživatelem, vyvážení bílé výstupního zařízení je nastaveno v Podmínkách prohlížení. TP_COLORAPP_NEUTRAL;Obnovit TP_COLORAPP_NEUTRAL_TIP;Obnoví původní hodnoty u všech posuvníků a křivek. +TP_COLORAPP_PRESETCAT02;Automatické přednastavení Cat02 +TP_COLORAPP_PRESETCAT02_TIP;Nastaví volby, posuvníky, teplotu a zelenou podle Cat02 automatického přednastavení.\nMusíte nastavit světelné podmínky při fotografování.\nPokud je potřeba, musíte změnit Cat02 podmínky přizpůsobení pro prohlížení.\nPokud je potřeba, můžete změnit teplotu a odstín podmínek při prohlížení a také další nastavení. TP_COLORAPP_RSTPRO;Ochrana červených a pleťových tónů TP_COLORAPP_RSTPRO_TOOLTIP;Ochrana červených a pleťových tónů ovlivňuje posuvníky i křivky. TP_COLORAPP_SURROUND;Okolí @@ -1472,7 +1545,9 @@ TP_COLORAPP_TCMODE_LABEL2;Mód křivky 2 TP_COLORAPP_TCMODE_LABEL3;Mód barevné křivky TP_COLORAPP_TCMODE_LIGHTNESS;Světlost TP_COLORAPP_TCMODE_SATUR;Nasycení -TP_COLORAPP_TEMP_TOOLTIP;Pro výběr osvětlení vždy nastavte Tint=1.\n\nA barva=2856\nD50 barva=5003\nD55 barva=5503\nD65 barva=6504\nD75 barva=7504 +TP_COLORAPP_TEMP2_TOOLTIP;Buď symetrický režim teploty = Nastavení bílé,\nNebo vyberte osvětlení, vždy nastavte Odstín=1.\n\nA barva=2856\nD50 barva=5003\nD55 barva=5503\nD65 barva=6504\nD75 barva=7504 +TP_COLORAPP_TEMPOUT_TOOLTIP;Zakažte pro změnu teploty a nádechu +TP_COLORAPP_TEMP_TOOLTIP;Pro výběr osvětlení vždy nastavte Odstín=1.\n\nA barva=2856\nD41 temp=4100\nD50 barva=5003\nD55 barva=5503\nD60 temp=6000\nD65 barva=6504\nD75 barva=7504 TP_COLORAPP_TONECIE;Mapování tónů pomocí CIECAM02 TP_COLORAPP_TONECIE_TOOLTIP;Pokud je volba zakázána, probíhá mapování tónů v prostoru L*a*b*.\nPokud je volba povolena. probíhá mapování tónů pomocí CIECAM02.\nAby měla tato volba efekt, musí být povolen nástroj Mapování tónů. TP_COLORAPP_VIEWING_ABSOLUTELUMINANCE_TOOLTIP;Absolutní jas prostředí prohlížení\n(obvykle 16 cd/m²). @@ -1499,7 +1574,7 @@ TP_COLORTONING_LABREGION_CHANNEL_B;Modrá TP_COLORTONING_LABREGION_CHANNEL_G;Zelená TP_COLORTONING_LABREGION_CHANNEL_R;Červená TP_COLORTONING_LABREGION_CHROMATICITYMASK;C -TP_COLORTONING_LABREGION_HUEMASK;H +TP_COLORTONING_LABREGION_HUEMASK;H TP_COLORTONING_LABREGION_LIGHTNESS;Světlost TP_COLORTONING_LABREGION_LIGHTNESSMASK;L TP_COLORTONING_LABREGION_LIST_TITLE;Oprava @@ -1561,6 +1636,7 @@ TP_DEFRINGE_RADIUS;Poloměr TP_DEFRINGE_THRESHOLD;Práh TP_DEHAZE_DEPTH;Hloubka TP_DEHAZE_LABEL;Odstranění závoje +TP_DEHAZE_LUMINANCE;Pouze jas TP_DEHAZE_SHOW_DEPTH_MAP;Ukázat hloubkovou mapu TP_DEHAZE_STRENGTH;Síla TP_DIRPYRDENOISE_CHROMINANCE_AMZ;Více zónová automatika @@ -1669,6 +1745,15 @@ TP_EXPOSURE_TCMODE_STANDARD;Běžný TP_EXPOSURE_TCMODE_WEIGHTEDSTD;Běžný vážený TP_EXPOS_BLACKPOINT_LABEL;Raw černé body TP_EXPOS_WHITEPOINT_LABEL;Raw bílé body +TP_FILMNEGATIVE_BLUE;Poměr modré +TP_FILMNEGATIVE_FILMBASE_PICK;Výběr barvy podkladu filmu +TP_FILMNEGATIVE_FILMBASE_TOOLTIP;Vyberte místo neexponovaného filmu (například okraj mezi snímky) pro získání aktuální barvy podkladu a uložte jej do profilu zpracování.\nTo umožňuje jednoduchou kontrolu konzistence vyvážení barev během dávkového zpracování více obrázků ze stejného filmu.\nTaké použijte pokud jsou převáděné snímky moc tmavé, přesvícené nebo barevně nevyvážené. +TP_FILMNEGATIVE_FILMBASE_VALUES;Barva podkladu filmu: +TP_FILMNEGATIVE_GREEN;Referenční exponent (kontrast) +TP_FILMNEGATIVE_GUESS_TOOLTIP;Automaticky nastaví poměr červené a modré výběrem dvou vzorků s neutrálním odstínem (bez barvy) v původní scéně. Vzorky by se měly lišit jasem. Následně je nastaveno vyvážení bílé. +TP_FILMNEGATIVE_LABEL;Negativní film +TP_FILMNEGATIVE_PICK;Výběr neutrálních míst +TP_FILMNEGATIVE_RED;Poměr červené TP_FILMSIMULATION_LABEL;Simulace filmu TP_FILMSIMULATION_SLOWPARSEDIR;RawTherapee je nakonfigurován aby hledal Hald CLUT obrázky pro nástroj Simulace filmu ve složce, jejíž načítání trvá velmi dlouho.\nZkontrolujte prosím nastavení v menu Volby > Zpracování obrázku > Simulace filmu.\nNastavená složka by měla buď obsahovat jen a pouze Hald CLUT obrázky nebo být prázdná, pokud nechcete nástroj Simulace filmu používat.\n\nVíce informací získáte v článku o nástroji Simulace filmu na RawPedii.\n\nChcete zrušit právě probíhající prohledávání složky? TP_FILMSIMULATION_STRENGTH;Síla @@ -1681,7 +1766,7 @@ TP_FLATFIELD_BT_HORIZONTAL;Vodorovně TP_FLATFIELD_BT_VERTHORIZ;Vodorovně a svisle TP_FLATFIELD_BT_VERTICAL;Svisle TP_FLATFIELD_CLIPCONTROL;Kontrola oříznutí -TP_FLATFIELD_CLIPCONTROL_TOOLTIP;Kontrola oříznutí zabrání oříznutí světel po aplikaci Flat Field. Pokud byly světla oříznuta ještě před aplikací Flat field, může se objevit barevný nádech. +TP_FLATFIELD_CLIPCONTROL_TOOLTIP;Kontrola oříznutí zabrání oříznutí světel po aplikaci Flat Field. Pokud byly světla oříznuta ještě před aplikací Flat field, použije se hodnota 0. TP_FLATFIELD_LABEL;Flat Field TP_GENERAL_11SCALE_TOOLTIP;Efekt tohoto nástroje je viditelný pouze při přiblížení 1:1. TP_GRADIENT_CENTER;Střed @@ -1789,6 +1874,8 @@ TP_LABCURVE_RSTPRO_TOOLTIP;Pracuje s posuvníkem barevnosti a CC křivkou. TP_LENSGEOM_AUTOCROP;Automatický ořez TP_LENSGEOM_FILL;Automatické vyplnění TP_LENSGEOM_LABEL;Objektiv / Geometrie +TP_LENSGEOM_LIN;Lineární +TP_LENSGEOM_LOG;Logaritmická TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automaticky vybráno TP_LENSPROFILE_CORRECTION_LCPFILE;LCP soubor TP_LENSPROFILE_CORRECTION_MANUAL;Ručně vybráno @@ -1817,6 +1904,7 @@ TP_PCVIGNETTE_ROUNDNESS;Zaoblení TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Zaoblení:\n0 = čtverec,\n50 = elipsa,\n100 = kruh. TP_PCVIGNETTE_STRENGTH;Síla TP_PCVIGNETTE_STRENGTH_TOOLTIP;Síla filtru v expozičních stupních (v rozích). +TP_PDSHARPENING_LABEL;Doostření vstupu TP_PERSPECTIVE_HORIZONTAL;Vodorovně TP_PERSPECTIVE_LABEL;Perspektiva TP_PERSPECTIVE_VERTICAL;Svisle @@ -1836,6 +1924,10 @@ TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Vodorovně pouze u PDAF řádků TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Svisle TP_PREPROCESS_NO_FOUND;Nic nenalezeno TP_PREPROCESS_PDAFLINESFILTER;Filtr PDAF linek +TP_PREPROCWB_LABEL;Předzpracování Vyvážení bílé +TP_PREPROCWB_MODE;Mód +TP_PREPROCWB_MODE_AUTO;Automaticky +TP_PREPROCWB_MODE_CAMERA;Fotoaparát TP_PRSHARPENING_LABEL;Doostření po změně velikosti TP_PRSHARPENING_TOOLTIP;Obrázek po zmenšení doostří. Funguje pouze pokud je použita "Lanczos" metoda zmenšení. Náhled výsledku není v tomto nástroji možný. Podívejte se do RawPedie pro návod k použití. TP_RAWCACORR_AUTO;Automatická korekce @@ -1868,7 +1960,7 @@ TP_RAW_DCBENHANCE;Vylepšení DCB TP_RAW_DCBITERATIONS;Počet průchodů DCB TP_RAW_DCBVNG4;DCB+VNG4 TP_RAW_DMETHOD;Metoda -TP_RAW_DMETHOD_PROGRESSBAR;%1 demozajkování... +TP_RAW_DMETHOD_PROGRESSBAR;%1 demozajkování… TP_RAW_DMETHOD_PROGRESSBAR_REFINE;Vylepšení demozajkování... TP_RAW_DMETHOD_TOOLTIP;Poznámka: IGV a LMMSE jsou určeny pro obrázky s vysokým ISO, kterým pomáhají vyhnout se u redukce šumu vzniku vzorů, posterizaci a vyžehlenému vzhledu.\n\nPixel Shift je určen pro soubory Pentax/Sony Pixel Shift.\nPro soubory neobsahující Pixel Shift data je použita metoda AMaZE. TP_RAW_DUALDEMOSAICAUTOCONTRAST;Automatický práh @@ -1882,7 +1974,7 @@ TP_RAW_HD_TOOLTIP;Nižší hodnoty učiní detekci vypálených/mrtvých bodů a TP_RAW_HPHD;HPHD TP_RAW_IGV;IGV TP_RAW_IMAGENUM;Dílčí snímek -TP_RAW_IMAGENUM_SN;Režim SN +TP_RAW_IMAGENUM_SN;Režim Signál/Šum TP_RAW_IMAGENUM_TOOLTIP;Některé raw snímky obsahují několik podsnímků (Pentax/Sony Pixel Shift, Pentax 3-in-1 HDR, Canon Dual Pixel, Fuji EXR).\n\nV případě, že je pro demozajkování použita jiná metoda než Pixel Shift, tato volba určí, který podsnímek se použije.\n\nPokud je použita Pixel Shift metoda demozajkování na Pixel Shift raw soubory, budou použity všechny podsnímky a tato volba určí, který snímek bude použit pro pohyblivé části. TP_RAW_LABEL;Demozajkování TP_RAW_LMMSE;LMMSE @@ -1924,7 +2016,7 @@ TP_RAW_SENSOR_XTRANS_DMETHOD_TOOLTIP;Tří průchodová dává lepší výsledky TP_RAW_SENSOR_XTRANS_LABEL;Senzory s X-Trans maticí TP_RAW_VNG4;VNG4 TP_RAW_XTRANS;X-Trans -TP_RAW_XTRANSFAST;Fast X-Trans +TP_RAW_XTRANSFAST;Rychlý X-Trans TP_RESIZE_ALLOW_UPSCALING;Povolit zvětšení TP_RESIZE_APPLIESTO;Aplikovat na: TP_RESIZE_CROPPEDAREA;Oblast ořezu @@ -2047,10 +2139,12 @@ TP_SHARPENING_EDRADIUS;Poloměr TP_SHARPENING_EDTOLERANCE;Tolerance k hranám TP_SHARPENING_HALOCONTROL;Omezení halo efektu TP_SHARPENING_HCAMOUNT;Míra +TP_SHARPENING_ITERCHECK;Automatické omezení průchodů TP_SHARPENING_LABEL;Doostření TP_SHARPENING_METHOD;Metoda TP_SHARPENING_ONLYEDGES;Doostřit pouze hrany TP_SHARPENING_RADIUS;Poloměr +TP_SHARPENING_RADIUS_BOOST;Zvýšení poloměru rohu TP_SHARPENING_RLD;RL Dekonvoluce TP_SHARPENING_RLD_AMOUNT;Míra TP_SHARPENING_RLD_DAMPING;Útlum @@ -2110,11 +2204,17 @@ TP_WAVELET_BACKGROUND;Pozadí TP_WAVELET_BACUR;Křivka TP_WAVELET_BALANCE;Vyvážení kontrastu d/v-h TP_WAVELET_BALANCE_TOOLTIP;Změní vyvážení mezi směry vlnky: svisle-vodorovně a úhlopříčně.\nPokud je aktivován kontrast, barevnost nebo zbytkové tónové mapování je efekt díky vyvážení zesílen. -TP_WAVELET_BALCHRO;Vyvážení barev +TP_WAVELET_BALCHRO;Vyvážení barevnosti +TP_WAVELET_BALCHROM;Korekce odšumění Modrá-Červená TP_WAVELET_BALCHRO_TOOLTIP;Pokud je povoleno, křivka nebo posuvníky "Vyvážení kontrastu" ovlivňují i vyvážení barev. +TP_WAVELET_BALLUM;Korekce odšumění Bílá-Černá TP_WAVELET_BANONE;Nic TP_WAVELET_BASLI;Posuvník TP_WAVELET_BATYPE;Metoda vyvážení kontrastu +TP_WAVELET_BL;Úrovně rozmazání +TP_WAVELET_BLCURVE;Rozmazání dle úrovní +TP_WAVELET_BLURFRAME;Rozmazání +TP_WAVELET_BLUWAV;Útlum TP_WAVELET_CBENAB;Tónování a vyvážení barev TP_WAVELET_CB_TOOLTIP;Pro silnější hodnoty barevného tónování s kombinováním nebo bez rozkladu na vrstvy tónování\nPro menší hodnoty můžete změnit vyvážení bílé na barvu pozadí (obloha, ...) bez změny předního plánu, obecně více kontrastní. TP_WAVELET_CCURVE;Místní kontrast @@ -2124,22 +2224,32 @@ TP_WAVELET_CH3;Propojit kontrast úrovní TP_WAVELET_CHCU;Křivka TP_WAVELET_CHR;Barevnost - kontrast síla propojení TP_WAVELET_CHRO;Práh nasycené/pastelové +TP_WAVELET_CHROFRAME;Odšumění Barevnosti +TP_WAVELET_CHROMAFRAME;Barevnost +TP_WAVELET_CHROMCO;Hrubá barevnost +TP_WAVELET_CHROMFI;Jemná barevnost TP_WAVELET_CHRO_TOOLTIP;Nastaví úroveň vlnky, která bude prahová pro syté a pastelové barvy.\n1-x: syté\nx-9: pastelové\n\nHodnota bude ignorována pokud přesáhne množství úrovní vlnky. +TP_WAVELET_CHRWAV;Rozmazat barevnost TP_WAVELET_CHR_TOOLTIP;Upraví barevnost jako funkci "Kontrast úrovní" a "Barevnost - kontrast síla propojení" TP_WAVELET_CHSL;Posuvníky TP_WAVELET_CHTYPE;Metoda barevnost +TP_WAVELET_CLA;Čirost +TP_WAVELET_CLARI;Ostrá maska a čirost TP_WAVELET_COLORT;Neprůhlednost červená-zelená TP_WAVELET_COMPCONT;Kontrast TP_WAVELET_COMPGAMMA;Komprese gamy TP_WAVELET_COMPGAMMA_TOOLTIP;Úprava gamy zůstatku obrázku vám umožní vyvážit data a histogram. TP_WAVELET_COMPTM;Mapování tónů TP_WAVELET_CONTEDIT;Křivka kontrastu 'Po' +TP_WAVELET_CONTFRAME;Kontrast - komprese TP_WAVELET_CONTR;Gamut TP_WAVELET_CONTRA;Kontrast +TP_WAVELET_CONTRASTEDIT;Jemnější - Hrubší úrovně TP_WAVELET_CONTRAST_MINUS;Kontrast - TP_WAVELET_CONTRAST_PLUS;Kontrast + TP_WAVELET_CONTRA_TOOLTIP;Změní kontrast zůstatku obrazu. TP_WAVELET_CTYPE;Ovládání barevnosti +TP_WAVELET_CURVEEDITOR_BL_TOOLTIP;Zakázáno pokud je přiblížení přes 300% TP_WAVELET_CURVEEDITOR_CC_TOOLTIP;Mění lokální kontrast jako funkci originálního lokálního kontrastu(úsečka).\nNízké hodnoty na úsečce představují malý lokální kontrast (skutečné hodnoty okolo 10..20).\n50% z úsečky představuje průměrný lokální kontrast (skutečné hodnoty okolo 100..300).\n66% z úsečky představuje představuje standardní odchylku lokálního kontrastu (skutečné hodnoty okolo 300..800).\n100% z úsečky představuje maximální lokální kontrast (skutečné hodnoty okolo 3000..8000). TP_WAVELET_CURVEEDITOR_CH;Kontrast úrovní=f(Barevnost) TP_WAVELET_CURVEEDITOR_CH_TOOLTIP;Mění kontrast každé úrovně jako funkci odstínu.\nDejte pozor, abyste nepřepsali změny udělané v podnástroji Gamut nástroje Odstín.\nZměny křivky se projeví pouze v případě, že posuvníky kontrastu úrovní vlnky nejsou nastaveny na nulu. @@ -2155,10 +2265,13 @@ TP_WAVELET_DAUB6;D6 - standard plus TP_WAVELET_DAUB10;D10 - střední TP_WAVELET_DAUB14;D14 - Vysoká TP_WAVELET_DAUB_TOOLTIP;Změní Daubechiesové koeficienty:\nD4 = Standard,\nD14 = Nejčastěji nejlepší výkon, ale o 10% delší zpracování .\n\nOvlivňuje detekci hran a obecnou kvalitu obrázku na prvních úrovních.Ovšem kvalita není striktně vázána na koeficienty a může se lišit v závislosti na obrázku a použití. +TP_WAVELET_DIRFRAME;Směrový kontrast TP_WAVELET_DONE;Svisle TP_WAVELET_DTHR;Napříč TP_WAVELET_DTWO;Vodorovně TP_WAVELET_EDCU;Křivka +TP_WAVELET_EDEFFECT;Útlum +TP_WAVELET_EDEFFECT_TOOLTIP;Posuvník ovlivňuje rozsah hodnot kontrastu, které získají maximální efekt nástroje.\nMaximální hodnota (2.5) nástroj zakáže. TP_WAVELET_EDGCONT;Místní kontrast TP_WAVELET_EDGCONT_TOOLTIP;Posunutí bodů doleva snižuje kontrast a posunutí bodů doprava jej zvyšuje.\nRohy levý spodní, levý horní, pravý horní, pravý spodní postupně představují místní kontrast pro nízké hodnoty, průměr, průměr + stdev a maximum. TP_WAVELET_EDGE;Doostření hran @@ -2178,10 +2291,12 @@ TP_WAVELET_EDSL;Práh posuvníků TP_WAVELET_EDTYPE;Metoda místního kontrastu TP_WAVELET_EDVAL;Síla TP_WAVELET_FINAL;Finální doladění +TP_WAVELET_FINCFRAME;Finální místní kontrast +TP_WAVELET_FINCOAR_TOOLTIP;Levá (pozitivní) část křivky působí na jemnější úrovně (navýšení).\nDva body na úsečce představují příslušné akční limity jemnějšía hrubší úrovně 5 a 6 (výchozí). Pravá (negativní) část křivky působí na hrubší úrovně (navýšení).\nVyvarujte se posouvání levé části křivky se zápornými hodnotami. Vyvarujte se posouvání pravé části křivky s kladnými hodnotami. TP_WAVELET_FINEST;Nejjemnější -TP_WAVELET_HIGHLIGHT;Zvýrazněný rozsah jasů +TP_WAVELET_HIGHLIGHT;Jemnější úrovně rozsahu jasu TP_WAVELET_HS1;Celý rozsah jasů -TP_WAVELET_HS2;Stíny/Světla +TP_WAVELET_HS2;Výběrový rozsah jasu TP_WAVELET_HUESKIN;Odstín pleti TP_WAVELET_HUESKIN_TOOLTIP;Spodní body nastaví začátek zóny přenosu a horní body její konec. Tam bude efekt největší.\n\nPokud potřebujete oblast výrazně změnit nebo se objevily artefakty, je nastavení vyvážení bílé nesprávné. TP_WAVELET_HUESKY;Odstín oblohy @@ -2192,9 +2307,9 @@ TP_WAVELET_LABEL;Úrovně vlnky TP_WAVELET_LARGEST;Nejhrubší TP_WAVELET_LEVCH;Barevnost TP_WAVELET_LEVDIR_ALL;Všechny úrovně ve všech směrech -TP_WAVELET_LEVDIR_INF;Méně nebo shodně s úrovní +TP_WAVELET_LEVDIR_INF;Jemnější úrovně detailů s vybranou úrovní TP_WAVELET_LEVDIR_ONE;Jedna úroveň -TP_WAVELET_LEVDIR_SUP;Nad úrovní +TP_WAVELET_LEVDIR_SUP;Hrubší úrovně detailů s vybranou úrovní TP_WAVELET_LEVELS;Úrovně vlnky TP_WAVELET_LEVELS_TOOLTIP;Vyberte počet úrovní detailu mezi které bude obrázek rozložen. Více úrovní potřebuje více paměti a zpracování trvá déle. TP_WAVELET_LEVF;Kontrast @@ -2205,57 +2320,89 @@ TP_WAVELET_LEVTWO;Úroveň 3 TP_WAVELET_LEVZERO;Úroveň 1 TP_WAVELET_LINKEDG;Spojit se sílou doostření hran TP_WAVELET_LIPST;Vylepšený algoritmus -TP_WAVELET_LOWLIGHT;Rozsah jasu a stínů +TP_WAVELET_LOWLIGHT;Hrubší úrovně rozsahu jasu +TP_WAVELET_LOWTHR_TOOLTIP;Zabraňuje zesílení šumu v jemných texturách TP_WAVELET_MEDGREINF;První úroveň TP_WAVELET_MEDI;Omezení artefaktů na modré obloze TP_WAVELET_MEDILEV;Detekce hran TP_WAVELET_MEDILEV_TOOLTIP;Pro povolení Detekce hran Vám doporučujeme:\n- zakázat úrovně s nízkým kontrastem pro vyhnutí se vzniku artefaktů,\n- použít vysoké hodnoty gradientu citlivosti.\n\nSílu můžete ovlivnit pomocí 'vylepšení' z Odšumění a vylepšení. +TP_WAVELET_MERGEC;Sloučení barevnosti +TP_WAVELET_MERGEL;Sloučení jasu TP_WAVELET_NEUTRAL;Neutrální TP_WAVELET_NOIS;Odšumění TP_WAVELET_NOISE;Odšumění a vylepšení +TP_WAVELET_NOISE_TOOLTIP;Pokud je čtvrtá úroveň jasu odšumění lepší než 20, použije se Agresivní režim.\nPokud je hrubší barevnost lepší než 20, použije se Agresívní režim. TP_WAVELET_NPHIGH;Vysoká TP_WAVELET_NPLOW;Nízká TP_WAVELET_NPNONE;Nic TP_WAVELET_NPTYPE;Sousední pixely TP_WAVELET_NPTYPE_TOOLTIP;Tento algoritmus zkoumá blízkost pixelu a jeho osmi sousedů. V případě menšího rozdílu je hrana zesílena. +TP_WAVELET_OFFSET_TOOLTIP;Posun změní vyvážení mezi světly a stíny.\nVyšší hodnoty zdůrazní změnu kontrastu světel, kdežto nižší hodnoty zdůrazní změnu kontrastu stínů.\nZároveň s nízkou hodnotou útlumu máte možnost si vybrat, které kontrasty budou zvýrazněny. +TP_WAVELET_OLDSH;Algoritmus používá záporné hodnoty TP_WAVELET_OPACITY;Neprůhlednost modrá-žlutá TP_WAVELET_OPACITYW;Vyrovnání kontrastu d/v-h křivka -TP_WAVELET_OPACITYWL;Finální místní kontrast +TP_WAVELET_OPACITYWL;Místní kontrast TP_WAVELET_OPACITYWL_TOOLTIP;Změní finální lokální kontrast na konci zpracování vlnky.\n\nLevá strana představuje nejmenší lokální kontrast a pravá strana zase největší lokální kontrast. TP_WAVELET_PASTEL;Barevnost pastelů TP_WAVELET_PROC;Zpracování +TP_WAVELET_PROTAB;Ochrana +TP_WAVELET_RADIUS;Poloměr Stíny - Světla +TP_WAVELET_RANGEAB;Rozsah a a b % TP_WAVELET_RE1;Zesílená TP_WAVELET_RE2;Nezměněno TP_WAVELET_RE3;Omezená -TP_WAVELET_RESCHRO;Barevnost +TP_WAVELET_RESBLUR;Jas rozmazání +TP_WAVELET_RESBLURC;Barevnost rozmazání +TP_WAVELET_RESBLUR_TOOLTIP;Zakázáno pokud je přiblížení přes 500% +TP_WAVELET_RESCHRO;Intenzita TP_WAVELET_RESCON;Stíny TP_WAVELET_RESCONH;Světla TP_WAVELET_RESID;Zůstatek obrazu TP_WAVELET_SAT;Nasycená barevnost TP_WAVELET_SETTINGS;Nastavení vlnky +TP_WAVELET_SHA;Ostrá maska +TP_WAVELET_SHFRAME;Stíny/Světla +TP_WAVELET_SHOWMASK;Ukázat vlnkovou 'masku' +TP_WAVELET_SIGMA;Útlum +TP_WAVELET_SIGMAFIN;Útlum +TP_WAVELET_SIGMA_TOOLTIP;Efekt posuvníků kontrastu je silnější u detailů se středními hodnotami kontrastu, a slabší u detailů s vysokými nebo nízkými hodnotami kontrastu.\n Tímto posuvníkem můžete kontrolovat jak rychle je potlačen efekt u extrémních hodnot kontrastu.\n Čím výše je posuvník nastaven, tím širší je rozsah hodnot kontrastů u kterých nastane silnější změna s větší šancí na vytvoření artefaktů.\n Čím je níže, tím přesněji bude účinek aplikován na úzkýrozsah hodnot kontrastu. TP_WAVELET_SKIN;Ochrana: zaměření na pleťové tóny TP_WAVELET_SKIN_TOOLTIP;Hodnota -100: zaměřeno na pleťové tóny.\nHodnota 0: se všemi tóny je zacházeno stejně.\nHodnota +100: pleťové tóny jsou chráněny zatímco všechny ostatní tóny jsou ovlivněny. TP_WAVELET_SKY;Ochrana a zaměření na tóny oblohy TP_WAVELET_SKY_TOOLTIP;Hodnota -100: zaměřeno na pleťové tóny.\nHodnota 0: se všemi tóny je zacházeno stejně.\nHodnota +100: pleťové tóny jsou chráněny zatímco všechny ostatní tóny jsou ovlivněny. +TP_WAVELET_SOFTRAD;Jemný poloměr TP_WAVELET_STREN;Síla TP_WAVELET_STRENGTH;Síla TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Práh stínů -TP_WAVELET_THRESHOLD;Úrovně světel -TP_WAVELET_THRESHOLD2;Úrovně stínů -TP_WAVELET_THRESHOLD2_TOOLTIP;Pouze úrovně 9 a 9 mínus hodnota budou ovlivněny rozsahem stínů a jasů.Ostatní úrovně budou plně zpracovány. Maximální možná úroveň je omezena na nejvyšší hodnotu úrovně (9 - nejvyšší úroveň). -TP_WAVELET_THRESHOLD_TOOLTIP;Pouze úrovně pod vybranou hodnotou budou ovlivněny rozsahem zvýraznění jasů.Ostatní úrovně budou plně zpracovány. Zde vybraná hodnota omezí nejvyšší možnou hodnotu úrovně stínů. +TP_WAVELET_THRESHOLD;Jemnější úrovně +TP_WAVELET_THRESHOLD2;Hrubší úrovně +TP_WAVELET_THRESHOLD2_TOOLTIP;Pouze úrovně mezi 9 a 9 mínus hodnota budou ovlivněny rozsahem jasu stínů. Ostatní úrovně budou upraveny celé. Nejvyšší možná úroveň je omezena hodnotou zvýrazněné úrovně (9 mínus hodnota zvýrazněné úrovně). Pouze úrovně mezi vybranou hodnotou a úrovní 9/Extra budou ovlivněny Hrubým rozsahem úrovní.\nVšechny ostatní úrovně budou ovlivněny v celém rozsahu jasu, pokud nebudou omezeny nastavením Jemnými úrovněmi.\nNejnižší možná úroveň, kterou bude algoritmus zvažovat, je omezená hodnotou Jemných úrovní. +TP_WAVELET_THRESHOLD_TOOLTIP;Pouze úrovně mimo vybranou hodnotu budou ovlivněny rozsahem jasu stínů. Ostatní úrovně budou upraveny celé. Zde vybraná hodnota omezuje nejvyšší možnou hodnotu úrovní stínů. Všechny úrovně od úrovně jedna až po vybranou úroveň ovlivněny Jemným rozsahem úrovní.\nVšechny ostatní úrovně budou ovlivněny v celém rozsahu jasu, pokud nebudou omezeny nastavením Hrubými úrovněmi.\nZde vybraná hodnota, se stane nejnižší možnou úrovní Hrubých úrovní. +TP_WAVELET_THRESWAV;Práh vyvážení TP_WAVELET_THRH;Práh světel -TP_WAVELET_TILESBIG;Velké dlaždice +TP_WAVELET_TILESBIG;Dlaždice TP_WAVELET_TILESFULL;Celý obrázek TP_WAVELET_TILESIZE;Metoda dlaždicování TP_WAVELET_TILESLIT;Malé dlaždice TP_WAVELET_TILES_TOOLTIP;Zpracování celého obrázku vede k lepší kvalitě a je doporučováno. Naproti tomu dlaždice jsou náhradní řešení pro uživatele s nedostatkem paměti. Paměťové nároky najdete na RawPedii. +TP_WAVELET_TMEDGS;Zachování hran +TP_WAVELET_TMSCALE;Měřítko TP_WAVELET_TMSTRENGTH;Síla komprese -TP_WAVELET_TMSTRENGTH_TOOLTIP;Ovládá sílu mapování tónů nebo kontrast komprese zůstatku obrázku. Pokud je hodnota rozdílná od nuly, budou posuvníky Síla a Gama v nástroji Mapování tónů v kartě Expozice neaktivní. +TP_WAVELET_TMSTRENGTH_TOOLTIP;Kontroluje sílu tónového mapování nebo komprese kontrastu zůstatku obrazu. TP_WAVELET_TMTYPE;Metoda komprese TP_WAVELET_TON;Tónování +TP_WAVELET_TONFRAME;Vyloučené barvy +TP_WAVELET_USH;Nic +TP_WAVELET_USHARP;Metoda čirosti +TP_WAVELET_USHARP_TOOLTIP;Původní : zdrojovým souborem je soubor před Vlnkou.\nVlnka : zdrojovým souborem je soubor s aplikovanou Vlnkou. +TP_WAVELET_USH_TOOLTIP;Pokud vyberete Ostrou masku, bude nastavení vlnky automaticky změněno na:\nPozadí=černá, zpracování=pod, úroveň=3 — ta může být změněna v rozmezí 1 až 4.\n\nPokud vyberete Čirost, bude nastavení vlnky automaticky změněno na:\nPozadí=zůstatek, zpracování=nad, úroveň=7 — ta může být změněna v rozmezí 5 až 10 úrovní vlnky. +TP_WAVELET_WAVLOWTHR;Práh nízkého kontrastu +TP_WAVELET_WAVOFFSET;Posun TP_WBALANCE_AUTO;Automaticky +TP_WBALANCE_AUTOITCGREEN;Teplotní korelace +TP_WBALANCE_AUTOOLD;RGB šedé +TP_WBALANCE_AUTO_HEADER;Automaticky TP_WBALANCE_CAMERA;Fotoaparát TP_WBALANCE_CLOUDY;Zataženo TP_WBALANCE_CUSTOM;Vlastní @@ -2297,6 +2444,8 @@ TP_WBALANCE_SOLUX41;Solux 4100K TP_WBALANCE_SOLUX47;Solux 4700K (vendor) TP_WBALANCE_SOLUX47_NG;Solux 4700K (Nat. Gallery) TP_WBALANCE_SPOTWB;Použijte pipetu pro nabrání vyvážení bílé z neutrální oblasti v náhledu. +TP_WBALANCE_STUDLABEL;Faktor korelace: %1 +TP_WBALANCE_STUDLABEL_TOOLTIP;Zobrazí vypočítanou korelaci metody Student\nNižší hodnoty jsou lepší, přičemž pro <0.005 jsou výborné\n<0.01 jsou dobré a >0.5 jsou špatné.\nNízké hodnoty neznamenají, že je vyvážení bílé dobré: pro nestandardní světelné podmínky jsou výsledky nepředvídatelné.\nHodnota 1000 znamená, že byl použit předchozí výpočet a výsledky jsou pravděpodobně dobré. TP_WBALANCE_TEMPBIAS;AVB - Zdůraznění teploty TP_WBALANCE_TEMPBIAS_TOOLTIP;Dovolí ovlivnit výpočet "automatického vyvážení bílé"\nzdůrazněním teplejší nebo chladnější teploty. Toto zdůraznění\nje vyjádřeno v procentech vypočtené teploty a výsledek\nlze vyjádřit vzorcem "vypočtenáTeplota + vypočtenáTeplota * zdůraznění". TP_WBALANCE_TEMPERATURE;Teplota @@ -2311,42 +2460,3 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Přizpůsobit ořez obrazovce\nZkratka: f ZOOMPANEL_ZOOMFITSCREEN;Přizpůsobit celý obrázek obrazovce\nZkratka: Alt-f ZOOMPANEL_ZOOMIN;Přiblížit\nZkratka: + ZOOMPANEL_ZOOMOUT;Oddálit\nZkratka: - - -!!!!!!!!!!!!!!!!!!!!!!!!! -! Untranslated keys follow; remove the ! prefix after an entry is translated. -!!!!!!!!!!!!!!!!!!!!!!!!! - -!FILEBROWSER_DELETEDIALOG_ALL;Are you sure you want to permanently delete all %1 files in trash? -!FILEBROWSER_DELETEDIALOG_SELECTED;Are you sure you want to permanently delete the selected %1 files? -!FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Are you sure you want to permanently delete the selected %1 files, including a queue-processed version? -!FILEBROWSER_EMPTYTRASHHINT;Permanently delete all files in trash. -!FILEBROWSER_POPUPREMOVE;Delete permanently -!FILEBROWSER_POPUPREMOVEINCLPROC;Delete permanently, including queue-processed version -!FILEBROWSER_SHOWNOTTRASHHINT;Show only images not in trash. -!GENERAL_HELP;Help -!HISTORY_MSG_494;Capture Sharpening -!HISTORY_MSG_DEHAZE_LUMINANCE;Dehaze - Luminance only -!HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative -!HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values -!HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold -!HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;CS - Auto radius -!HISTORY_MSG_PDSHARPEN_CHECKITER;CS - Auto limit iterations -!HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrast threshold -!HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Iterations -!HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius -!HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost -!HISTORY_MSG_TRANS_Method;Geometry - Method -!PARTIALPASTE_FILMNEGATIVE;Film Negative -!PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI mode -!TP_DEHAZE_LUMINANCE;Luminance only -!TP_FILMNEGATIVE_BLUE;Blue ratio -!TP_FILMNEGATIVE_GREEN;Reference exponent (contrast) -!TP_FILMNEGATIVE_GUESS_TOOLTIP;Automatically set the red and blue ratios by picking two patches which had a neutral hue (no color) in the original scene. The patches should differ in brightness. Set the white balance afterwards. -!TP_FILMNEGATIVE_LABEL;Film Negative -!TP_FILMNEGATIVE_PICK;Pick neutral spots -!TP_FILMNEGATIVE_RED;Red ratio -!TP_LENSGEOM_LIN;Linear -!TP_LENSGEOM_LOG;Logarithmic -!TP_PDSHARPENING_LABEL;Capture Sharpening -!TP_SHARPENING_ITERCHECK;Auto limit iterations -!TP_SHARPENING_RADIUS_BOOST;Corner radius boost diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US) index d8c497f16..1bd319e6f 100644 --- a/rtdata/languages/English (US) +++ b/rtdata/languages/English (US) @@ -937,7 +937,7 @@ !MAIN_TOOLTIP_BACKCOLOR0;Background color of the preview: theme-based\nShortcut: 9 !MAIN_TOOLTIP_BACKCOLOR1;Background color of the preview: black\nShortcut: 9 !MAIN_TOOLTIP_BACKCOLOR2;Background color of the preview: white\nShortcut: 9 -!MAIN_TOOLTIP_BACKCOLOR3;Background color of the preview: middle grey\nShortcut: 9 +!MAIN_TOOLTIP_BACKCOLOR3;Background color of the preview: middle gray\nShortcut: 9 !MAIN_TOOLTIP_BEFOREAFTERLOCK;Lock / Unlock the Before view\n\nLock: keep the Before view unchanged.\nUseful to evaluate the cumulative effect of multiple tools.\nAdditionally, comparisons can be made to any state in the History.\n\nUnlock: the Before view will follow the After view one step behind, showing the image before the effect of the currently used tool. !MAIN_TOOLTIP_HIDEHP;Show/Hide the left panel (including the history).\nShortcut: l !MAIN_TOOLTIP_INDCLIPPEDH;Clipped highlight indication.\nShortcut: > @@ -2097,7 +2097,7 @@ !TP_WAVELET_APPLYTO;Apply To !TP_WAVELET_AVOID;Avoid color shift !TP_WAVELET_B0;Black -!TP_WAVELET_B1;Grey +!TP_WAVELET_B1;Gray !TP_WAVELET_B2;Residual !TP_WAVELET_BACKGROUND;Background !TP_WAVELET_BACUR;Curve diff --git a/rtdata/languages/Nederlands b/rtdata/languages/Nederlands index c43f6314e..ba5880bfb 100644 --- a/rtdata/languages/Nederlands +++ b/rtdata/languages/Nederlands @@ -14,6 +14,7 @@ #14 2015-11-23 update by wim ter meer #15 2016-07-21 update by wim ter meer #16 2017-04-21 update by wim ter meer +#17 2020-06-05 update by dheijl ABOUT_TAB_BUILD;Versie ABOUT_TAB_CREDITS;Credits @@ -1962,367 +1963,367 @@ ZOOMPANEL_ZOOMOUT;Zoom uit\nSneltoets: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! -!ADJUSTER_RESET_TO_DEFAULT;Click - reset to default value.\nCtrl+click - reset to initial value. -!CURVEEDITOR_CATMULLROM;Flexible -!DONT_SHOW_AGAIN;Don't show this message again. -!DYNPROFILEEDITOR_IMGTYPE_ANY;Any -!DYNPROFILEEDITOR_IMGTYPE_HDR;HDR -!DYNPROFILEEDITOR_IMGTYPE_PS;Pixel Shift -!DYNPROFILEEDITOR_IMGTYPE_STD;Standard -!EXIFFILTER_IMAGETYPE;Image type -!EXIFPANEL_SHOWALL;Show all -!FILEBROWSER_BROWSEPATHBUTTONHINT;Click to open specified path, reload folder and apply "find" keywords. -!FILEBROWSER_CACHECLEARFROMFULL;Clear all including cached profiles -!FILEBROWSER_CACHECLEARFROMPARTIAL;Clear all except cached profiles -!FILEBROWSER_DELETEDIALOG_ALL;Are you sure you want to permanently delete all %1 files in trash? -!FILEBROWSER_DELETEDIALOG_SELECTED;Are you sure you want to permanently delete the selected %1 files? -!FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Are you sure you want to permanently delete the selected %1 files, including a queue-processed version? -!FILEBROWSER_EMPTYTRASHHINT;Permanently delete all files in trash. -!FILEBROWSER_POPUPREMOVE;Delete permanently -!FILEBROWSER_POPUPREMOVEINCLPROC;Delete permanently, including queue-processed version -!FILEBROWSER_SHOWNOTTRASHHINT;Show only images not in trash. -!GENERAL_CURRENT;Current -!GENERAL_HELP;Help -!GENERAL_RESET;Reset -!GENERAL_SAVE_AS;Save as... -!GENERAL_SLIDER;Slider -!GIMP_PLUGIN_INFO;Welcome to the RawTherapee GIMP plugin!\nOnce you are done editing, simply close the main RawTherapee window and the image will be automatically imported in GIMP. -!HISTOGRAM_TOOLTIP_MODE;Toggle between linear, log-linear and log-log scaling of the histogram. -!HISTORY_MSG_173;NR - Detail recovery -!HISTORY_MSG_203;NR - Color space -!HISTORY_MSG_235;B&W - CM - Auto -!HISTORY_MSG_237;B&W - CM -!HISTORY_MSG_256;NR - Median - Type -!HISTORY_MSG_273;CT - Color Balance SMH -!HISTORY_MSG_297;NR - Mode -!HISTORY_MSG_392;W - Residual - Color Balance -!HISTORY_MSG_441;Retinex - Gain transmission -!HISTORY_MSG_475;PS - Equalize channel -!HISTORY_MSG_476;CAM02 - Temp out -!HISTORY_MSG_477;CAM02 - Green out -!HISTORY_MSG_478;CAM02 - Yb out -!HISTORY_MSG_479;CAM02 - CAT02 adaptation out -!HISTORY_MSG_480;CAM02 - Automatic CAT02 out -!HISTORY_MSG_481;CAM02 - Temp scene -!HISTORY_MSG_482;CAM02 - Green scene -!HISTORY_MSG_483;CAM02 - Yb scene -!HISTORY_MSG_484;CAM02 - Auto Yb scene -!HISTORY_MSG_485;Lens Correction -!HISTORY_MSG_486;Lens Correction - Camera -!HISTORY_MSG_487;Lens Correction - Lens -!HISTORY_MSG_488;Dynamic Range Compression -!HISTORY_MSG_489;DRC - Detail -!HISTORY_MSG_490;DRC - Amount -!HISTORY_MSG_491;White Balance -!HISTORY_MSG_492;RGB Curves -!HISTORY_MSG_493;L*a*b* Adjustments -!HISTORY_MSG_494;Capture Sharpening -!HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors -!HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction -!HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Color correction -!HISTORY_MSG_COLORTONING_LABREGION_CHANNEL;CT - Channel -!HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - region C mask -!HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - H mask -!HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - Lightness -!HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L mask -!HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - List -!HISTORY_MSG_COLORTONING_LABREGION_MASKBLUR;CT - region mask blur -!HISTORY_MSG_COLORTONING_LABREGION_OFFSET;CT - region offset -!HISTORY_MSG_COLORTONING_LABREGION_POWER;CT - region power -!HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - Saturation -!HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - region show mask -!HISTORY_MSG_COLORTONING_LABREGION_SLOPE;CT - region slope -!HISTORY_MSG_DEHAZE_DEPTH;Dehaze - Depth -!HISTORY_MSG_DEHAZE_ENABLED;Haze Removal -!HISTORY_MSG_DEHAZE_LUMINANCE;Dehaze - Luminance only -!HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Dehaze - Show depth map -!HISTORY_MSG_DEHAZE_STRENGTH;Dehaze - Strength -!HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual demosaic - Auto threshold -!HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual demosaic - Contrast threshold -!HISTORY_MSG_FILMNEGATIVE_ENABLED;Film Negative -!HISTORY_MSG_FILMNEGATIVE_VALUES;Film negative values -!HISTORY_MSG_HISTMATCHING;Auto-matched tone curve -!HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Output - Primaries -!HISTORY_MSG_ICM_OUTPUT_TEMP;Output - ICC-v4 illuminant D -!HISTORY_MSG_ICM_OUTPUT_TYPE;Output - Type -!HISTORY_MSG_ICM_WORKING_GAMMA;Working - Gamma -!HISTORY_MSG_ICM_WORKING_SLOPE;Working - Slope -!HISTORY_MSG_ICM_WORKING_TRC_METHOD;Working - TRC method -!HISTORY_MSG_LOCALCONTRAST_AMOUNT;Local Contrast - Amount -!HISTORY_MSG_LOCALCONTRAST_DARKNESS;Local Contrast - Darkness -!HISTORY_MSG_LOCALCONTRAST_ENABLED;Local Contrast -!HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Local Contrast - Lightness -!HISTORY_MSG_LOCALCONTRAST_RADIUS;Local Contrast - Radius -!HISTORY_MSG_METADATA_MODE;Metadata copy mode -!HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrast threshold -!HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto threshold -!HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;CS - Auto radius -!HISTORY_MSG_PDSHARPEN_CHECKITER;CS - Auto limit iterations -!HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrast threshold -!HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Iterations -!HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius -!HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost -!HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion -!HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction -!HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter -!HISTORY_MSG_PRSHARPEN_CONTRAST;PRS - Contrast threshold -!HISTORY_MSG_RAWCACORR_AUTOIT;Raw CA Correction - Iterations -!HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw CA Correction - Avoid color shift -!HISTORY_MSG_RAW_BORDER;Raw border -!HISTORY_MSG_RESIZE_ALLOWUPSCALING;Resize - Allow upscaling -!HISTORY_MSG_SHARPENING_BLUR;Sharpening - Blur radius -!HISTORY_MSG_SHARPENING_CONTRAST;Sharpening - Contrast threshold -!HISTORY_MSG_SH_COLORSPACE;S/H - Colorspace -!HISTORY_MSG_SOFTLIGHT_ENABLED;Soft light -!HISTORY_MSG_SOFTLIGHT_STRENGTH;Soft light - Strength -!HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anchor -!HISTORY_MSG_TRANS_Method;Geometry - Method -!ICCPROFCREATOR_COPYRIGHT;Copyright: -!ICCPROFCREATOR_COPYRIGHT_RESET_TOOLTIP;Reset to the default copyright, granted to "RawTherapee, CC0" -!ICCPROFCREATOR_CUSTOM;Custom -!ICCPROFCREATOR_DESCRIPTION;Description: -!ICCPROFCREATOR_DESCRIPTION_ADDPARAM;Append gamma and slope values to the description -!ICCPROFCREATOR_DESCRIPTION_TOOLTIP;Leave empty to set the default description. -!ICCPROFCREATOR_GAMMA;Gamma -!ICCPROFCREATOR_ICCVERSION;ICC version: -!ICCPROFCREATOR_ILL;Illuminant: -!ICCPROFCREATOR_ILL_41;D41 -!ICCPROFCREATOR_ILL_50;D50 -!ICCPROFCREATOR_ILL_55;D55 -!ICCPROFCREATOR_ILL_60;D60 -!ICCPROFCREATOR_ILL_65;D65 -!ICCPROFCREATOR_ILL_80;D80 -!ICCPROFCREATOR_ILL_DEF;Default -!ICCPROFCREATOR_ILL_INC;StdA 2856K -!ICCPROFCREATOR_ILL_TOOLTIP;You can only set the illuminant for ICC v4 profiles. -!ICCPROFCREATOR_PRIMARIES;Primaries: -!ICCPROFCREATOR_PRIM_ACESP0;ACES AP0 -!ICCPROFCREATOR_PRIM_ACESP1;ACES AP1 -!ICCPROFCREATOR_PRIM_ADOBE;Adobe RGB (1998) -!ICCPROFCREATOR_PRIM_BEST;BestRGB -!ICCPROFCREATOR_PRIM_BETA;BetaRGB -!ICCPROFCREATOR_PRIM_BLUX;Blue X -!ICCPROFCREATOR_PRIM_BLUY;Blue Y -!ICCPROFCREATOR_PRIM_BRUCE;BruceRGB -!ICCPROFCREATOR_PRIM_GREX;Green X -!ICCPROFCREATOR_PRIM_GREY;Green Y -!ICCPROFCREATOR_PRIM_PROPH;Prophoto -!ICCPROFCREATOR_PRIM_REC2020;Rec2020 -!ICCPROFCREATOR_PRIM_REDX;Red X -!ICCPROFCREATOR_PRIM_REDY;Red Y -!ICCPROFCREATOR_PRIM_SRGB;sRGB -!ICCPROFCREATOR_PRIM_TOOLTIP;You can only set custom primaries for ICC v4 profiles. -!ICCPROFCREATOR_PRIM_WIDEG;Widegamut -!ICCPROFCREATOR_PROF_V2;ICC v2 -!ICCPROFCREATOR_PROF_V4;ICC v4 -!ICCPROFCREATOR_SAVEDIALOG_TITLE;Save ICC profile as... -!ICCPROFCREATOR_SLOPE;Slope -!ICCPROFCREATOR_TRC_PRESET;Tone response curve: -!MAIN_BUTTON_ICCPROFCREATOR;ICC Profile Creator -!MAIN_FRAME_PLACES_DEL;Remove -!MAIN_MSG_TOOMANYOPENEDITORS;Too many open editors.\nPlease close an editor to continue. -!MAIN_TAB_ADVANCED;Advanced -!MAIN_TAB_ADVANCED_TOOLTIP;Shortcut: Alt-a -!MAIN_TAB_FAVORITES;Favorites -!MAIN_TAB_FAVORITES_TOOLTIP;Shortcut: Alt-u -!MAIN_TOOLTIP_BACKCOLOR3;Background color of the preview: middle grey\nShortcut: 9 -!MAIN_TOOLTIP_PREVIEWSHARPMASK;Preview the sharpening contrast mask.\nShortcut: p\n\nOnly works when sharpening is enabled and zoom >= 100%. -!OPTIONS_BUNDLED_MISSING;The bundled profile "%1" could not be found!\n\nYour installation could be damaged.\n\nDefault internal values will be used instead. -!OPTIONS_DEFIMG_MISSING;The default profile for non-raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead. -!OPTIONS_DEFRAW_MISSING;The default profile for raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead. -!PARTIALPASTE_ADVANCEDGROUP;Advanced Settings -!PARTIALPASTE_DEHAZE;Haze removal -!PARTIALPASTE_FILMNEGATIVE;Film Negative -!PARTIALPASTE_LOCALCONTRAST;Local contrast -!PARTIALPASTE_METADATA;Metadata mode -!PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;PDAF lines filter -!PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA avoid color shift -!PARTIALPASTE_RAW_BORDER;Raw border -!PARTIALPASTE_SOFTLIGHT;Soft light -!PARTIALPASTE_TM_FATTAL;Dynamic range compression -!PREFERENCES_APPEARANCE;Appearance -!PREFERENCES_APPEARANCE_COLORPICKERFONT;Color picker font -!PREFERENCES_APPEARANCE_CROPMASKCOLOR;Crop mask color -!PREFERENCES_APPEARANCE_MAINFONT;Main font -!PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI mode -!PREFERENCES_APPEARANCE_THEME;Theme -!PREFERENCES_AUTOSAVE_TP_OPEN;Save tool collapsed/expanded state on exit -!PREFERENCES_CACHECLEAR;Clear -!PREFERENCES_CACHECLEAR_ALL;Clear all cached files: -!PREFERENCES_CACHECLEAR_ALLBUTPROFILES;Clear all cached files except for cached processing profiles: -!PREFERENCES_CACHECLEAR_ONLYPROFILES;Clear only cached processing profiles: -!PREFERENCES_CACHECLEAR_SAFETY;Only files in the cache are cleared. Processing profiles stored alongside the source images are not touched. -!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_CROP;Crop Editing -!PREFERENCES_CROP_AUTO_FIT;Automatically zoom to fit the crop -!PREFERENCES_CROP_GUIDES;Guides shown when not editing the crop -!PREFERENCES_CROP_GUIDES_FRAME;Frame -!PREFERENCES_CROP_GUIDES_FULL;Original -!PREFERENCES_CROP_GUIDES_NONE;None -!PREFERENCES_DIRECTORIES;Directories -!PREFERENCES_EDITORCMDLINE;Custom command line -!PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Compact toolbars in File Browser -!PREFERENCES_LANG;Language -!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_PROFILESAVEBOTH;Save processing profile both to the cache and next to the input file -!PREFERENCES_PROFILESAVELOCATION;Processing profile saving location -!PREFERENCES_SAVE_TP_OPEN_NOW;Save tool collapsed/expanded state now -!PREFERENCES_TAB_PERFORMANCE;Performance -!PREFERENCES_THUMBNAIL_INSPECTOR_JPEG;Embedded JPEG preview -!PREFERENCES_THUMBNAIL_INSPECTOR_MODE;Image to show -!PREFERENCES_THUMBNAIL_INSPECTOR_RAW;Neutral raw rendering -!PREFERENCES_THUMBNAIL_INSPECTOR_RAW_IF_NO_JPEG_FULLSIZE;Embedded JPEG if fullsize, neutral raw otherwise -!PROGRESSBAR_DECODING;Decoding... -!PROGRESSBAR_GREENEQUIL;Green equilibration... -!PROGRESSBAR_HLREC;Highlight reconstruction... -!PROGRESSBAR_HOTDEADPIXELFILTER;Hot/dead pixel filter... -!PROGRESSBAR_LINEDENOISE;Line noise filter... -!PROGRESSBAR_RAWCACORR;Raw CA correction... -!QINFO_FRAMECOUNT;%2 frames -!QINFO_HDR;HDR / %2 frame(s) -!QINFO_PIXELSHIFT;Pixel Shift / %2 frame(s) -!QUEUE_LOCATION_TITLE;Output Location -!QUEUE_STARTSTOP_TOOLTIP;Start or stop processing the images in the queue.\n\nShortcut: Ctrl+s -!SAMPLEFORMAT_0;Unknown data format -!SAMPLEFORMAT_1;8-bit unsigned -!SAMPLEFORMAT_2;16-bit unsigned -!SAMPLEFORMAT_4;24-bit LogLuv -!SAMPLEFORMAT_8;32-bit LogLuv -!SAMPLEFORMAT_16;16-bit floating-point -!SAMPLEFORMAT_32;24-bit floating-point -!SAMPLEFORMAT_64;32-bit floating-point -!SAVEDLG_FILEFORMAT_FLOAT; floating-point -!SOFTPROOF_GAMUTCHECK_TOOLTIP;Highlight pixels with out-of-gamut colors with respect to:\n- the printer profile, if one is set and soft-proofing is enabled,\n- the output profile, if a printer profile is not set and soft-proofing is enabled,\n- the monitor profile, if soft-proofing is disabled. -!SOFTPROOF_TOOLTIP;Soft-proofing simulates the appearance of the image:\n- when printed, if a printer profile is set in Preferences > Color Management,\n- when viewed on a display that uses the current output profile, if a printer profile is not set. -!TP_BWMIX_MIXC;Channel Mixer -!TP_BWMIX_NEUTRAL;Reset -!TP_COLORAPP_ABSOLUTELUMINANCE;Absolute luminance -!TP_COLORAPP_CAT02ADAPTATION_TOOLTIP;When setting manually, values above 65 are recommended. -!TP_COLORAPP_FREE;Free temp+green + CAT02 + [output] -!TP_COLORAPP_MEANLUMINANCE;Mean luminance (Yb%) -!TP_COLORAPP_NEUTRAL;Reset -!TP_COLORAPP_NEUTRAL_TIP;Reset all sliders checkbox and curves to their default values -!TP_COLORAPP_TEMP_TOOLTIP;To select an illuminant, always set Tint=1.\n\nA temp=2856\nD50 temp=5003\nD55 temp=5503\nD65 temp=6504\nD75 temp=7504 -!TP_COLORTONING_LABGRID;L*a*b* color correction grid -!TP_COLORTONING_LABGRID_VALUES;HL: a=%1 b=%2\nS: a=%3 b=%4 -!TP_COLORTONING_LABREGIONS;Color correction regions -!TP_COLORTONING_LABREGION_ABVALUES;a=%1 b=%2 -!TP_COLORTONING_LABREGION_CHANNEL;Channel -!TP_COLORTONING_LABREGION_CHANNEL_ALL;All -!TP_COLORTONING_LABREGION_CHANNEL_B;Blue -!TP_COLORTONING_LABREGION_CHANNEL_G;Green -!TP_COLORTONING_LABREGION_CHANNEL_R;Red -!TP_COLORTONING_LABREGION_CHROMATICITYMASK;C -!TP_COLORTONING_LABREGION_HUEMASK;H -!TP_COLORTONING_LABREGION_LIGHTNESS;Lightness -!TP_COLORTONING_LABREGION_LIGHTNESSMASK;L -!TP_COLORTONING_LABREGION_LIST_TITLE;Correction -!TP_COLORTONING_LABREGION_MASK;Mask -!TP_COLORTONING_LABREGION_MASKBLUR;Mask Blur -!TP_COLORTONING_LABREGION_OFFSET;Offset -!TP_COLORTONING_LABREGION_POWER;Power -!TP_COLORTONING_LABREGION_SATURATION;Saturation -!TP_COLORTONING_LABREGION_SHOWMASK;Show mask -!TP_COLORTONING_LABREGION_SLOPE;Slope -!TP_CROP_PPI;PPI -!TP_CROP_RESETCROP;Reset -!TP_CROP_SELECTCROP;Select -!TP_DEHAZE_DEPTH;Depth -!TP_DEHAZE_LABEL;Haze Removal -!TP_DEHAZE_LUMINANCE;Luminance only -!TP_DEHAZE_SHOW_DEPTH_MAP;Show depth map -!TP_DEHAZE_STRENGTH;Strength -!TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Increase (multiply) the value of all chrominance sliders.\nThis curve lets you adjust the strength of chromatic noise reduction as a function of chromaticity, for instance to increase the action in areas of low saturation and to decrease it in those of high saturation. -!TP_DIRPYRDENOISE_LABEL;Noise Reduction -!TP_EXPOSURE_CLAMPOOG;Clip out-of-gamut colors -!TP_EXPOSURE_HISTMATCHING;Auto-Matched Tone Curve -!TP_EXPOSURE_HISTMATCHING_TOOLTIP;Automatically adjust sliders and curves (except exposure compensation) to match the look of the embedded JPEG thumbnail. -!TP_FILMNEGATIVE_BLUE;Blue ratio -!TP_FILMNEGATIVE_GREEN;Reference exponent (contrast) -!TP_FILMNEGATIVE_GUESS_TOOLTIP;Automatically set the red and blue ratios by picking two patches which had a neutral hue (no color) in the original scene. The patches should differ in brightness. Set the white balance afterwards. -!TP_FILMNEGATIVE_LABEL;Film Negative -!TP_FILMNEGATIVE_PICK;Pick neutral spots -!TP_FILMNEGATIVE_RED;Red ratio -!TP_ICM_WORKING_TRC;Tone response curve: -!TP_ICM_WORKING_TRC_CUSTOM;Custom -!TP_ICM_WORKING_TRC_GAMMA;Gamma -!TP_ICM_WORKING_TRC_NONE;None -!TP_ICM_WORKING_TRC_SLOPE;Slope -!TP_ICM_WORKING_TRC_TOOLTIP;Only for built-in profiles. -!TP_LENSGEOM_LIN;Linear -!TP_LENSGEOM_LOG;Logarithmic -!TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatically selected -!TP_LENSPROFILE_CORRECTION_LCPFILE;LCP file -!TP_LENSPROFILE_CORRECTION_MANUAL;Manually selected -!TP_LENSPROFILE_LENS_WARNING;Warning: the crop factor used for lens profiling is larger than the crop factor of the camera, the results might be wrong. -!TP_LENSPROFILE_MODE_HEADER;Lens Profile -!TP_LENSPROFILE_USE_CA;Chromatic aberration -!TP_LENSPROFILE_USE_GEOMETRIC;Geometric distortion -!TP_LENSPROFILE_USE_HEADER;Correct -!TP_LENSPROFILE_USE_VIGNETTING;Vignetting -!TP_LOCALCONTRAST_AMOUNT;Amount -!TP_LOCALCONTRAST_DARKNESS;Darkness level -!TP_LOCALCONTRAST_LABEL;Local Contrast -!TP_LOCALCONTRAST_LIGHTNESS;Lightness level -!TP_LOCALCONTRAST_RADIUS;Radius -!TP_METADATA_EDIT;Apply modifications -!TP_METADATA_MODE;Metadata copy mode -!TP_METADATA_STRIP;Strip all metadata -!TP_METADATA_TUNNEL;Copy unchanged -!TP_PDSHARPENING_LABEL;Capture Sharpening -!TP_PREPROCESS_LINEDENOISE_DIRECTION;Direction -!TP_PREPROCESS_LINEDENOISE_DIRECTION_BOTH;Both -!TP_PREPROCESS_LINEDENOISE_DIRECTION_HORIZONTAL;Horizontal -!TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Horizontal only on PDAF rows -!TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Vertical -!TP_PREPROCESS_PDAFLINESFILTER;PDAF lines filter -!TP_RAWCACORR_AUTOIT;Iterations -!TP_RAWCACORR_AUTOIT_TOOLTIP;This setting is available if "Auto-correction" is checked.\nAuto-correction is conservative, meaning that it often does not correct all chromatic aberration.\nTo correct the remaining chromatic aberration, you can use up to five iterations of automatic chromatic aberration correction.\nEach iteration will reduce the remaining chromatic aberration from the last iteration at the cost of additional processing time. -!TP_RAWCACORR_AVOIDCOLORSHIFT;Avoid color shift -!TP_RAW_2PASS;1-pass+fast -!TP_RAW_4PASS;3-pass+fast -!TP_RAW_AMAZEVNG4;AMaZE+VNG4 -!TP_RAW_BORDER;Border -!TP_RAW_DCBVNG4;DCB+VNG4 -!TP_RAW_DUALDEMOSAICAUTOCONTRAST;Auto threshold -!TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP;If the checkbox is checked (recommended), RawTherapee calculates an optimum value based on flat regions in the image.\nIf there is no flat region in the image or the image is too noisy, the value will be set to 0.\nTo set the value manually, uncheck the checkbox first (reasonable values depend on the image). -!TP_RAW_DUALDEMOSAICCONTRAST;Contrast threshold -!TP_RAW_IMAGENUM_SN;SN mode -!TP_RAW_IMAGENUM_TOOLTIP;Some raw files consist of several sub-images (Pentax/Sony Pixel Shift, Pentax 3-in-1 HDR, Canon Dual Pixel, Fuji EXR).\n\nWhen using any demosaicing method other than Pixel Shift, this selects which sub-image is used.\n\nWhen using the Pixel Shift demosaicing method on a Pixel Shift raw, all sub-images are used, and this selects which sub-image should be used for moving parts. -!TP_RAW_PIXELSHIFTDMETHOD;Demosaic method for motion -!TP_RAW_PIXELSHIFTEPERISO;Sensitivity -!TP_RAW_PIXELSHIFTEPERISO_TOOLTIP;The default value of 0 should work fine for base ISO.\nHigher values increase sensitivity of motion detection.\nChange in small steps and watch the motion mask while changing.\nIncrease sensitivity for underexposed or high ISO images. -!TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL;Equalize per channel -!TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL_TOOLTIP;Enabled: Equalize the RGB channels individually.\nDisabled: Use same equalization factor for all channels. -!TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Overlays the image with a green mask showing the regions with motion. -!TP_RAW_RCD;RCD -!TP_RAW_RCDVNG4;RCD+VNG4 -!TP_RAW_XTRANS;X-Trans -!TP_RAW_XTRANSFAST;Fast X-Trans -!TP_RESIZE_ALLOW_UPSCALING;Allow Upscaling -!TP_RETINEX_CONTEDIT_MAP;Equalizer -!TP_RETINEX_GAINOFFS;Gain and Offset (brightness) -!TP_RETINEX_GAINTRANSMISSION;Gain transmission -!TP_RETINEX_GAINTRANSMISSION_TOOLTIP;Amplify or reduce the transmission map to achieve the desired luminance.\nThe x-axis is the transmission.\nThe y-axis is the gain. -!TP_RETINEX_MAP;Method -!TP_SHARPENING_BLUR;Blur radius -!TP_SHARPENING_CONTRAST;Contrast threshold -!TP_SHARPENING_ITERCHECK;Auto limit iterations -!TP_SHARPENING_RADIUS_BOOST;Corner radius boost -!TP_SHARPENMICRO_CONTRAST;Contrast threshold -!TP_SOFTLIGHT_LABEL;Soft Light -!TP_SOFTLIGHT_STRENGTH;Strength -!TP_TM_FATTAL_AMOUNT;Amount -!TP_TM_FATTAL_ANCHOR;Anchor -!TP_TM_FATTAL_LABEL;Dynamic Range Compression -!TP_TM_FATTAL_THRESHOLD;Detail -!TP_WAVELET_CB_TOOLTIP;For strong values product color-toning by combining it or not with levels decomposition 'toning'\nFor low values you can change the white balance of the background (sky, ...) without changing that of the front plane, generally more contrasted -!TP_WBALANCE_PICKER;Pick +ADJUSTER_RESET_TO_DEFAULT;Klik - terug naar standaardwaarde.\nCtrl+klik - terug naar laatst opgeslagen waarde. +CURVEEDITOR_CATMULLROM;Flexibel +DONT_SHOW_AGAIN;Dit bericht niet meer tonen +DYNPROFILEEDITOR_IMGTYPE_ANY;Alles +DYNPROFILEEDITOR_IMGTYPE_HDR;HDR +DYNPROFILEEDITOR_IMGTYPE_PS;Pixel Shift +DYNPROFILEEDITOR_IMGTYPE_STD;Standaard +EXIFFILTER_IMAGETYPE;Type afbeelding +EXIFPANEL_SHOWALL;Toon alles +FILEBROWSER_BROWSEPATHBUTTONHINT;Klik om de opgegeven map te laden, en het zoekfilter opnieuw toe te passen. +FILEBROWSER_CACHECLEARFROMFULL;Wis alles inclusief opgeslagen profielen +FILEBROWSER_CACHECLEARFROMPARTIAL;Wis alles behalve opgeslagen profielen +FILEBROWSER_DELETEDIALOG_ALL;Alle %1 bestanden in de prullenbak definitief verwijderen? +FILEBROWSER_DELETEDIALOG_SELECTED;Geselecteerde %1 bestanden definitief verwijderen? +FILEBROWSER_DELETEDIALOG_SELECTEDINCLPROC;Geselecteerde %1 bestanden inclusief een versie die door de verwerkingsrij is gemaakt verwijderen? +FILEBROWSER_EMPTYTRASHHINT;Alle bestanden in de prullenbak permanent verwijderen +FILEBROWSER_POPUPREMOVE;Permanent verwijderen +FILEBROWSER_POPUPREMOVEINCLPROC;Verwijder definitief, inclusief met uitvoer in de verwerkingsrij +FILEBROWSER_SHOWNOTTRASHHINT;Toon alleen niet-verwijderde afbeeldingen. +GENERAL_CURRENT;Huidig +GENERAL_HELP;Help +GENERAL_RESET;Terugzetten +GENERAL_SAVE_AS;Bewaren als... +GENERAL_SLIDER;Schuifregelaar +GIMP_PLUGIN_INFO;Welkom bij de RawTherapee GIMP plug-in!\nAls uw bewerking gereed is, sluit dan het hoofdvenster van RawTherapee en uw afbeelding wordt automatisch in GIMP geladen. +HISTOGRAM_TOOLTIP_MODE;Wissel tussen lineair, log-lineair and log-log schalen van het histogram. +HISTORY_MSG_173;NR - Detailbehoud +HISTORY_MSG_203;NR - Kleurruimte +HISTORY_MSG_235;B&W - CM - Auto +HISTORY_MSG_237;B&W - CM +HISTORY_MSG_256;NR - Mediaan - Type +HISTORY_MSG_273;CT - Kleurbalans SMH +HISTORY_MSG_297;NR - Modus +HISTORY_MSG_392;W - Overblijvend - Kleurbalans +HISTORY_MSG_441;Retinex - Toename transmissie +HISTORY_MSG_475;PS - Kanaalbalans +HISTORY_MSG_476;CAM02 - Temp uit +HISTORY_MSG_477;CAM02 - Groen uit +HISTORY_MSG_478;CAM02 - Yb uit +HISTORY_MSG_479;CAM02 - CAT02 aanpassing uit +HISTORY_MSG_480;CAM02 - Automatische CAT02 uit +HISTORY_MSG_481;CAM02 - Temp scène +HISTORY_MSG_482;CAM02 - Groen scène +HISTORY_MSG_483;CAM02 - Yb scène +HISTORY_MSG_484;CAM02 - Auto Yb scène +HISTORY_MSG_485;Lenscorrectie +HISTORY_MSG_486;Lenscorrectie - Camera +HISTORY_MSG_487;Lenscorrectie - Lens +HISTORY_MSG_488;Compressie Dynamisch Bereik +HISTORY_MSG_489;DRC - Detail +HISTORY_MSG_490;DRC - Hoeveelheid +HISTORY_MSG_491;Witbalans +HISTORY_MSG_492;RGB Curven +HISTORY_MSG_493;L*a*b* Adjustments +HISTORY_MSG_494;verscherpen +HISTORY_MSG_CLAMPOOG;Kleuren afkappen die buiten het gamma vallen +HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Kleurcorrectie +HISTORY_MSG_COLORTONING_LABREGION_AB;CT - Kleurcorrectie +HISTORY_MSG_COLORTONING_LABREGION_CHANNEL;CT - Kanaal +HISTORY_MSG_COLORTONING_LABREGION_CHROMATICITYMASK;CT - gebied C masker +HISTORY_MSG_COLORTONING_LABREGION_HUEMASK;CT - H masker +HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESS;CT - Licht +HISTORY_MSG_COLORTONING_LABREGION_LIGHTNESSMASK;CT - L masker +HISTORY_MSG_COLORTONING_LABREGION_LIST;CT - Lijst +HISTORY_MSG_COLORTONING_LABREGION_MASKBLUR;CT - verzachtingsmasker gebied +HISTORY_MSG_COLORTONING_LABREGION_OFFSET;CT - offset gebied +HISTORY_MSG_COLORTONING_LABREGION_POWER;CT - sterkte gebied +HISTORY_MSG_COLORTONING_LABREGION_SATURATION;CT - Verzadiging +HISTORY_MSG_COLORTONING_LABREGION_SHOWMASK;CT - toon gebiedsmasker +HISTORY_MSG_COLORTONING_LABREGION_SLOPE;CT - hellingsgebied +HISTORY_MSG_DEHAZE_DEPTH;Nevelvermindering - Diepte +HISTORY_MSG_DEHAZE_ENABLED;Nevelvermindering +HISTORY_MSG_DEHAZE_LUMINANCE;Nevelvermindering - Alleen Luminantie +HISTORY_MSG_DEHAZE_SHOW_DEPTH_MAP;Nevelvermindering - Toon dieptemap +HISTORY_MSG_DEHAZE_STRENGTH;Nevelvermindering - Sterkte +HISTORY_MSG_DUALDEMOSAIC_AUTO_CONTRAST;Dual-demozaïek - auto-drempel +HISTORY_MSG_DUALDEMOSAIC_CONTRAST;Dual-demozaïek - Contrastdrempel +HISTORY_MSG_FILMNEGATIVE_ENABLED;Filmnegatief +HISTORY_MSG_FILMNEGATIVE_VALUES;Filmnegatief waarden +HISTORY_MSG_HISTMATCHING;Auto-matched tooncurve +HISTORY_MSG_ICM_OUTPUT_PRIMARIES;Uitvoer - Primaries +HISTORY_MSG_ICM_OUTPUT_TEMP;Uitvoer - ICC-v4 illuminant D +HISTORY_MSG_ICM_OUTPUT_TYPE;Uitvoer - Type +HISTORY_MSG_ICM_WORKING_GAMMA;Working - Gamma +HISTORY_MSG_ICM_WORKING_SLOPE;Working - Helling +HISTORY_MSG_ICM_WORKING_TRC_METHOD;Working - TRC methode +HISTORY_MSG_LOCALCONTRAST_AMOUNT;Local Contrast - Hoeveelheid +HISTORY_MSG_LOCALCONTRAST_DARKNESS;Local Contrast - Donker +HISTORY_MSG_LOCALCONTRAST_ENABLED;Lokaal Contrast +HISTORY_MSG_LOCALCONTRAST_LIGHTNESS;Lokaal Contrast - Licht +HISTORY_MSG_LOCALCONTRAST_RADIUS;Lokaal Contrast - Radius +HISTORY_MSG_METADATA_MODE;Metadata kopieermodus +HISTORY_MSG_MICROCONTRAST_CONTRAST;Microcontrast - Contrastdrempel +HISTORY_MSG_PDSHARPEN_AUTO_CONTRAST;CS - Auto drempel +HISTORY_MSG_PDSHARPEN_AUTO_RADIUS;CS - Auto radius +HISTORY_MSG_PDSHARPEN_CHECKITER;CS - Auto limiet herhalingen +HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrastdrempel +HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Herhalingen +HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius +HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Toename hoekradius +HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demozaïekmethode voor beweging +HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;lijnruisfilter richting +HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lijnfilter +HISTORY_MSG_PRSHARPEN_CONTRAST;PRS - Contrastdrempel +HISTORY_MSG_RAWCACORR_AUTOIT;Raw CA Correctie - Herhalingen +HISTORY_MSG_RAWCACORR_COLORSHIFT;Raw CA Correctie - Vermijd kleurverschuiving +HISTORY_MSG_RAW_BORDER;Raw rand +HISTORY_MSG_RESIZE_ALLOWUPSCALING;Schalen - sta vergroting toe +HISTORY_MSG_SHARPENING_BLUR;Verscherpen - Vervagingsradius +HISTORY_MSG_SHARPENING_CONTRAST;Verscherpen - Contrastdrempel +HISTORY_MSG_SH_COLORSPACE;S/H - Kleurruimte +HISTORY_MSG_SOFTLIGHT_ENABLED;Zacht licht +HISTORY_MSG_SOFTLIGHT_STRENGTH;Zacht licht - Sterkte +HISTORY_MSG_TM_FATTAL_ANCHOR;DRC - Anker +HISTORY_MSG_TRANS_Method;Geometrie - Methode +ICCPROFCREATOR_COPYRIGHT;Copyright: +ICCPROFCREATOR_COPYRIGHT_RESET_TOOLTIP;Zet terug naar standaard copyright, verleend aan "RawTherapee, CC0" +ICCPROFCREATOR_CUSTOM;Handmatig +ICCPROFCREATOR_DESCRIPTION;Beschriiving: +ICCPROFCREATOR_DESCRIPTION_ADDPARAM;Voeg gamma- en hellingwaarden toe aan de beschrijving +ICCPROFCREATOR_DESCRIPTION_TOOLTIP;Laat leeg voor de standaard beschrijving. +ICCPROFCREATOR_GAMMA;Gamma +ICCPROFCREATOR_ICCVERSION;ICC versie: +ICCPROFCREATOR_ILL;Illuminant: +ICCPROFCREATOR_ILL_41;D41 +ICCPROFCREATOR_ILL_50;D50 +ICCPROFCREATOR_ILL_55;D55 +ICCPROFCREATOR_ILL_60;D60 +ICCPROFCREATOR_ILL_65;D65 +ICCPROFCREATOR_ILL_80;D80 +ICCPROFCREATOR_ILL_DEF;Standaard +ICCPROFCREATOR_ILL_INC;StdA 2856K +ICCPROFCREATOR_ILL_TOOLTIP;U kunt alleen de illuminant instellen voor ICC v4-profielen. +ICCPROFCREATOR_PRIMARIES;Primaire kleuren: +ICCPROFCREATOR_PRIM_ACESP0;ACES AP0 +ICCPROFCREATOR_PRIM_ACESP1;ACES AP1 +ICCPROFCREATOR_PRIM_ADOBE;Adobe RGB (1998) +ICCPROFCREATOR_PRIM_BEST;BestRGB +ICCPROFCREATOR_PRIM_BETA;BetaRGB +ICCPROFCREATOR_PRIM_BLUX;Blauw X +ICCPROFCREATOR_PRIM_BLUY;Blauw Y +ICCPROFCREATOR_PRIM_BRUCE;BruceRGB +ICCPROFCREATOR_PRIM_GREX;Groen X +ICCPROFCREATOR_PRIM_GREY;Groen Y +ICCPROFCREATOR_PRIM_PROPH;Prophoto +ICCPROFCREATOR_PRIM_REC2020;Rec2020 +ICCPROFCREATOR_PRIM_REDX;Rood X +ICCPROFCREATOR_PRIM_REDY;Rood Y +ICCPROFCREATOR_PRIM_SRGB;sRGB +ICCPROFCREATOR_PRIM_TOOLTIP;U kunt alleen aangepaste primaries voor ICC v4-profielen instellen. +ICCPROFCREATOR_PRIM_WIDEG;Widegamut +ICCPROFCREATOR_PROF_V2;ICC v2 +ICCPROFCREATOR_PROF_V4;ICC v4 +ICCPROFCREATOR_SAVEDIALOG_TITLE;Bewaar ICC profiel als... +ICCPROFCREATOR_SLOPE;Helling +ICCPROFCREATOR_TRC_PRESET;Toonresponscurve: +MAIN_BUTTON_ICCPROFCREATOR;ICC Profielmaker +MAIN_FRAME_PLACES_DEL;Verwijderen +MAIN_MSG_TOOMANYOPENEDITORS;Teveel open fotobewerkers.\nSluit er een om verder te kunnen. +MAIN_TAB_ADVANCED;Geavanceerd +MAIN_TAB_ADVANCED_TOOLTIP;Sneltoets: Alt-a +MAIN_TAB_FAVORITES;Favorieten +MAIN_TAB_FAVORITES_TOOLTIP;Sneltoets: Alt-u +MAIN_TOOLTIP_BACKCOLOR3;Achtergrondkleur van het voorbeeld: middelgrijs\nSneltoets: 9 +MAIN_TOOLTIP_PREVIEWSHARPMASK;Bekijk het scherptecontrastmasker.\nSneltoets: p\nWerkt alleen als verscherping is geactiveerd en het zoomniveau >= 100%. +OPTIONS_BUNDLED_MISSING;Het gebundelde profiel "%1" werd niet gevonden!\n\nUw installatie kan beschadigd zijn.\n\nDaarom worden interne standaardwaarden gebruikt. +OPTIONS_DEFIMG_MISSING;Het standaardprofiel voor niet-raw- foto's werd niet gevonden of is niet ingesteld.\n\nControleer de profielenmap, het kan ontbreken of beschadigd zijn.\n\n"%1" wordt daarom gebruikt. +OPTIONS_DEFRAW_MISSING;Het standaardprofiel voor raw-foto's werd niet gevonden of is niet ingesteld.\n\nControleer de profielenmap, het kan ontbreken of beschadigd zijn.\n\n"%1" wordt daarom gebruikt. +PARTIALPASTE_ADVANCEDGROUP;Geavanceerd +PARTIALPASTE_DEHAZE;Nevel verminderen +PARTIALPASTE_FILMNEGATIVE;Film Negatief +PARTIALPASTE_LOCALCONTRAST;Lokaal contrast +PARTIALPASTE_METADATA;Metadata modus +PARTIALPASTE_PREPROCESS_PDAFLINESFILTER;PDAF lijnfilter +PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT;CA vermijd kleurverschuiving +PARTIALPASTE_RAW_BORDER;Raw rand +PARTIALPASTE_SOFTLIGHT;Zacht licht +PARTIALPASTE_TM_FATTAL;Compressie dynamisch bereik +PREFERENCES_APPEARANCE;Uiterlijk +PREFERENCES_APPEARANCE_COLORPICKERFONT;Lettertype kleurenkiezer +PREFERENCES_APPEARANCE_CROPMASKCOLOR;Kleur bijsnijdmasker +PREFERENCES_APPEARANCE_MAINFONT;Standaard lettertype +PREFERENCES_APPEARANCE_PSEUDOHIDPI;Pseudo-HiDPI modus +PREFERENCES_APPEARANCE_THEME;Thema +PREFERENCES_AUTOSAVE_TP_OPEN;Bewaar positie gereedschappen (open/dicht) bij afsluiten +PREFERENCES_CACHECLEAR;Wissen +PREFERENCES_CACHECLEAR_ALL;Wis alle bestanden in de cache: +PREFERENCES_CACHECLEAR_ALLBUTPROFILES;Wis alle bestanden in de cache behalve verwerkingsprofielen: +PREFERENCES_CACHECLEAR_ONLYPROFILES;Wis alleen verwerkingsprofielen in de cache: +PREFERENCES_CACHECLEAR_SAFETY;Alleen bestanden in de cache worden gewist. Verwerkingsprofielen van de oorspronkelijke afbeeldingen blijven ongemoeid. +PREFERENCES_CHUNKSIZES;Tegels per thread +PREFERENCES_CHUNKSIZE_RAW_AMAZE;AMaZE demosaïek +PREFERENCES_CHUNKSIZE_RAW_CA;Raw CA correctie +PREFERENCES_CHUNKSIZE_RAW_RCD;RCD demosaïek +PREFERENCES_CHUNKSIZE_RAW_XT;Xtrans demosaïek +PREFERENCES_CHUNKSIZE_RGB;RGB verwerking +PREFERENCES_CROP;Uitsnijden +PREFERENCES_CROP_AUTO_FIT;Automatisch zoomen tot de uitsnede +PREFERENCES_CROP_GUIDES;Getoonde hulplijnen als uitsnede niet bewerkt wordt +PREFERENCES_CROP_GUIDES_FRAME;Frame +PREFERENCES_CROP_GUIDES_FULL;Origineel +PREFERENCES_CROP_GUIDES_NONE;Geen +PREFERENCES_DIRECTORIES;Mappen +PREFERENCES_EDITORCMDLINE;Aangepaste opdrachtregel +PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Compacte gereedschapsbalken in bestandsnavigator +PREFERENCES_LANG;Taal +PREFERENCES_PERFORMANCE_MEASURE;Meting +PREFERENCES_PERFORMANCE_MEASURE_HINT;Log verwerkingstijden in de console +PREFERENCES_PERFORMANCE_THREADS;Threads +PREFERENCES_PERFORMANCE_THREADS_LABEL;Maximaal aantal threads voor ruisvermindering and Wavelet Niveaus (0 = Automatisch) +PREFERENCES_PROFILESAVEBOTH;Bewaar verwerkingsprofielen zowel in de cache als naast het invoerbestand +PREFERENCES_PROFILESAVELOCATION;Opslaglocatie profielen +PREFERENCES_SAVE_TP_OPEN_NOW;Bewaar open/dicht-status van de gereedschappen nu +PREFERENCES_TAB_PERFORMANCE;Performantie +PREFERENCES_THUMBNAIL_INSPECTOR_JPEG;Ingesloten JPEG voorbeeld +PREFERENCES_THUMBNAIL_INSPECTOR_MODE;Te tonen foto +PREFERENCES_THUMBNAIL_INSPECTOR_RAW;Neutrale raw rendering +PREFERENCES_THUMBNAIL_INSPECTOR_RAW_IF_NO_JPEG_FULLSIZE;Ingesloten JPEG indien vol formaat, anders neutrale raw +PROGRESSBAR_DECODING;Decoderen... +PROGRESSBAR_GREENEQUIL;Groen blancering... +PROGRESSBAR_HLREC;Reconstructie hoge lichten... +PROGRESSBAR_HOTDEADPIXELFILTER;Hot/dead pixel filter... +PROGRESSBAR_LINEDENOISE;Lijnruis filter... +PROGRESSBAR_RAWCACORR;Raw CA correctie... +QINFO_FRAMECOUNT;%2 frames +QINFO_HDR;HDR / %2 frame(s) +QINFO_PIXELSHIFT;Pixel Shift / %2 frame(s) +QUEUE_LOCATION_TITLE;Uitvoerlocatie +QUEUE_STARTSTOP_TOOLTIP;;Start of stop de verwerking van foto's in de rij.\n\nSneltoets: Ctrl+s +SAMPLEFORMAT_0;onbekend data formaat +SAMPLEFORMAT_1;8-bit unsigned +SAMPLEFORMAT_2;16-bit unsigned +SAMPLEFORMAT_4;24-bit LogLuv +SAMPLEFORMAT_8;32-bit LogLuv +SAMPLEFORMAT_16;16-bit drijvendekomma +SAMPLEFORMAT_32;24-bit drijvendekomma +SAMPLEFORMAT_64;32-bit drijvendekomma +SAVEDLG_FILEFORMAT_FLOAT; drijvendekomma +SOFTPROOF_GAMUTCHECK_TOOLTIP;Markeer pixels waarvan de kleuren buiten het kleurgamma vallen, relatief aan:\n- het printerprofiel, indien opgegeven en soft-proofing is ingeschakeld,\n- het uitvoerprofiel, indien geen printerprofiel is gekozen en soft-proofing is ingeschakeld,\n- het beeldschermprofiel, indien soft-proofing is uitgeschakeld. +SOFTPROOF_TOOLTIP;Soft-proofing simuleert hoe een foto wordt getoond:\n- als deze wordt afgedrukt, indien een printerprofiel is opgegeven in Voorkeuren > Kleurbeheer,\n- als de foto getoond wordt op een beeldscherm dat het huidige uitvoerprofiel gebruikt en een printerprofiel niet is opgegeven. +TP_BWMIX_MIXC;Kanaal Mixer +TP_BWMIX_NEUTRAL;Terugzetten +TP_COLORAPP_ABSOLUTELUMINANCE;Absolute luminantie +TP_COLORAPP_CAT02ADAPTATION_TOOLTIP;Bij manuele aanpassing worden waardon boven 65 aanbevolen. +TP_COLORAPP_FREE;Vrije temp+groen + CAT02 + [uitvoer] +TP_COLORAPP_MEANLUMINANCE;Gemiddelde luminantie (Yb%) +TP_COLORAPP_NEUTRAL;Terugzetten +TP_COLORAPP_NEUTRAL_TIP;Zet alle regelaars, vinkjes en curves terug naar hun standaardwaarde +TP_COLORAPP_TEMP_TOOLTIP;Zet altijd Tint=1 om een lichtbron te selecteren.\n\nA temp=2856\nD50 temp=5003\nD55 temp=5503\nD65 temp=6504\nD75 temp=7504 +TP_COLORTONING_LABGRID;L*a*b* kleurcorrectie raster +TP_COLORTONING_LABGRID_VALUES;HL: a=%1 b=%2\nS: a=%3 b=%4 +TP_COLORTONING_LABREGIONS;Kleurcorrectie gebieden +TP_COLORTONING_LABREGION_ABVALUES;a=%1 b=%2 +TP_COLORTONING_LABREGION_CHANNEL;Kanaal +TP_COLORTONING_LABREGION_CHANNEL_ALL;Alle +TP_COLORTONING_LABREGION_CHANNEL_B;Blauw +TP_COLORTONING_LABREGION_CHANNEL_G;Groen +TP_COLORTONING_LABREGION_CHANNEL_R;Rood +TP_COLORTONING_LABREGION_CHROMATICITYMASK;C +TP_COLORTONING_LABREGION_HUEMASK;H +TP_COLORTONING_LABREGION_LIGHTNESS;Helderheid(L) +TP_COLORTONING_LABREGION_LIGHTNESSMASK;L +TP_COLORTONING_LABREGION_LIST_TITLE;Correctie +TP_COLORTONING_LABREGION_MASK;Masker +TP_COLORTONING_LABREGION_MASKBLUR;Verzachtingsmasker +TP_COLORTONING_LABREGION_OFFSET;Offset +TP_COLORTONING_LABREGION_POWER;Kracht +TP_COLORTONING_LABREGION_SATURATION;Verzadiging +TP_COLORTONING_LABREGION_SHOWMASK;Toon masker +TP_COLORTONING_LABREGION_SLOPE;Helling +TP_CROP_PPI;PPI +TP_CROP_RESETCROP;Terugzetten +TP_CROP_SELECTCROP;Selecteer +TP_DEHAZE_DEPTH;Diepte +TP_DEHAZE_LABEL;Nevel vermindering +TP_DEHAZE_LUMINANCE;Luminantie alleen +TP_DEHAZE_SHOW_DEPTH_MAP;Toon de dieptemap +TP_DEHAZE_STRENGTH;Sterkte +TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Verhoog (vermenigvuldig) de waarde van alle chrominantie regelaars.\nDeze curve regelt de sterkte van de chromatische ruisvermindering als een functie van de chromaticiteit, om bijvoorbeeld het effect te vergroten in gebieden met lage verzadiging en te verminderen in deze met lage verzadiging. +TP_DIRPYRDENOISE_LABEL;Ruisvermindering +TP_EXPOSURE_CLAMPOOG;Knip kleuren die buiten het gamma vallen +TP_EXPOSURE_HISTMATCHING;Automatische Tooncurve +TP_EXPOSURE_HISTMATCHING_TOOLTIP;Pas automatisch de curves en schuifregelaars aan (behalve belichtingscompensatie) om overeen te komen met de ingesloten JPEG miniatuur. +TP_FILMNEGATIVE_BLUE;Blauw verhouding +TP_FILMNEGATIVE_GREEN;Referentie exponent (contrast) +TP_FILMNEGATIVE_GUESS_TOOLTIP;Zet automatisch de rood/groen verhouding door 2 gebieden te kiezen met een neutrale tint (geen kleur) in het origineel. De gebieden moeten verschillen in helderheid. Zet de witbalans nadien. +TP_FILMNEGATIVE_LABEL;Film Negatief +TP_FILMNEGATIVE_PICK;Kies neutrale punten +TP_FILMNEGATIVE_RED;Rood verhouding +TP_ICM_WORKING_TRC;Tooncurve: +TP_ICM_WORKING_TRC_CUSTOM;Gebruiker gedefinieerd +TP_ICM_WORKING_TRC_GAMMA;Gamma +TP_ICM_WORKING_TRC_NONE;Geen +TP_ICM_WORKING_TRC_SLOPE;Helling +TP_ICM_WORKING_TRC_TOOLTIP;Enkel voor ingebouwde profielen. +TP_LENSGEOM_LIN;Lineair +TP_LENSGEOM_LOG;Logarithmisch +TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatische selectie +TP_LENSPROFILE_CORRECTION_LCPFILE;LCP bestand +TP_LENSPROFILE_CORRECTION_MANUAL;Manuele selectie +TP_LENSPROFILE_LENS_WARNING;Waarschuwing: de gebruikte lens profiel crop factor komt niet overeen met de camera crop factor, de resultaten kunnen verkeerd zijn. +TP_LENSPROFILE_MODE_HEADER;Lens Profiel +TP_LENSPROFILE_USE_CA;Chromatische afwijking +TP_LENSPROFILE_USE_GEOMETRIC;Geometrische vervorming +TP_LENSPROFILE_USE_HEADER;Lenscorrecties +TP_LENSPROFILE_USE_VIGNETTING;Vignettering +TP_LOCALCONTRAST_AMOUNT;Hoeveelheid +TP_LOCALCONTRAST_DARKNESS;Donker niveau +TP_LOCALCONTRAST_LABEL;Lokaal Contrast +TP_LOCALCONTRAST_LIGHTNESS;helderheidsniveau +TP_LOCALCONTRAST_RADIUS;Straal +TP_METADATA_EDIT;Pas wijzigingen toe +TP_METADATA_MODE;Metadata kopieermodus +TP_METADATA_STRIP;Strip alle metadata +TP_METADATA_TUNNEL;Kopieer ongewijzigd +TP_PDSHARPENING_LABEL;Verscherpen +TP_PREPROCESS_LINEDENOISE_DIRECTION;Richting +TP_PREPROCESS_LINEDENOISE_DIRECTION_BOTH;Beide +TP_PREPROCESS_LINEDENOISE_DIRECTION_HORIZONTAL;Horizontaal +TP_PREPROCESS_LINEDENOISE_DIRECTION_PDAF_LINES;Horizontaal enkel op PDAF-rijen +TP_PREPROCESS_LINEDENOISE_DIRECTION_VERTICAL;Verticaal +TP_PREPROCESS_PDAFLINESFILTER;PDAF lijnfilter +TP_RAWCACORR_AUTOIT;Herhalingen +TP_RAWCACORR_AUTOIT_TOOLTIP;Deze schuif is alleen actief als Automatische CA-correctie is aangevinkt.\nAuto-correctie werkt conservatief en corrigeert meestal niet alle chromatische aberratie.\nOm de resterende CA te corrigeren, kunt u dit proces tot vijf keer herhalen.\nElke herhaling vermindert de CA van de vorige herhaling, maar gaat wel ten koste van extra rekentijd. +TP_RAWCACORR_AVOIDCOLORSHIFT;Vermijd kleurverschuiving +TP_RAW_2PASS;1-pass+snel +TP_RAW_4PASS;3-pass+snel +TP_RAW_AMAZEVNG4;AMaZE+VNG4 +TP_RAW_BORDER;Rand +TP_RAW_DCBVNG4;DCB+VNG4 +TP_RAW_DUALDEMOSAICAUTOCONTRAST;Auto drempel +TP_RAW_DUALDEMOSAICAUTOCONTRAST_TOOLTIP;Als checkbox is aangevinkt (aanbevolen), berekent RT een optimale waarde gebaseerd op vlakke gebieden in de foto.\nIndien die niet gevonden worden of de foto bevat veel ruis, wordt de waarde op 0 gezet.\nOm de waarde handmatig in te voeren moet u eerst de checkbox uitvinken (redelijke waarden zijn afhankelijk van het soort foto). +TP_RAW_DUALDEMOSAICCONTRAST;Contrast drempel +TP_RAW_IMAGENUM_SN;SN modus +TP_RAW_IMAGENUM_TOOLTIP;Sommige raw bestanden bestaan uit verschillende sub-afbeeldingen (Pentax/Sony Pixel Shift, Pentax 3-in-1 HDR, Canon Dual Pixel, Fuji EXR).\n\Als een andere demozaïek methode dan Pixel Shift gebruikt wordt, selecteert dit de gebruikte sub-afbeelding.\n\nBij gebruik van de Pixel Shift demozaïek methode op een Pixel Shift raw, worden alle sub-afbeeldingen gebruikt, and dit selecteert de subafbeeldijg die gebruikt wordt voor bewegende moving gebieden. +TP_RAW_PIXELSHIFTDMETHOD;Demozaïek voor beweging +TP_RAW_PIXELSHIFTEPERISO;Gevoeligheid +TP_RAW_PIXELSHIFTEPERISO_TOOLTIP;De standaardwaarde 0 werkt goed voor lage ISO-waarden.\nHogere waarden vergroten de gevoeligheid van bewegingsdetectie.\nWijzig in kleine stappen en controleer het bewegingsmasker.\nVerhoog gevoeligheid voor onderbelichte foto's of foto's met hoge ISO-waarden. +TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL;Balanceer per kanaal +TP_RAW_PIXELSHIFTEQUALBRIGHTCHANNEL_TOOLTIP;Ingeschakeld: Balanceer elk RGB kanaal afzonderlijk.\nUitgeschakeld: Balanceer alle kanalen evenveel. +TP_RAW_PIXELSHIFTSHOWMOTION_TOOLTIP;Toont de foto met een groen masker dat de bewegingsgebieden toont. +TP_RAW_RCD;RCD +TP_RAW_RCDVNG4;RCD+VNG4 +TP_RAW_XTRANS;X-Trans +TP_RAW_XTRANSFAST;Snelle X-Trans +TP_RESIZE_ALLOW_UPSCALING;Sta opschalen toe +TP_RETINEX_CONTEDIT_MAP;Equalizer +TP_RETINEX_GAINOFFS;Versterking en Offset (helderheid) +TP_RETINEX_GAINTRANSMISSION;Transmissie versterking +TP_RETINEX_GAINTRANSMISSION_TOOLTIP;Versterk of verzwak de transmssiemap om de gewenste luminantie te bekomen.\nThe x-as is the transmissie.\nThe y-as is the versterking. +TP_RETINEX_MAP;Methode +TP_SHARPENING_BLUR;Vervagen straal +TP_SHARPENING_CONTRAST;Contrast drempel +TP_SHARPENING_ITERCHECK;Automatische limiet herhalingen +TP_SHARPENING_RADIUS_BOOST;Straalvergroting +TP_SHARPENMICRO_CONTRAST;Contrast drempel +TP_SOFTLIGHT_LABEL;Zacht licht +TP_SOFTLIGHT_STRENGTH;Sterkte +TP_TM_FATTAL_AMOUNT;Hoeveelheid +TP_TM_FATTAL_ANCHOR;Anker +TP_TM_FATTAL_LABEL;Dynamisch bereik compressie +TP_TM_FATTAL_THRESHOLD;Detail +TP_WAVELET_CB_TOOLTIP;Voor hoge waarden: kleurcorrectie door al of niet te combineren met niveau decompositie 'toning'\nVoor lage waarden de witbalans van de achtergrond (hemel, ...) wijzigen zonder die van de voorgrond, meestal meer contrastrijk +TP_WBALANCE_PICKER;Kies diff --git a/rtdata/languages/default b/rtdata/languages/default index b30e7dd32..e910d359a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -22,7 +22,7 @@ CURVEEDITOR_EDITPOINT_HINT;Enable edition of node in/out values.\n\nRight-click CURVEEDITOR_HIGHLIGHTS;Highlights CURVEEDITOR_LIGHTS;Lights CURVEEDITOR_LINEAR;Linear -CURVEEDITOR_LOADDLGLABEL;Load curve... +CURVEEDITOR_LOADDLGLABEL;Load curve CURVEEDITOR_MINMAXCPOINTS;Equalizer CURVEEDITOR_NURBS;Control cage CURVEEDITOR_PARAMETRIC;Parametric @@ -694,21 +694,37 @@ HISTORY_MSG_441;Retinex - Gain transmission HISTORY_MSG_442;Retinex - Scale HISTORY_MSG_443;Output black point compensation HISTORY_MSG_444;WB - Temp bias -HISTORY_MSG_445;Raw sub-image -HISTORY_MSG_449;PS - ISO adaption -HISTORY_MSG_452;PS - Show motion -HISTORY_MSG_453;PS - Show mask only -HISTORY_MSG_457;PS - Check red/blue -HISTORY_MSG_462;PS - Check green -HISTORY_MSG_464;PS - Blur motion mask -HISTORY_MSG_465;PS - Blur radius -HISTORY_MSG_468;PS - Fill holes -HISTORY_MSG_469;PS - Median -HISTORY_MSG_471;PS - Motion correction -HISTORY_MSG_472;PS - Smooth transitions -HISTORY_MSG_473;PS - Use LMMSE -HISTORY_MSG_474;PS - Equalize -HISTORY_MSG_475;PS - Equalize channel +HISTORY_MSG_445;Raw Sub-Image +HISTORY_MSG_446;EvPixelShiftMotion +HISTORY_MSG_447;EvPixelShiftMotionCorrection +HISTORY_MSG_448;EvPixelShiftStddevFactorGreen +HISTORY_MSG_449;PS ISO adaption +HISTORY_MSG_450;EvPixelShiftNreadIso +HISTORY_MSG_451;EvPixelShiftPrnu +HISTORY_MSG_452;PS Show motion +HISTORY_MSG_453;PS Show mask only +HISTORY_MSG_454;EvPixelShiftAutomatic +HISTORY_MSG_455;EvPixelShiftNonGreenHorizontal +HISTORY_MSG_456;EvPixelShiftNonGreenVertical +HISTORY_MSG_457;PS Check red/blue +HISTORY_MSG_458;EvPixelShiftStddevFactorRed +HISTORY_MSG_459;EvPixelShiftStddevFactorBlue +HISTORY_MSG_460;EvPixelShiftGreenAmaze +HISTORY_MSG_461;EvPixelShiftNonGreenAmaze +HISTORY_MSG_462;PS Check green +HISTORY_MSG_463;EvPixelShiftRedBlueWeight +HISTORY_MSG_464;PS Blur motion mask +HISTORY_MSG_465;PS Blur radius +HISTORY_MSG_466;EvPixelShiftSum +HISTORY_MSG_467;EvPixelShiftExp0 +HISTORY_MSG_468;PS Fill holes +HISTORY_MSG_469;PS Median +HISTORY_MSG_470;EvPixelShiftMedian3 +HISTORY_MSG_471;PS Motion correction +HISTORY_MSG_472;PS Smooth transitions +HISTORY_MSG_473;PS Use lmmse +HISTORY_MSG_474;PS Equalize +HISTORY_MSG_475;PS Equalize channel HISTORY_MSG_476;CAM02 - Temp out HISTORY_MSG_477;CAM02 - Green out HISTORY_MSG_478;CAM02 - Yb out @@ -726,8 +742,429 @@ HISTORY_MSG_489;DRC - Detail HISTORY_MSG_490;DRC - Amount HISTORY_MSG_491;White Balance HISTORY_MSG_492;RGB Curves -HISTORY_MSG_493;L*a*b* Adjustments +HISTORY_MSG_493;Local Adjustments HISTORY_MSG_494;Capture Sharpening +HISTORY_MSG_496;Local Spot deleted +HISTORY_MSG_497;Local Spot selected +HISTORY_MSG_498;Local Spot name +HISTORY_MSG_499;Local Spot visibility +HISTORY_MSG_500;Local Spot shape +HISTORY_MSG_501;Local Spot method +HISTORY_MSG_502;Local Spot shape method +HISTORY_MSG_503;Local Spot locX +HISTORY_MSG_504;Local Spot locXL +HISTORY_MSG_505;Local Spot locY +HISTORY_MSG_506;Local Spot locYT +HISTORY_MSG_507;Local Spot center +HISTORY_MSG_508;Local Spot circrad +HISTORY_MSG_509;Local Spot quality method +HISTORY_MSG_510;Local Spot transition +HISTORY_MSG_511;Local Spot thresh +HISTORY_MSG_512;Local Spot ΔE -weakening +HISTORY_MSG_513;Local Spot scope +HISTORY_MSG_514;Local Spot structure +HISTORY_MSG_515;Local Adjustments +HISTORY_MSG_516;Local - Color and light +HISTORY_MSG_517;Local - Enable super +HISTORY_MSG_518;Local - Lightness +HISTORY_MSG_519;Local - Contrast +HISTORY_MSG_520;Local - Chrominance +HISTORY_MSG_521;Local - Scope +HISTORY_MSG_522;Local - curve method +HISTORY_MSG_523;Local - LL Curve +HISTORY_MSG_524;Local - CC curve +HISTORY_MSG_525;Local - LH Curve +HISTORY_MSG_526;Local - H curve +HISTORY_MSG_527;Local - Color Inverse +HISTORY_MSG_528;Local - Exposure +HISTORY_MSG_529;Local - Exp Compensation +HISTORY_MSG_530;Local - Exp Hlcompr +HISTORY_MSG_531;Local - Exp hlcomprthresh +HISTORY_MSG_532;Local - Exp black +HISTORY_MSG_533;Local - Exp Shcompr +HISTORY_MSG_534;Local - Warm Cool +HISTORY_MSG_535;Local - Exp Scope +HISTORY_MSG_536;Local - Exp Contrast curve +HISTORY_MSG_537;Local - Vibrance +HISTORY_MSG_538;Local - Vib Saturated +HISTORY_MSG_539;Local - Vib Pastel +HISTORY_MSG_540;Local - Vib Threshold +HISTORY_MSG_541;Local - Vib Protect skin tones +HISTORY_MSG_542;Local - Vib avoid colorshift +HISTORY_MSG_543;Local - Vib link +HISTORY_MSG_544;Local - Vib Scope +HISTORY_MSG_545;Local - Vib H curve +HISTORY_MSG_546;Local - Blur and noise +HISTORY_MSG_547;Local - Radius +HISTORY_MSG_548;Local - Noise +HISTORY_MSG_549;Local - Blur scope +HISTORY_MSG_550;Local - Blur method +HISTORY_MSG_551;Local - Blur Luminance only +HISTORY_MSG_552;Local - Tone mapping +HISTORY_MSG_553;Local - TM compression strength +HISTORY_MSG_554;Local - TM gamma +HISTORY_MSG_555;Local - TM edge stopping +HISTORY_MSG_556;Local - TM scale +HISTORY_MSG_557;Local - TM Reweighting +HISTORY_MSG_558;Local - TM scope +HISTORY_MSG_559;Local - Retinex +HISTORY_MSG_560;Local - Retinex method +HISTORY_MSG_561;Local - Retinex strength +HISTORY_MSG_562;Local - Retinex chroma +HISTORY_MSG_563;Local - Retinex radius +HISTORY_MSG_564;Local - Retinex contrast +HISTORY_MSG_565;Local - scope +HISTORY_MSG_566;Local - Retinex Gain curve +HISTORY_MSG_567;Local - Retinex Inverse +HISTORY_MSG_568;Local - Sharpening +HISTORY_MSG_569;Local - Sh Radius +HISTORY_MSG_570;Local - Sh Amount +HISTORY_MSG_571;Local - Sh Damping +HISTORY_MSG_572;Local - Sh Iterations +HISTORY_MSG_573;Local - Sh Scope +HISTORY_MSG_574;Local - Sh Inverse +HISTORY_MSG_575;Local - CBDL +HISTORY_MSG_576;Local - cbdl mult +HISTORY_MSG_577;Local - cbdl chroma +HISTORY_MSG_578;Local - cbdl threshold +HISTORY_MSG_579;Local - cbdl scope +HISTORY_MSG_580;Local - Denoise +HISTORY_MSG_581;Local - deNoise lum f 1 +HISTORY_MSG_582;Local - deNoise lum c +HISTORY_MSG_583;Local - deNoise lum detail +HISTORY_MSG_584;Local - deNoise Equalizer white-black +HISTORY_MSG_585;Local - deNoise chro f +HISTORY_MSG_586;Local - deNoise chro c +HISTORY_MSG_587;Local - deNoise chro detail +HISTORY_MSG_588;Local - deNoise Equalizer blue-red +HISTORY_MSG_589;Local - deNoise bilateral +HISTORY_MSG_590;Local - deNoise Scope +HISTORY_MSG_591;Local - Avoid color shift +HISTORY_MSG_592;Local - Sh Contrast +HISTORY_MSG_593;Local - Local contrast +HISTORY_MSG_594;Local - Local contrast radius +HISTORY_MSG_595;Local - Local contrast amount +HISTORY_MSG_596;Local - Local contrast darkness +HISTORY_MSG_597;Local - Local contrast lightness +HISTORY_MSG_598;Local - Local contrast scope +HISTORY_MSG_599;Local - Retinex dehaze +HISTORY_MSG_600;Local - Soft Light enable +HISTORY_MSG_601;Local - Soft Light strength +HISTORY_MSG_602;Local - Soft Light scope +HISTORY_MSG_603;Local - Sh Blur radius +HISTORY_MSG_605;Local - Mask preview choice +HISTORY_MSG_606;Local Spot selected +HISTORY_MSG_607;Local - Color Mask C +HISTORY_MSG_608;Local - Color Mask L +HISTORY_MSG_609;Local - Exp Mask C +HISTORY_MSG_610;Local - Exp Mask L +HISTORY_MSG_611;Local - Color Mask H +HISTORY_MSG_612;Local - Color Structure +HISTORY_MSG_613;Local - Exp Structure +HISTORY_MSG_614;Local - Exp Mask H +HISTORY_MSG_615;Local - Blend color +HISTORY_MSG_616;Local - Blend Exp +HISTORY_MSG_617;Local - Blur Exp +HISTORY_MSG_618;Local - Use Color Mask +HISTORY_MSG_619;Local - Use Exp Mask +HISTORY_MSG_620;Local - Blur col +HISTORY_MSG_621;Local - Exp inverse +HISTORY_MSG_622;Local - Exclude structure +HISTORY_MSG_623;Local - Exp Chroma compensation +HISTORY_MSG_624;Local - Color correction grid +HISTORY_MSG_625;Local - Color correction strength +HISTORY_MSG_626;Local - Color correction Method +HISTORY_MSG_627;Local - Shadow Highlight +HISTORY_MSG_628;Local - SH Highlight +HISTORY_MSG_629;Local - SH H tonalwidth +HISTORY_MSG_630;Local - SH Shadows +HISTORY_MSG_631;Local - SH S tonalwidth +HISTORY_MSG_632;Local - SH radius +HISTORY_MSG_633;Local - SH Scope +HISTORY_MSG_634;Local - radius color +HISTORY_MSG_635;Local - radius Exp +HISTORY_MSG_636;Local - Tool added +HISTORY_MSG_637;Local - SH Mask C +HISTORY_MSG_638;Local - SH Mask L +HISTORY_MSG_639;Local - SH Mask H +HISTORY_MSG_640;Local - SH blend +HISTORY_MSG_641;Local - Use SH mask +HISTORY_MSG_642;Local - radius SH +HISTORY_MSG_643;Local - Blur SH +HISTORY_MSG_644;Local - inverse SH +HISTORY_MSG_645;Local - balance ΔE ab-L +HISTORY_MSG_646;Local - Exp mask chroma +HISTORY_MSG_647;Local - Exp mask gamma +HISTORY_MSG_648;Local - Exp mask slope +HISTORY_MSG_649;Local - Exp soft radius +HISTORY_MSG_650;Local - Color mask chroma +HISTORY_MSG_651;Local - Color mask gamma +HISTORY_MSG_652;Local - Color mask slope +HISTORY_MSG_653;Local - SH mask chroma +HISTORY_MSG_654;Local - SH mask gamma +HISTORY_MSG_655;Local - SH mask slope +HISTORY_MSG_656;Local - Color soft radius +HISTORY_MSG_657;Local - Retinex Reduce artifacts +HISTORY_MSG_658;Local - CBDL soft radius +HISTORY_MSG_659;Local Spot transition-weakening +HISTORY_MSG_660;Local - cbdl clarity +HISTORY_MSG_661;Local - cbdl contrast residual +HISTORY_MSG_662;Local - deNoise lum f 0 +HISTORY_MSG_663;Local - deNoise lum f 2 +HISTORY_MSG_664;Local - cbdl Blur +HISTORY_MSG_665;Local - cbdl mask Blend +HISTORY_MSG_666;Local - cbdl mask radius +HISTORY_MSG_667;Local - cbdl mask chroma +HISTORY_MSG_668;Local - cbdl mask gamma +HISTORY_MSG_669;Local - cbdl mask slope +HISTORY_MSG_670;Local - cbdl mask C +HISTORY_MSG_671;Local - cbdl mask L +HISTORY_MSG_672;Local - cbdl mask CL +HISTORY_MSG_673;Local - Use cbdl mask +HISTORY_MSG_674;Local - Tool removed +HISTORY_MSG_675;Local - TM soft radius +HISTORY_MSG_676;Local Spot transition-differentiation +HISTORY_MSG_677;Local - TM amount +HISTORY_MSG_678;Local - TM saturation +HISTORY_MSG_679;Local - Retinex mask C +HISTORY_MSG_680;Local - Retinex mask L +HISTORY_MSG_681;Local - Retinex mask CL +HISTORY_MSG_682;Local - Retinex mask +HISTORY_MSG_683;Local - Retinex mask Blend +HISTORY_MSG_684;Local - Retinex mask radius +HISTORY_MSG_685;Local - Retinex mask chroma +HISTORY_MSG_686;Local - Retinex mask gamma +HISTORY_MSG_687;Local - Retinex mask slope +HISTORY_MSG_688;Local - Tool removed +HISTORY_MSG_689;Local - Retinex mask transmission map +HISTORY_MSG_690;Local - Retinex scale +HISTORY_MSG_691;Local - Retinex darkness +HISTORY_MSG_692;Local - Retinex lightness +HISTORY_MSG_693;Local - Retinex threshold +HISTORY_MSG_694;Local - Retinex Laplacian threshold +HISTORY_MSG_695;Local - Soft method +HISTORY_MSG_696;Local - Retinex Normalize +HISTORY_MSG_697;Local - TM Normalize +HISTORY_MSG_698;Local - Local contrast Fast Fourier +HISTORY_MSG_699;Local - Retinex Fast Fourier +HISTORY_MSG_701;Local - Exp Shadows +HISTORY_MSG_702;Local - Exp Method +HISTORY_MSG_703;Local - Exp Laplacian threshold +HISTORY_MSG_704;Local - Exp PDE balance +HISTORY_MSG_705;Local - Exp linearity +HISTORY_MSG_706;Local - TM mask C +HISTORY_MSG_707;Local - TM mask L +HISTORY_MSG_708;Local - TM mask CL +HISTORY_MSG_709;Local - use TM mask +HISTORY_MSG_710;Local - TM mask Blend +HISTORY_MSG_711;Local - TM mask radius +HISTORY_MSG_712;Local - TM mask chroma +HISTORY_MSG_713;Local - TM mask gamma +HISTORY_MSG_714;Local - TM mask slope +HISTORY_MSG_716;Local - Local method +HISTORY_MSG_717;Local - Local contrast +HISTORY_MSG_718;Local - Local contrast levels +HISTORY_MSG_719;Local - Local contrast residual L +HISTORY_MSG_720;Local - Blur mask C +HISTORY_MSG_721;Local - Blur mask L +HISTORY_MSG_722;Local - Blur mask CL +HISTORY_MSG_723;Local - use Blur mask +HISTORY_MSG_725;Local - Blur mask Blend +HISTORY_MSG_726;Local - Blur mask radius +HISTORY_MSG_727;Local - Blur mask chroma +HISTORY_MSG_728;Local - Blur mask gamma +HISTORY_MSG_729;Local - Blur mask slope +HISTORY_MSG_730;Local - Blur method +HISTORY_MSG_731;Local - median method +HISTORY_MSG_732;Local - median iterations +HISTORY_MSG_733;Local - soft radius +HISTORY_MSG_734;Local - detail +HISTORY_MSG_738;Local - Local contrast Merge L +HISTORY_MSG_739;Local - Local contrast Soft radius +HISTORY_MSG_740;Local - Local contrast Merge C +HISTORY_MSG_741;Local - Local contrast Residual C +HISTORY_MSG_742;Local - Exp Laplacian gamma +HISTORY_MSG_743;Local - Exp Fattal Amount +HISTORY_MSG_744;Local - Exp Fattal Detail +HISTORY_MSG_745;Local - Exp Fattal Offset +HISTORY_MSG_746;Local - Exp Fattal Sigma +HISTORY_MSG_747;Local Spot created +HISTORY_MSG_748;Local - Exp Denoise +HISTORY_MSG_749;Local - Reti Depth +HISTORY_MSG_750;Local - Reti Mode log - lin +HISTORY_MSG_751;Local - Reti Dehaze luminance +HISTORY_MSG_752;Local - Reti Offset +HISTORY_MSG_753;Local - Reti Transmission map +HISTORY_MSG_754;Local - Reti Clip +HISTORY_MSG_755;Local - TM use tm mask +HISTORY_MSG_756;Local - Exp use algo exposure mask +HISTORY_MSG_757;Local - Exp Laplacian mask +HISTORY_MSG_758;Local - Reti Laplacian mask +HISTORY_MSG_759;Local - Exp Laplacian mask +HISTORY_MSG_760;Local - Color Laplacian mask +HISTORY_MSG_761;Local - SH Laplacian mask +HISTORY_MSG_762;Local - cbdl Laplacian mask +HISTORY_MSG_763;Local - Blur Laplacian mask +HISTORY_MSG_764;Local - Solve PDE Laplacian mask +HISTORY_MSG_765;Local - deNoise Detail threshold +HISTORY_MSG_766;Local - Blur Fast Fourier +HISTORY_MSG_767;Local - Grain Iso +HISTORY_MSG_768;Local - Grain Strength +HISTORY_MSG_769;Local - Grain Scale +HISTORY_MSG_770;Local - Color Mask contrast curve +HISTORY_MSG_771;Local - Exp Mask contrast curve +HISTORY_MSG_772;Local - SH Mask contrast curve +HISTORY_MSG_773;Local - TM Mask contrast curve +HISTORY_MSG_774;Local - Reti Mask contrast curve +HISTORY_MSG_775;Local - CBDL Mask contrast curve +HISTORY_MSG_776;Local - Blur Denoise Mask contrast curve +HISTORY_MSG_777;Local - Blur Mask local contrast curve +HISTORY_MSG_778;Local - Mask highlights +HISTORY_MSG_779;Local - Color Mask local contrast curve +HISTORY_MSG_780;Local - Color Mask shadows +HISTORY_MSG_781;Local - Contrast Mask Wavelet level +HISTORY_MSG_782;Local - Blur Denoise Mask Wavelet levels +HISTORY_MSG_783;Local - Color Wavelet levels +HISTORY_MSG_784;Local - Mask ΔE +HISTORY_MSG_785;Local - Mask Scope ΔE +HISTORY_MSG_786;Local - SH method +HISTORY_MSG_787;Local - Equalizer multiplier +HISTORY_MSG_788;Local - Equalizer detail +HISTORY_MSG_789;Local - SH mask amount +HISTORY_MSG_790;Local - SH mask anchor +HISTORY_MSG_791;Local - Mask Short L curves +HISTORY_MSG_792;Local - Mask Luminance Background +HISTORY_MSG_793;Local - SH TRC gamma +HISTORY_MSG_794;Local - SH TRC slope +HISTORY_MSG_795;Local - Mask save restore image +HISTORY_MSG_796;Local - Recursive references +HISTORY_MSG_797;Local - Merge Original method +HISTORY_MSG_798;Local - Opacity +HISTORY_MSG_799;Local - Color RGB ToneCurve +HISTORY_MSG_800;Local - Color ToneCurve Method +HISTORY_MSG_801;Local - Color ToneCurve Special +HISTORY_MSG_802;Local - Contrast threshold +HISTORY_MSG_803;Local - Color Merge +HISTORY_MSG_804;Local - Color mask Structure +HISTORY_MSG_805;Local - Blur Noise mask Structure +HISTORY_MSG_806;Local - Color mask Structure as tool +HISTORY_MSG_807;Local - Blur Noise mask Structure as tool +HISTORY_MSG_808;Local - Color mask curve H(H) +HISTORY_MSG_809;Local - Vib mask curve C(C) +HISTORY_MSG_810;Local - Vib mask curve L(L) +HISTORY_MSG_811;Local - Vib mask curve LC(H) +HISTORY_MSG_813;Local - Use Vib mask +HISTORY_MSG_814;Local - Vib mask Blend +HISTORY_MSG_815;Local - Vib mask radius +HISTORY_MSG_816;Local - Vib mask chroma +HISTORY_MSG_817;Local - Vib mask gamma +HISTORY_MSG_818;Local - Vib mask slope +HISTORY_MSG_819;Local - Vib mask laplacian +HISTORY_MSG_820;Local - Vib mask contrast curve +HISTORY_MSG_821;Local - color grid background +HISTORY_MSG_822;Local - color background merge +HISTORY_MSG_823;Local - color background luminance +HISTORY_MSG_824;Local - Exp gradient mask strength +HISTORY_MSG_825;Local - Exp gradient mask angle +HISTORY_MSG_826;Local - Exp gradient strength +HISTORY_MSG_827;Local - Exp gradient angle +HISTORY_MSG_828;Local - SH gradient strength +HISTORY_MSG_829;Local - SH gradient angle +HISTORY_MSG_830;Local - Color gradient strength L +HISTORY_MSG_831;Local - Color gradient angle +HISTORY_MSG_832;Local - Color gradient strength C +HISTORY_MSG_833;Local - Gradient feather +HISTORY_MSG_834;Local - Color gradient strength H +HISTORY_MSG_835;Local - Vib gradient strength L +HISTORY_MSG_836;Local - Vib gradient angle +HISTORY_MSG_837;Local - Vib gradient strength C +HISTORY_MSG_838;Local - Vib gradient strength H +HISTORY_MSG_839;Local - Software complexity +HISTORY_MSG_840;Local - CL Curve +HISTORY_MSG_841;Local - LC curve +HISTORY_MSG_842;Local - Contrast Threshold Mask blur +HISTORY_MSG_843;Local - Radius Mask blur +HISTORY_MSG_844;Local - Color Mask fftw +HISTORY_MSG_845;Local - Encoding log +HISTORY_MSG_846;Local - Encoding auto +HISTORY_MSG_847;Local - Source Gray +HISTORY_MSG_848;Local - Source Gray auto +HISTORY_MSG_849;Local - Auto Grayon +HISTORY_MSG_850;Local - Black Ev +HISTORY_MSG_851;Local - White Ev +HISTORY_MSG_852;Local - Target Gray +HISTORY_MSG_853;Local - Local contrast +HISTORY_MSG_854;Local - Scope encoding log +HISTORY_MSG_855;Local - Entire image +HISTORY_MSG_856;Local - Base log +HISTORY_MSG_857;Local - Contrast Blur Residual +HISTORY_MSG_858;Local - Contrast Luminance only +HISTORY_MSG_859;Local - Contrast Maximum Blur levels +HISTORY_MSG_860;Local - Contrast Curve Blur levels +HISTORY_MSG_861;Local - Contrast Curve Contrast levels +HISTORY_MSG_862;Local - Contrast Sigma luminance +HISTORY_MSG_863;Local - Contrast Merge Original +HISTORY_MSG_864;Local - Directional sigma +HISTORY_MSG_865;Local - Directional delta +HISTORY_MSG_866;Local - Contrast Curve Compression +HISTORY_MSG_867;Local - Contrast Amount residual +HISTORY_MSG_868;Local - balance ΔE C-H +HISTORY_MSG_869;Local - denoise curve luminance +HISTORY_MSG_870;Local - LC mask curve LC(H) +HISTORY_MSG_871;Local - LC mask curve C(C) +HISTORY_MSG_872;Local - LC mask curve L(L) +HISTORY_MSG_873;Local - LC mask enable +HISTORY_MSG_875;Local - LC mask blend +HISTORY_MSG_876;Local - LC mask radius +HISTORY_MSG_877;Local - LC mask chroma +HISTORY_MSG_878;Local - LC mask curve contrast +HISTORY_MSG_879;Local - LC Chroma levels +HISTORY_MSG_880;Local - LC Chroma blur levels +HISTORY_MSG_881;Local - Contrast Offset Luminance +HISTORY_MSG_882;Local - Contrast Blur +HISTORY_MSG_883;Local - Contrast By Levels +HISTORY_MSG_884;Local - Contrast Dynamic Range Laplacian +HISTORY_MSG_885;Local - Contrast Dynamic Range Wavelet +HISTORY_MSG_886;Local - Contrast Wavelet Curve Compression +HISTORY_MSG_887;Local - Contrast Wavelet Compression Residual +HISTORY_MSG_888;Local - Contrast Wavelet Balance Threshold +HISTORY_MSG_889;Local - Contrast Wavelet Graduated Strength +HISTORY_MSG_890;Local - Contrast Wavelet Graduated angle +HISTORY_MSG_891;Local - Contrast Wavelet Graduated +HISTORY_MSG_892;Local - Encoding log Graduated Strength +HISTORY_MSG_893;Local - Encoding log Graduated angle +HISTORY_MSG_894;Local - Color Preview dE +HISTORY_MSG_897;Local - Contrast Wavelet ES strength +HISTORY_MSG_898;Local - Contrast Wavelet ES radius +HISTORY_MSG_899;Local - Contrast Wavelet ES detail +HISTORY_MSG_900;Local - Contrast Wavelet ES gradient +HISTORY_MSG_901;Local - Contrast Wavelet ES threshold low +HISTORY_MSG_902;Local - Contrast Wavelet ES threshold high +HISTORY_MSG_903;Local - Contrast Wavelet ES local contrast +HISTORY_MSG_904;Local - Contrast Wavelet ES first level +HISTORY_MSG_905;Local - Contrast Wavelet Edge Sharpness +HISTORY_MSG_906;Local - Contrast Wavelet ES sensitivity +HISTORY_MSG_907;Local - Contrast Wavelet ES amplification +HISTORY_MSG_908;Local - Contrast Wavelet ES neighboring +HISTORY_MSG_909;Local - Contrast Wavelet ES show +HISTORY_MSG_910;Local - Wavelet Edge performance +HISTORY_MSG_911;Local - Blur Chroma Luma +HISTORY_MSG_912;Local - Blur Guide filter strength +HISTORY_MSG_913;Local - Contrast Wavelet Sigma DR +HISTORY_MSG_914;Local - Blur Wavelet Sigma BL +HISTORY_MSG_915;Local - Edge Wavelet Sigma ED +HISTORY_MSG_916;Local - Residual wavelet shadows +HISTORY_MSG_917;Local - Residual wavelet shadows threshold +HISTORY_MSG_918;Local - Residual wavelet highlights +HISTORY_MSG_919;Local - Residual wavelet highlights threshold +HISTORY_MSG_920;Local - Wavelet sigma LC +HISTORY_MSG_921;Local - Wavelet Graduated sigma LC2 +HISTORY_MSG_922;Local - changes In Black and White +HISTORY_MSG_923;Local - Tool complexity mode +HISTORY_MSG_924;Local - Tool complexity mode +HISTORY_MSG_925;Local - Scope color tools +HISTORY_MSG_926;Local - Show mask type +HISTORY_MSG_927;Local - Shadow mask HISTORY_MSG_CAT02PRESET;Cat02 automatic preset HISTORY_MSG_CLAMPOOG;Clip out-of-gamut colors HISTORY_MSG_COLORTONING_LABGRID_VALUE;CT - Color correction @@ -776,6 +1213,13 @@ HISTORY_MSG_PDSHARPEN_CONTRAST;CS - Contrast threshold HISTORY_MSG_PDSHARPEN_ITERATIONS;CS - Iterations HISTORY_MSG_PDSHARPEN_RADIUS;CS - Radius HISTORY_MSG_PDSHARPEN_RADIUS_BOOST;CS - Corner radius boost +HISTORY_MSG_PERSP_CAM_ANGLE;Perspective - Camera +HISTORY_MSG_PERSP_CAM_FL;Perspective - Camera +HISTORY_MSG_PERSP_CAM_SHIFT;Perspective - Camera +HISTORY_MSG_PERSP_METHOD;Perspective - Method +HISTORY_MSG_PERSP_PROJ_ANGLE;Perspective - Recovery +HISTORY_MSG_PERSP_PROJ_ROTATE;Perspective - PCA rotation +HISTORY_MSG_PERSP_PROJ_SHIFT;Perspective - PCA HISTORY_MSG_PIXELSHIFT_DEMOSAIC;PS - Demosaic method for motion HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION;Line noise filter direction HISTORY_MSG_PREPROCESS_PDAFLINESFILTER;PDAF lines filter @@ -804,12 +1248,12 @@ HISTORY_MSG_WAVMERGEL;Merge L HISTORY_MSG_WAVRADIUS;Radius Shadows-Highlight HISTORY_MSG_WAVSCALE;Scale HISTORY_MSG_WAVSHOWMASK;Show wavelet mask -HISTORY_MSG_WAVSIGMA;Damper +HISTORY_MSG_WAVSIGMA;Attenuation Response HISTORY_MSG_WAVSOFTRAD;Soft radius clarity HISTORY_MSG_WAVSOFTRADEND;Soft radius final HISTORY_MSG_WAVUSHAMET;Clarity method HISTORY_MSG_THRESWAV;Balance threshold -HISTORY_MSG_BLUWAV;Damper +HISTORY_MSG_BLUWAV;Attenuation Response HISTORY_MSG_WAVOLDSH;Old algorithm HISTORY_MSG_WAVOFFSET;Offset HISTORY_MSG_WAVLOWTHR;Threshold low contrast @@ -817,11 +1261,11 @@ HISTORY_MSG_BLSHAPE;Blur by level HISTORY_MSG_WAVBL;Blur levels HISTORY_MSG_BLURWAV;Blur luminance HISTORY_MSG_BLURCWAV;Blur chroma -HISTORY_MSG_EDGEFFECT;Edge Damper -HISTORY_MSG_SIGMAFIN;Final contrast Damper -HISTORY_MSG_SIGMATON;Toning Damper -HISTORY_MSG_SIGMACOL;Chroma Damper -HISTORY_MSG_SIGMADIR;Dir Damper +HISTORY_MSG_EDGEFFECT;Edge Attenuation Response +HISTORY_MSG_SIGMAFIN;Final contrast Attenuation Response +HISTORY_MSG_SIGMATON;Toning Attenuation Response +HISTORY_MSG_SIGMACOL;Chroma Attenuation Response +HISTORY_MSG_SIGMADIR;Dir Attenuation Response HISTORY_MSG_RANGEAB;Range ab HISTORY_MSG_PROTAB;Protection HISTORY_NEWSNAPSHOT;Add @@ -963,6 +1407,8 @@ MAIN_TAB_FAVORITES_TOOLTIP;Shortcut: Alt-u MAIN_TAB_FILTER; Filter MAIN_TAB_INSPECT; Inspect MAIN_TAB_IPTC;IPTC +MAIN_TAB_LOCALLAB;Local +MAIN_TAB_LOCALLAB_TOOLTIP;Shortcut: Alt-o MAIN_TAB_METADATA;Metadata MAIN_TAB_METADATA_TOOLTIP;Shortcut: Alt-m MAIN_TAB_RAW;Raw @@ -1005,6 +1451,7 @@ NAVIGATOR_XY_NA;x: --, y: -- OPTIONS_BUNDLED_MISSING;The bundled profile "%1" could not be found!\n\nYour installation could be damaged.\n\nDefault internal values will be used instead. OPTIONS_DEFIMG_MISSING;The default profile for non-raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead. OPTIONS_DEFRAW_MISSING;The default profile for raw photos could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\n"%1" will be used instead. +PARTIALPASTE_LOCALLABGROUP;Local Adjustments Settings PARTIALPASTE_ADVANCEDGROUP;Advanced Settings PARTIALPASTE_BASICGROUP;Basic Settings PARTIALPASTE_CACORRECTION;Chromatic aberration correction @@ -1045,6 +1492,8 @@ PARTIALPASTE_IMPULSEDENOISE;Impulse noise reduction PARTIALPASTE_IPTCINFO;IPTC PARTIALPASTE_LABCURVE;L*a*b* adjustments PARTIALPASTE_LENSGROUP;Lens Related Settings +PARTIALPASTE_LOCALLAB;Local Adjustments +PARTIALPASTE_LOCGROUP;Local PARTIALPASTE_LENSPROFILE;Profiled lens correction PARTIALPASTE_LOCALCONTRAST;Local contrast PARTIALPASTE_METADATA;Metadata mode @@ -1121,6 +1570,9 @@ PREFERENCES_CLUTSCACHE;HaldCLUT Cache PREFERENCES_CLUTSCACHE_LABEL;Maximum number of cached CLUTs PREFERENCES_CLUTSDIR;HaldCLUT directory PREFERENCES_CMMBPC;Black point compensation +PREFERENCES_COMPLEXITYLOC;Default complexity for Local Adjustments +PREFERENCES_COMPLEXITY_EXP;Expert +PREFERENCES_COMPLEXITY_NORM;Normal PREFERENCES_CROP;Crop Editing PREFERENCES_CROP_AUTO_FIT;Automatically zoom to fit the crop PREFERENCES_CROP_GUIDES;Guides shown when not editing the crop @@ -1235,8 +1687,9 @@ PREFERENCES_SERIALIZE_TIFF_READ_TOOLTIP;Enabling this option when working with f PREFERENCES_SET;Set PREFERENCES_SHOWBASICEXIF;Show basic Exif info PREFERENCES_SHOWDATETIME;Show date and time -PREFERENCES_SHOWEXPOSURECOMPENSATION;Append exposure compensation PREFERENCES_SHOWFILMSTRIPTOOLBAR;Show Filmstrip toolbar +PREFERENCES_SHOWEXPOSURECOMPENSATION;Append exposure compensation +PREFERENCES_SHOWTOOLTIP;Show Local Adjustments advice tooltips PREFERENCES_SHTHRESHOLD;Threshold for clipped shadows PREFERENCES_SINGLETAB;Single Editor Tab Mode PREFERENCES_SINGLETABVERTAB;Single Editor Tab Mode, Vertical Tabs @@ -1503,7 +1956,7 @@ TP_COLORAPP_TCMODE_LIGHTNESS;Lightness TP_COLORAPP_TCMODE_SATUR;Saturation TP_COLORAPP_TEMP_TOOLTIP;To select an illuminant, always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504 TP_COLORAPP_TEMP2_TOOLTIP;Either symmetrical mode temp = White balance.\nEither select illuminant always set Tint=1.\n\nA temp=2856\nD41 temp=4100\nD50 temp=5003\nD55 temp=5503\nD60 temp=6000\nD65 temp=6504\nD75 temp=7504 -TP_COLORAPP_TEMPOUT_TOOLTIP;Disable to chnage temperature and tint +TP_COLORAPP_TEMPOUT_TOOLTIP;Disable to change temperature and tint TP_COLORAPP_TONECIE;Tone mapping using CIECAM02 TP_COLORAPP_TONECIE_TOOLTIP;If this option is disabled, tone mapping is done in L*a*b* space.\nIf this option is enabled, tone mapping is done using CIECAM02.\nThe Tone Mapping tool must be enabled for this setting to take effect. TP_COLORAPP_VIEWING_ABSOLUTELUMINANCE_TOOLTIP;Absolute luminance of the viewing environment\n(usually 16 cd/m²). @@ -1842,6 +2295,562 @@ TP_LENSPROFILE_USE_CA;Chromatic aberration TP_LENSPROFILE_USE_GEOMETRIC;Geometric distortion TP_LENSPROFILE_USE_HEADER;Correct TP_LENSPROFILE_USE_VIGNETTING;Vignetting +TP_LOCALLAB_ACTIV;Luminance only +TP_LOCALLAB_ADJ;Equalizer Blue-yellow Red-green +TP_LOCALLAB_ALL;All rubrics +TP_LOCALLAB_AMOUNT;Amount +TP_LOCALLAB_ARTIF;Shape detection +TP_LOCALLAB_ARTIF_TOOLTIP;Threshold deltaE-scope increase the range of scope-deltaE - high values are for very wide gamut images.\nIncrease deltaE Weakening improve shape detection, but can reduce the scope of detection. +TP_LOCALLAB_AUTOGRAY;Automatic +TP_LOCALLAB_AVOID;Avoid color shift +TP_LOCALLAB_BALAN;Balance ΔE ab-L +TP_LOCALLAB_BALANH;Balance ΔE C-H +TP_LOCALLAB_BALAN_TOOLTIP;Change algorithm ΔE parameter.\nMore or less ab-L, more or less C - H.\nNot for Denoise +TP_LOCALLAB_BALANEXP;ΔØ PDE balance +TP_LOCALLAB_BASELOG;Logarithm base +TP_LOCALLAB_BILATERAL;Bilateral filter +TP_LOCALLAB_BLACK_EV;Black Ev +TP_LOCALLAB_BLENDMASKCOL;Blend +TP_LOCALLAB_BLENDMASK_TOOLTIP;If blend = 0 only shape detection is improved.\nIf blend > 0 the mask is added to the image. If blend < 0 the mask is subtracted from the image +TP_LOCALLAB_BLGUID;Guided Filter +TP_LOCALLAB_BLINV;Inverse +TP_LOCALLAB_BLCO;Chrominance only +TP_LOCALLAB_BLLO;Luminance only +TP_LOCALLAB_BLLC;Luminance & Chrominance +TP_LOCALLAB_BLMED;Median +TP_LOCALLAB_BLMETHOD_TOOLTIP;Normal - direct blur and noise with all settings.\nInverse blur and noise with all settings. Be careful some results may be curious +TP_LOCALLAB_BLNORM;Normal +TP_LOCALLAB_BLSYM;Symmetric +TP_LOCALLAB_BLWH;All changes forced in Black and White +TP_LOCALLAB_BLWH_TOOLTIP;Force color change composante "a" and "b" to zero.\nUsefull when the user chooses black and white processes, or film. +TP_LOCALLAB_SPOTNAME;New Spot +TP_LOCALLAB_BLUFR;Smooth - Blur - Grain - Denoise +TP_LOCALLAB_BLUMETHOD_TOOLTIP;To blur the background and isolate the foreground:\n*Blur the background by a RT-spot fully covering the image (high values for scope and transition) - normal or inverse.\n*Isolate the foreground by one or more excluding RT-spot with the tools you want (increse scope).\n\nThis module can be used in additional noise reduction,including "median" and "Guided filter" +TP_LOCALLAB_BLURMASK_TOOLTIP;Generate a blur mask, take into account the structure with the contrast threshold Mask Blur slider. +TP_LOCALLAB_BLUR;Gaussian Blur - Noise - Grain +TP_LOCALLAB_BLURCBDL;Blur levels 0-1-2-3-4 +TP_LOCALLAB_BLURCOL;Radius Mask Blur +TP_LOCALLAB_BLURDE;Blur Shape detection +TP_LOCALLAB_BLURLC;Luminance Only +TP_LOCALLAB_BLURLEVELFRA;Blur Levels +TP_LOCALLAB_BLURRESIDFRA;Blur Residual +TP_LOCALLAB_BUTTON_ADD;Add +TP_LOCALLAB_BUTTON_DEL;Delete +TP_LOCALLAB_BUTTON_DUPL;Duplicate +TP_LOCALLAB_BUTTON_REN;Rename +TP_LOCALLAB_BUTTON_VIS;Show/Hide +TP_LOCALLAB_CBDL;Contrast by detail levels - Defects +TP_LOCALLAB_CBDL_ADJ_TOOLTIP;Acts as a wavelet tools.\nThe first level (0) acts on 2x2 details.\nThe last level (5) acts on 64x64 details. +TP_LOCALLAB_CBDLCLARI_TOOLTIP;Takes the midtones and enhance them. +TP_LOCALLAB_CBDL_THRES_TOOLTIP;Prevent the sharpening of noise +TP_LOCALLAB_CENTER_X;Center X +TP_LOCALLAB_CENTER_Y;Center Y +TP_LOCALLAB_CH;Curves CL - LC +TP_LOCALLAB_CHROMA;Chrominance +TP_LOCALLAB_CHROMACBDL;Chroma +TP_LOCALLAB_CHROMACB_TOOLTIP;Acts as an amplifier-reducer action compare to sliders of luminance.\nUnder 100 reduce, above 100 amplifie +TP_LOCALLAB_CHROMALEV;Chroma levels +TP_LOCALLAB_CHROMABLU;Chroma levels +TP_LOCALLAB_CHROMABLU_TOOLTIP;Acts as an amplifier-reducer action compare to settings of luma.\nUnder 1 reduce, above 1 amplifie +TP_LOCALLAB_CHROMASKCOL;Chroma mask +TP_LOCALLAB_CHROMASK_TOOLTIP;You can use this slider to desaturated background (inverse mask - curve near 0).\nAlso to attenuate or enhance the action of a mask on the chroma +TP_LOCALLAB_CHRRT;Chroma +TP_LOCALLAB_CIRCRADIUS;Spot size +TP_LOCALLAB_CIRCRAD_TOOLTIP;Contains the references of RT-spot, useful for shape detection (hue, luma, chroma, Sobel).\nLow values may be useful for treating foliage.\nHigh values may be useful for treating skin +TP_LOCALLAB_CLARICRES;Merge Chroma +TP_LOCALLAB_CLARIFRA;Clarity & Sharp mask - Blend & Soft images +TP_LOCALLAB_CLARILRES;Merge Luma +TP_LOCALLAB_CLARISOFT;Soft radius +TP_LOCALLAB_CLARISOFT_TOOLTIP;Enabled for Clarity and Sharp mask if Merge Luma different from zero.\n\nEnabled for all wavelets pyramid modules.\nDisabled if Soft radius = 0 +TP_LOCALLAB_CLARITYML;Clarity +TP_LOCALLAB_CLARI_TOOLTIP;Under or equal level wavelet 4, 'Sharp mask' is enabled.\nAbove level wavelet 5 'Clarity' is enabled.\nUsefull if you use 'Level dynamic Range Compression' +TP_LOCALLAB_CLIPTM;Clip Restored datas (gain) +TP_LOCALLAB_COFR;Color & Light - Small defects +TP_LOCALLAB_COL_NAME;Name +TP_LOCALLAB_COL_VIS;Status +TP_LOCALLAB_COLORDE;Color preview selection ΔE - Intensity +TP_LOCALLAB_COLORDE_TOOLTIP;Show preview selection ΔE in blue if negative and in green if positive.\n\nMask and modifications (show modifications without mask): show real modifications if positive, show enhanced modifications (only luminance) with blue and yellow if negative. +TP_LOCALLAB_COLORDEPREV_TOOLTIP;Button Preview ΔE needs that only one tool is enabled (expander).\nTo be able to have an Preview ΔE with several enable tools use Mask and modifications - Preview ΔE +TP_LOCALLAB_COLORSCOPE;Scope Color Tools +TP_LOCALLAB_COLORSCOPE_TOOLTIP;Use a common Scope for Color and light, Exposure (Standard), Shadows highlight, Vibrance.\nOthers tools have their specific scope. +TP_LOCALLAB_COMPFRA;Levels Directional Contrast +TP_LOCALLAB_COMPREFRA;Levels Dynamic Wavelet Range (un)Compression +TP_LOCALLAB_COMPRESS_TOOLTIP;Use if necessary the module 'Clarity & Sharp mask and Blend & Soft Images' by adjusting 'Soft radius' to reduce artifacts. +TP_LOCALLAB_COMPFRAME_TOOLTIP;Allows special effects. You can reduce artifacts with 'Clarity & Sharp mask - Blend & Soft Images".\nUses a lot of resources +TP_LOCALLAB_COMPLEX_METHOD;Software Complexity +TP_LOCALLAB_COMPLEX_TOOLTIP; Allow user to select Local adjustments rubrics. +TP_LOCALLAB_CONTCOL;Contrast threshold Mask Blur +TP_LOCALLAB_CONTFRA;Contrast by Level +TP_LOCALLAB_CONTRAST;Contrast +TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP;Main mask contrast control. +TP_LOCALLAB_CONTRESID;Contrast +TP_LOCALLAB_CONTTHR;Contrast Threshold +TP_LOCALLAB_CSTHRESHOLD;Ψ Wavelets Levels +TP_LOCALLAB_CSTHRESHOLDBLUR;Ψ Mask Wavelet level +TP_LOCALLAB_CURV;Lightness - Contrast - Chrominance "Super" +TP_LOCALLAB_CURVCURR;Normal +TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP;If curves at the top, mask is completely black no transformation is made by the mask on the image.\nAs you go down the curve, the mask gradually more colorful and brilliant, the image is changing more and more.\n\nIt is recommended (not required) to position the top of the curves on the gray transition line which represents the references (chroma, luma, hue). +TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP;To be active, you must enable combobox 'Curves type' +TP_LOCALLAB_CURVEEDITOR_TONES_LABEL;Tone curve +TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP;L=f(L), can be used with L(H) in Color and Light +TP_LOCALLAB_CURVEMETHOD_TOOLTIP;'Normal', the curve L=f(L) has the same algorithm than slider lightness.\n'Super' the curve L=f(L) has an new improved algorithm, which can leeds in some cases to artifacts. +TP_LOCALLAB_CURVENCONTRAST;Super+Contrast threshold (experimental) +TP_LOCALLAB_CURVENH;Super +TP_LOCALLAB_CURVENHSU;Combined HueChroma (experimental) +TP_LOCALLAB_CURVENSOB2;Combined HueChroma + Contrast threshold (experimental) +TP_LOCALLAB_CURVNONE;Disable curves +TP_LOCALLAB_DARKRETI;Darkness +TP_LOCALLAB_DEHAFRA;Dehaze +TP_LOCALLAB_DEHAZ;Strength +TP_LOCALLAB_DEHAZ_TOOLTIP;Negative values adds haze +TP_LOCALLAB_DELTAD;Delta balance +TP_LOCALLAB_DELTAEC;Mask ΔE Image +TP_LOCALLAB_DENOIS;Ψ Denoise +TP_LOCALLAB_DENOI_TOOLTIP;This module can be used alone (at the end of process), or in complement of main denoise (at the beginning).\nScope allows to differentiate the action according to the color (deltaE).\nYou can complete the action with "median" or "Guided Filter" (Smooth Blur...).\nYou can complete the action with "Blur levels" "Wavelet pyramid" +TP_LOCALLAB_DEPTH;Depth +TP_LOCALLAB_DETAIL;Local contrast +TP_LOCALLAB_DETAILSH;Details +TP_LOCALLAB_DETAILTHR;Detail threshold Luminance Chroma (DCT ƒ) +TP_LOCALLAB_DUPLSPOTNAME;Copy +TP_LOCALLAB_EDGFRA;Edge Sharpness +TP_LOCALLAB_EDGSHOW;Show all tools +TP_LOCALLAB_ELI;Elipse +TP_LOCALLAB_ENABLE_AFTER_MASK;Use Tone Mapping +TP_LOCALLAB_ENABLE_MASK;Enable mask +TP_LOCALLAB_ENABLE_MASKAFT;Use all algorithms Exposure +TP_LOCALLAB_ENARETIMASKTMAP_TOOLTIP;If enabled Mask uses Restored Datas after Transmission Map instead of Original datas +TP_LOCALLAB_ENH;Enhanced +TP_LOCALLAB_ENHDEN;Enhanced + chroma denoise +TP_LOCALLAB_EPSBL;Detail +TP_LOCALLAB_EQUIL;Normalize Luminance +TP_LOCALLAB_EQUILTM_TOOLTIP;Reconstruct luminance in such a way that the mean and the variance of the output image is identical to that of the original. +TP_LOCALLAB_ESTOP;Edge stopping +TP_LOCALLAB_EV_DUPL;Copy of +TP_LOCALLAB_EV_NVIS;Hide +TP_LOCALLAB_EV_NVIS_ALL;Hide all +TP_LOCALLAB_EV_VIS;Show +TP_LOCALLAB_EV_VIS_ALL;Show all +TP_LOCALLAB_EXCLUF;Excluding +TP_LOCALLAB_EXCLUF_TOOLTIP;Can be used to exclude this part of datas - move Scope to extend color.\n You can apply all settings to this RT-spot. +TP_LOCALLAB_EXCLUTYPE;Spot method +TP_LOCALLAB_EXCLUTYPE_TOOLTIP;Normal spot use recursive data.\n\nExcluding spot reinitialize data to origin.\nCan be used to totally or partially cancel a previous action or to perform a inverse mode +TP_LOCALLAB_EXECLU;Excluding spot +TP_LOCALLAB_EXNORM;Normal spot +TP_LOCALLAB_EXPCBDL_TOOLTIP;In the case of contaminated sensor (type "grease"), and when the area is important or for a series of small defects.\n\na) Put the selection spot on a pronounced default (adapting its size if necessary), use a large spot enough to allow wavelet; b) choose a wide selection area to cover most of the area affected by the defects; c) Select a transition value (low) and transition weakening (high value); d) act on levels 2, 3, 4 or 5 or lower by reducing the contrast (values below 100) and by acting on the chroma slider if necessary. e)possibly act on "scope" to reduce the extent of the action.\n\nYou can also complete with Blur levels and Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCHROMA;Chroma compensation +TP_LOCALLAB_EXPCHROMA_TOOLTIP;Only in association with exposure compensation and PDE Ipol.\nAvoids desaturation of colors +TP_LOCALLAB_EXPCOLOR_TOOLTIP;In the case of small defects.\n\nRed-eyes : red-centered circular selector, spot delimiters close to the eye, weak scope, "lightness" -100, "chrominance" -100.\n\nSpotIR :Circular selector centered on the defect, spot delimiters close to the default - reduce "chrominance", possibly act on "scope" to reduce the extent of the action.\n\nDust - grease (small) :Circular selector centered on the defect (adapt the size of the spot), spot delimiters not too close to the defect to allow an inconspicuous transition. a) "Transition" (low values) and "Transition weak" (high values); b) act on "lightness" and possibly on "chrominance" or "Color correction grid - direct" to approach the rendering of the polluted zone to that of the healthy zone; c) act moderately on "scope" to modulate the desired action.\n\nYou can also complete with Gaussian blur (Smooth Blur and noise) +TP_LOCALLAB_EXPCONTRAST_TOOLTIP;Avoid spots that are too small(< 32x32 pixels).\nUse low transition values and high weakening transition values and scope to simulate small RT-spot and deal wth defects.\nUse if necessary the module 'Clarity & Sharp mask and Blend images' by adjusting 'Soft radius' to reduce artifacts. +TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP;See the documentation of wavelet levels.\nHowever there are some differences: more tools and closer to the details.\nEx: Tone mapping for wavelet levels. +TP_LOCALLAB_EXPCURV;Curves +TP_LOCALLAB_EXPGRAD;Graduated Filter +TP_LOCALLAB_EXPLAP_TOOLTIP;The more you act on this threshold slider, the greater the action of reducing contrast. +TP_LOCALLAB_EXPLAPBAL_TOOLTIP;Balances the action between the original image and the Laplace transform. +TP_LOCALLAB_EXPLAPLIN_TOOLTIP;Add linear exposure component before application Laplace transform +TP_LOCALLAB_EXPLAPGAMM_TOOLTIP;Apply a gamma before and after Laplace transform +TP_LOCALLAB_EXPMERGEFILE_TOOLTIP;Allows various possibilities to blend image (as layers in Photosshop) : difference, multiply, soft light, overlay...with opacity...\nOriginal Image : merge current RT-spot with Original.\nPrevious spot : merge current Rt-spot with previous - if there is only one spot previous = original.\nBackground : merge current RT-spot with a color and luminance background (less possibilties) +TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP;Apply a median before Laplace transform to prevent artifacts (noise).\nYou can also use "Denoise" tool. +TP_LOCALLAB_EXPMETHOD_TOOLTIP;Standard : use an algorithm similar as main Exposure but in L*a*b* and taking account of deltaE.\n\nLaplacian & PDE : use another algorithm also with deltaE and with Poisson equation to solve Laplacian in Fourier space.\nPDE IPOL, PDE Fattal and Standard can be combined.\nFFTW Fourier Transform is optimized in size to reduce processing time.\nPDE reduce artifacts and noise. +TP_LOCALLAB_EXPOSE;Exposure - PDE algorithms +TP_LOCALLAB_EXPOSURE_TOOLTIP;In some cases (strong shadows ..) you can use others modules "Shadows Highlights", "Tone equalizer", "TRC", "Encoding Log"... +TP_LOCALLAB_EXPRETITOOLS;Advanced Retinex Tools +TP_LOCALLAB_EXPSHARP_TOOLTIP;RT-Spot minimum 39*39.\nUse low transition values and high weakening transition values and scope to simulate small RT-spot. +TP_LOCALLAB_EXPTOOL;Tools exposure +TP_LOCALLAB_EXPTRC;Tone Response Curve - TRC +TP_LOCALLAB_FATAMOUNT;Amount +TP_LOCALLAB_FATANCHOR;Anchor +TP_LOCALLAB_FATANCHORA;Offset +TP_LOCALLAB_FATDETAIL;Detail +TP_LOCALLAB_FATFRA;Dynamic Range Compression ƒ +TP_LOCALLAB_FATFRAME_TOOLTIP;PDE Fattal - use Fattal Tone mapping algorithm. +TP_LOCALLAB_FATLEVEL;Sigma +TP_LOCALLAB_FATRES;Amount Residual Image +TP_LOCALLAB_FATSHFRA;Dynamic Range Compression Mask ƒ +TP_LOCALLAB_FEATH_TOOLTIP;Gradient width in percent of the Spot diagonal\nUsed by all Graduated filter in all tools.\nNo action if Graduated Filter are not used. +TP_LOCALLAB_FEATVALUE;Feather gradient (Graduated Filters) +TP_LOCALLAB_FFTCOL_MASK;FFTW ƒ +TP_LOCALLAB_FFTW;ƒ - Use Fast Fourier Transform +TP_LOCALLAB_FFTW2;ƒ - Use Fast Fourier Transform (TIF, JPG,..) +TP_LOCALLAB_FFTWBLUR;ƒ - Always Use Fast Fourier Transform +TP_LOCALLAB_FULLIMAGE;Calculate DarkEv - WhiteEv - Value on the entire image +TP_LOCALLAB_GAM;Gamma +TP_LOCALLAB_GAMFRA;Tone response curve (TRC) +TP_LOCALLAB_GAMM;Gamma +TP_LOCALLAB_GAMMASKCOL;Gamma mask +TP_LOCALLAB_GAMSH;Gamma +TP_LOCALLAB_GRADANG;Gradient angle +TP_LOCALLAB_GRADANG_TOOLTIP;Rotation angle in degrees : -180 0 +180 +TP_LOCALLAB_GRADFRA;Graduated Filter Mask +TP_LOCALLAB_GRADGEN_TOOLTIP;Graduated filter is supplied with Color and Light & merge file, Exposure & mask, Shadows Highlight, Vibrance, Encoding log.\n\nVibrance, Color and Light & merge file, are provided with GF luminance, chrominance, Hue.\nFeather is located in settings. +TP_LOCALLAB_GRADLOGFRA;Graduated Filter Luminance +TP_LOCALLAB_GRADSTR;Gradient strength +TP_LOCALLAB_GRADSTRAB_TOOLTIP;Filter chroma strength +TP_LOCALLAB_GRADSTRCHRO;Gradient strength Chrominance +TP_LOCALLAB_GRADSTRHUE2;Gradient strength Hue +TP_LOCALLAB_GRADSTRHUE;Gradient strength Hue +TP_LOCALLAB_GRADSTRHUE_TOOLTIP;Filter Hue strength +TP_LOCALLAB_GRADSTRLUM;Gradient strength Luminance +TP_LOCALLAB_GRADSTR_TOOLTIP;Filter strength in stops +TP_LOCALLAB_GRAINFRA;Film Grain 1:1 +TP_LOCALLAB_GRALWFRA;Graduated Filter Local contrast +TP_LOCALLAB_GRIDFRAME_TOOLTIP;You can use this tool as a brush. Use small spot and adapt transition and transition weakening\nOnly mode NORMAL and eventually Hue, Saturation, Color, Luminosity are concerned by Merge background (ΔE) +TP_LOCALLAB_GRIDONE;Color Toning +TP_LOCALLAB_GRIDTWO;Direct +TP_LOCALLAB_GUIDBL;Soft radius +TP_LOCALLAB_GUIDFILTER;Guided filter radius +TP_LOCALLAB_GUIDFILTER_TOOLTIP;Adapt this values according to images - can reduce or increase artifacts. +TP_LOCALLAB_HIGHMASKCOL;Highlights mask +TP_LOCALLAB_HHMASK_TOOLTIP;Fine hue adjustments for example for the skin. +TP_LOCALLAB_HLH;Curves H +TP_LOCALLAB_IND;Independent (mouse) +TP_LOCALLAB_INDSL;Independent (mouse + sliders) +TP_LOCALLAB_INVERS;Inverse +TP_LOCALLAB_INVERS_TOOLTIP;If selected (inverse) less possibilities.\n\nAlternative\nFirst Spot:\n full image - delimiter outside preview\n Shape RT-spot area : rectangle. Transition 100\n\nSecond spot : Excluding spot +TP_LOCALLAB_ISOGR;Coarseness (ISO) +TP_LOCALLAB_LABEL;Local Adjustments +TP_LOCALLAB_LABBLURM;Mask Blur +TP_LOCALLAB_LABGRID;Color correction grid +TP_LOCALLAB_LABGRIDMERG;Background +TP_LOCALLAB_LABGRID_VALUES;High(a)=%1 High(b)=%2\nLow(a)=%3 Low(b)=%4 +TP_LOCALLAB_LABSTRUM;Mask Structure +TP_LOCALLAB_LAPLACC;ΔØ Mask Laplacian solve PDE +TP_LOCALLAB_LAP_MASK_TOOLTIP;Solve PDE for all Laplacian masks.\nIf enabled Laplacian threshold mask reduce artifacts and smooth result.\nIf disabled linear response. +TP_LOCALLAB_LAPLACE;Δ - Laplacian threshold ΔE +TP_LOCALLAB_LAPLACEXP;∆ - Laplacian threshold +TP_LOCALLAB_LAPMASKCOL;∆ - Laplacian threshold mask +TP_LOCALLAB_LAPRAD_TOOLTIP;Avoid using Radius and Laplace Threshold simultaneously.\nLaplacian threshold reduce contrast, artifacts, smooth result (if PDE settings enabled). +TP_LOCALLAB_LC_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated.\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\n +TP_LOCALLAB_LEVELBLUR;Maximum Blur levels +TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP;On the abscissa local contrast (near concept luminance). On the ordinate, amplification or reduction local contrast. +TP_LOCALLAB_LEVELWAV;Ψ Wavelets Levels +TP_LOCALLAB_LEVELWAV_TOOLTIP;The Level is automatically adapted to the size of the spot and the preview.\nFrom level 9 size max 512 to level 1 size max = 4 +TP_LOCALLAB_LIGHTNESS;Lightness +TP_LOCALLAB_LIGHTN_TOOLTIP;In inverse mode: selection = -100 force luminance to zero +TP_LOCALLAB_LIGHTRETI;Lightness +TP_LOCALLAB_LINEAR;Linearity +TP_LOCALLAB_LMASK_LEVEL_TOOLTIP;Give priority to action on midtones and high lights and by choosing the concerned wavelet levels +TP_LOCALLAB_LMASK_LL_TOOLTIP;Give priority to action on midtones and high lights +TP_LOCALLAB_LOCCONT;Unsharp Mask +TP_LOCALLAB_LOC_CONTRAST;Local contrast -Wavelet - defects +TP_LOCALLAB_LOC_CONTRASTPYR;Ψ Pyramid 1: +TP_LOCALLAB_LOC_CONTRASTPYRLAB; Graduated Filter - Edge Sharpness - Blur +TP_LOCALLAB_LOC_CONTRASTPYR2;Ψ Pyramid 2: +TP_LOCALLAB_LOC_CONTRASTPYR2LAB; Contrast by Levels- Tone Mapping - Dir. Contrast +TP_LOCALLAB_LOC_RESIDPYR;Residual Image Main +TP_LOCALLAB_LOG;Encoding log +TP_LOCALLAB_LOGAUTO;Automatic +TP_LOCALLAB_LOGFRA;Source Gray Point +TP_LOCALLAB_LOGLIN;Logarithm mode +TP_LOCALLAB_LOGPFRA;Relative Exposure Levels +TP_LOCALLAB_LOGENCOD_TOOLTIP;Allows Tone Mapping with Logarithm encoding (ACES).\nUsefull for underexposed pictures, or with high dynamic range.\n\nTwo steps in the process : 1) Calculate Dynamic Range 2) User adaptation +TP_LOCALLAB_LOGFRAME_TOOLTIP;Calculate or use Exposure levels of the image early in the process:\n Black Ev, White Ev and Source Gray point.\n Take into account main exposure compensation. +TP_LOCALLAB_LOGAUTO_TOOLTIP;Pressing this button will bring an evaluation of dynamic range and Source Gray point (if "Automatic" Source gray enabled).\nTo be able to touch up the automatic values, press the button again +TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP;Estimated values of Dynamic Range - Black Ev and White Ev +TP_LOCALLAB_LOGSRCGREY_TOOLTIP;Estimated gray point value of the image, early in the process +TP_LOCALLAB_LOGTARGGREY_TOOLTIP;You can change this value to adapt it to your taste. +TP_LOCALLAB_LOGBASE_TOOLTIP;Default = 2.\nValues less than 2 reduce the action of the algorithm, the shadows are darker, the highlights are brighter.\nValues greater than 2 change the action of the algorithm, the shadows are grayer the highlights are more washed out +TP_LOCALLAB_LUM;Curves LL - CC +TP_LOCALLAB_LUMADARKEST;Darkest +TP_LOCALLAB_LUMASK;Luminance Background Mask +TP_LOCALLAB_LUMASK_TOOLTIP;Adjust the gray of the mask background in Show Mask (Mask and modifications) +TP_LOCALLAB_LUMAWHITESEST;Whiteest +TP_LOCALLAB_LUMONLY;Luminance only +TP_LOCALLAB_MASFRAME;Mask and Merge +TP_LOCALLAB_MASFRAME_TOOLTIP;For all masks.\nTake into account deltaE image to avoid retouching the selection area when sliders gamma mask, slope mask, chroma mask and curves contrast , levels contrasts, and mask blur, structure(if enabled tool) are used.\nDisabled in Inverse +TP_LOCALLAB_MASK2;Contrast curve mask +TP_LOCALLAB_MASK;Mask +TP_LOCALLAB_MASKCOL;Mask Curves +TP_LOCALLAB_MASKH;Hue curve mask +TP_LOCALLAB_MASK_TOOLTIP;You can enable multiple masks for a single tool, this requires activating another tool (but without using the tool : sliders to 0,...) where is the mask you want to activate.\n\nYou can also duplicate the RT-spot and place it right next to each other,variations of references allow fine work on images. +TP_LOCALLAB_MED;Medium +TP_LOCALLAB_MEDIAN;Median Low +TP_LOCALLAB_MEDNONE;None +TP_LOCALLAB_MERCOL;Color +TP_LOCALLAB_MERDCOL;Merge background (ΔE) +TP_LOCALLAB_MERELE;Lighten only +TP_LOCALLAB_MERFIV;Addition +TP_LOCALLAB_MERFOR;Color Dodge +TP_LOCALLAB_MERFOU;Multiply +TP_LOCALLAB_MERGE1COLFRA;Merge with Original or Previous or Background +TP_LOCALLAB_MERGECOLFRA;Mask: LCH & Structure +TP_LOCALLAB_MERGEFIV;Previous Spot(Mask 7) + Mask LCH +TP_LOCALLAB_MERGEFOU;Previous Spot(Mask 7) +TP_LOCALLAB_MERGENONE;None +TP_LOCALLAB_MERGEONE;Short Curves 'L' Mask +TP_LOCALLAB_MERGEOPA_TOOLTIP;Opacity merge % current Spot with original or previous Spot.\nContrast threshold : adjust result in function of Original contrast +TP_LOCALLAB_MERGETHR;Original(Mask 7) + Mask LCH +TP_LOCALLAB_MERGETWO;Original(Mask 7) +TP_LOCALLAB_MERGETYPE;Merge image and mask +TP_LOCALLAB_MERGETYPE_TOOLTIP;None, use all mask in LCH mode.\nShort curves 'L' mask, use a short circuit for mask 2, 3, 4, 6, 7.\nOriginal mask 8, blend current image with original +TP_LOCALLAB_MERGEMER_TOOLTIP;Take into account ΔE to merge files (equivalent of scope for this use) +TP_LOCALLAB_MERHEI;Overlay +TP_LOCALLAB_MERHUE;Hue +TP_LOCALLAB_MERLUCOL;Luminance +TP_LOCALLAB_MERLUM;Luminosity +TP_LOCALLAB_MERNIN;Screen +TP_LOCALLAB_MERONE;Normal +TP_LOCALLAB_MERSAT;Saturation +TP_LOCALLAB_MERSEV0;Soft Light Illusion +TP_LOCALLAB_MERSEV1;Soft Light W3C +TP_LOCALLAB_MERSEV2;Hard Light +TP_LOCALLAB_MERSEV;Soft Light Photshop +TP_LOCALLAB_MERSIX;Divide +TP_LOCALLAB_MERTEN;Darken only +TP_LOCALLAB_MERTHI;Color Burn +TP_LOCALLAB_MERTHR;Difference +TP_LOCALLAB_MERTWE;Exclusion +TP_LOCALLAB_MERTWO;Substract +TP_LOCALLAB_METHOD_TOOLTIP;'Enhanced + chroma denoise' significantly increases processing times.\nBut reduce artifacts. +TP_LOCALLAB_MLABEL;Restored datas Min=%1 Max=%2 (Clip - Offset) +TP_LOCALLAB_MLABEL_TOOLTIP;'Should be' near min=0 max=32768 (log mode) but others values are possible.\nYou can act on Clip Restored datas and Offset to normalize.\n\nRestored image with no mixture. +TP_LOCALLAB_MODE_EXPERT;Expert +TP_LOCALLAB_MODE_NORMAL;Normal +TP_LOCALLAB_MRFIV;Background +TP_LOCALLAB_MRFOU;Previous Spot +TP_LOCALLAB_MRONE;None +TP_LOCALLAB_MRTHR;Original Image +TP_LOCALLAB_MRTWO;Short Curves 'L' Mask +TP_LOCALLAB_MULTIPL_TOOLTIP;Allows the retouching of tones over a very wide range : -18EV +4EV. The first slider acts on very dark tones between -18EV and -6EV. The last slider acts light tones up to 4EV +TP_LOCALLAB_NEIGH;Radius +TP_LOCALLAB_NOISECHROCOARSE;Chroma coarse (Wav) +TP_LOCALLAB_NOISECHROC_TOOLTIP;If superior to zero, high quality algorithm is enabled.\nCoarse is for slider >=0.02 +TP_LOCALLAB_NOISECHRODETAIL;Chroma detail recovery (DCT ƒ) +TP_LOCALLAB_NOISECHROFINE;Chroma fine (Wav) +TP_LOCALLAB_NOISEDETAIL_TOOLTIP;Disabled if slider = 100 +TP_LOCALLAB_NOISELEQUAL;Equalizer white-black +TP_LOCALLAB_NOISELUMCOARSE;Luminance coarse (Wav) +TP_LOCALLAB_NOISELUMDETAIL;Luminance detail recovery (DCT ƒ) +TP_LOCALLAB_NOISELUMFINE;Luminance fine 1 (Wav) +TP_LOCALLAB_NOISELUMFINETWO;Luminance fine 2 (Wav) +TP_LOCALLAB_NOISELUMFINEZERO;Luminance fine 0 (Wav) +TP_LOCALLAB_NOISEMETH;Denoise +TP_LOCALLAB_NONENOISE;None +TP_LOCALLAB_OFFS;Offset +TP_LOCALLAB_OFFSETWAV;Offset +TP_LOCALLAB_OPACOL;Opacity +TP_LOCALLAB_ORIGLC;Merge only with original image +TP_LOCALLAB_ORRETISTREN_TOOLTIP;Acts on the Laplacian threshold, the greater the action, the more the differences in contrast will be reduced +TP_LOCALLAB_ORRETILAP_TOOLTIP;Acts on a second Laplacian threshold, to take into account ΔE to differentiate the action especially with the background (different from Scope) +TP_LOCALLAB_PASTELS2;Vibrance +TP_LOCALLAB_PDE;ΔØ Laplacian PDE - Dynamic Range compression + Standard +TP_LOCALLAB_PDEFRA;PDE IPOL - Contrast attenuator ƒ +TP_LOCALLAB_PDEFRAME_TOOLTIP;PDE IPOL - personal algorithm adapted from IPOL to Rawtherapee: lead to very different results and needs differents settings that Standard (negative black, gamma < 1,...)\nMay be usefull for low exposure or high dynamic range.\n +TP_LOCALLAB_PREVIEW;Preview ΔE +TP_LOCALLAB_PROXI;ΔE weakening +TP_LOCALLAB_QUALCURV_METHOD;Curves type +TP_LOCALLAB_QUAL_METHOD;Global quality +TP_LOCALLAB_RADIUS;Radius +TP_LOCALLAB_RADIUS_TOOLTIP;Above Radius 30 Use Fast Fourier Transform +TP_LOCALLAB_RADMASKCOL;Smooth Radius Mask +TP_LOCALLAB_RECT;Rectangle +TP_LOCALLAB_RECURS;Recursive references +TP_LOCALLAB_RECURS_TOOLTIP;Recalculate references for hue, luma, chroma after each module and after each RT-spot.\nAlso useful for working with masks. +TP_LOCALLAB_REFLABEL;Ref. (0..1) Chroma=%1 Luma=%2 Hue=%3 +TP_LOCALLAB_REN_DIALOG_LAB;Enter the new Control Spot name +TP_LOCALLAB_REN_DIALOG_NAME;Renaming Control Spot +TP_LOCALLAB_RESETSHOW;Reset All Show Modifications +TP_LOCALLAB_RESID;Residual Image +TP_LOCALLAB_RESIDBLUR;Blur Residual Image +TP_LOCALLAB_RESIDCHRO;Residual image Chroma +TP_LOCALLAB_RESIDCOMP;Compress Residual image +TP_LOCALLAB_RESIDCONT;Residual image Contrast +TP_LOCALLAB_RESIDHI;Highlights +TP_LOCALLAB_RESIDHITHR;Highlights threshold +TP_LOCALLAB_RESIDSHA;Shadows +TP_LOCALLAB_RESIDSHATHR;Shadows threshold +TP_LOCALLAB_RETI;Dehaze - Retinex Strong contrast +TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP;Play on internal parameters to optimize response.\nLook at the "restored datas" indicators "near" min=0 and max=32768 (log mode), but others values are possible. +TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP;Have no effect when the value "Lightness = 1" or "Darkness =2" is chosen.\nIn other cases, the last step of "Multiple scale Retinex" is applied an algorithm close to "local contrast", these 2 cursors, associated with "Strength" will allow to play upstream on the local contrast. +TP_LOCALLAB_RETI_LOGLIN_TOOLTIP;Logarithm allows differenciation for haze or normal.\nLogarithm brings more contrast but will generate more halo. +TP_LOCALLAB_RETI_SCALE_TOOLTIP;If scale=1, retinex behaves like local contrast with many more possibilities.\nThe greater the scale, the more intense the recursive action, the longer the calculation times +TP_LOCALLAB_RETIFRA;Retinex +TP_LOCALLAB_RETIM;Original Retinex +TP_LOCALLAB_RETITOOLFRA;Retinex Tools +TP_LOCALLAB_RETI_FFTW_TOOLTIP;FFT improve quality and allow big radius, but increases the treatment time.\nThe treatment time depends on the surface to be treated\nThe treatment time depends on the value of scale (be carefull to high values).\nTo be used preferably for large radius.\n\nDimensions can be reduced by a few pixels to optimize FFTW.\nThis optimization can reduce the treatment time by a factor of 1.5 to 10.\nOptimization not used in Preview +TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP;Adapt these values according to images - if misty images and depending on whether you want to act on the front or the background +TP_LOCALLAB_REWEI;Reweighting iterates +TP_LOCALLAB_RGB;RGB Tone Curve +TP_LOCALLAB_ROW_NVIS;Not visible +TP_LOCALLAB_ROW_VIS;Visible +TP_LOCALLAB_SATUR;Saturation +TP_LOCALLAB_SAVREST;Save - Restore Current Image +TP_LOCALLAB_SCALEGR;Scale +TP_LOCALLAB_SCALERETI;Scale +TP_LOCALLAB_SCALTM;Scale +TP_LOCALLAB_SCOPEMASK;Scope Mask ΔE Image +TP_LOCALLAB_SCOPEMASK_TOOLTIP;Enabled if Mask DeltaE Image is enabled.\nLow values avoid retouching selected area +TP_LOCALLAB_SENSI;Scope +TP_LOCALLAB_SENSIBN;Scope +TP_LOCALLAB_SENSICB;Scope +TP_LOCALLAB_SENSIDEN;Scope +TP_LOCALLAB_SENSIEXCLU;Scope +TP_LOCALLAB_SENSIEXCLU_TOOLTIP;Adjust color to include in exclusion! +TP_LOCALLAB_SENSIH;Scope +TP_LOCALLAB_SENSIH_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors. +TP_LOCALLAB_SENSILOG;Scope +TP_LOCALLAB_SENSIS;Scope +TP_LOCALLAB_SENSIS_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm. +TP_LOCALLAB_SENSI_TOOLTIP;Adjust scope of action:\nSmall values limit action to colors very similar to those under the center spot.\nHigh values let the tool act upon a wider range of colors.\nValues smaller than 20 lead to a better algorithm. +TP_LOCALLAB_SETTINGS;Settings +TP_LOCALLAB_SH1;Shadows Highlights +TP_LOCALLAB_SH2;Equalizer +TP_LOCALLAB_SHADEX;Shadows +TP_LOCALLAB_SHADEXCOMP;Shadows compression & tonal width +TP_LOCALLAB_SHADHIGH;ShadowsHighlight - Tone equalizer +TP_LOCALLAB_SHADOWHIGHLIGHT_TOOLTIP;Can be used instead - or in complement - of exposure module in difficult cases.\nThe use of Denoise may be necessary : lightening the shadows.\n\nCan be used as graduated filter (increase Scope) +TP_LOCALLAB_SHAMASKCOL;Shadows mask +TP_LOCALLAB_SHAPETYPE;Shape RT-spot area +TP_LOCALLAB_SHAPE_TOOLTIP;Elipse is normal mode.\nRectangle can be used in some cases, for example to work in full image in conjonction with delimiters outside preview, transition = 100.\n\nPolygone - Beziers are waiting for GUI... +TP_LOCALLAB_SHARAMOUNT;Amount +TP_LOCALLAB_SHARBLUR;Blur radius +TP_LOCALLAB_SHARDAMPING;Damping +TP_LOCALLAB_SHARITER;Iterations +TP_LOCALLAB_SHARFRAME;Modifications +TP_LOCALLAB_SHARP;Sharpening +TP_LOCALLAB_SHARRADIUS;Radius +TP_LOCALLAB_SHORTC;Short Curves 'L' Mask +TP_LOCALLAB_SHORTCMASK_TOOLTIP;Short circuit the 2 curves L(L) and L(H).\nAllows you to mix the current image with the original image modified by the mask job.\nUsable with masks 2, 3, 4, 6, 7 +TP_LOCALLAB_SHOWC1;Merge file +TP_LOCALLAB_SHOWC;Mask and modifications +TP_LOCALLAB_SHOWCB;Mask and modifications +TP_LOCALLAB_SHOWDCT;Show process Fourier ƒ +TP_LOCALLAB_SHOWE;Mask and modifications +TP_LOCALLAB_SHOWFOURIER;Fourier ƒ(dct) +TP_LOCALLAB_SHOWLAPLACE;∆ Laplacian (first) +TP_LOCALLAB_SHOWLC;Mask and modifications +TP_LOCALLAB_SHOWMASK;Show mask +TP_LOCALLAB_SHOWMASKCOL_TOOLTIP;Display mask modifications.\nBeware, you can only view one tool mask at the same time.\n\nNote: Use Mask is before algorihtm shape detection. +TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP;Show process Fourier:\nShows the different stages of the process.\nLaplace - builds the second derivative according to the threshold (first step).\nFourier -shows the transformed Laplacian with DCT.\nPoisson - show solution of Poisson DCE.\nNormalize - show result without normalization luminance. +TP_LOCALLAB_SHOWMASKTYP1;Blur & Noise +TP_LOCALLAB_SHOWMASKTYP2;Denoise +TP_LOCALLAB_SHOWMASKTYP3;Blur & Noise + Denoise +TP_LOCALLAB_SHOWMASKTYP_TOOLTIP;Mask and modifications can be chosen.\nBlur and noise : in this case it is not used for 'denoise'.\nDenoise : in this case it is not used for 'blur and noise'.\n\nBlur and noise + denoise : mask is shared, be carefull to 'show modifications' and 'scope' +TP_LOCALLAB_SHOWMNONE;None +TP_LOCALLAB_SHOWMODIF;Show modifications without mask +TP_LOCALLAB_SHOWMODIFMASK;Show modifications with mask +TP_LOCALLAB_SHOWNORMAL;Normalize luminance (no) +TP_LOCALLAB_SHOWPLUS;Mask and modifications - Smooth-Blur & Denoise +TP_LOCALLAB_SHOWPOISSON;Poisson (pde ƒ) +TP_LOCALLAB_SHOWR;Mask and modifications +TP_LOCALLAB_SHOWS;Mask and modifications +TP_LOCALLAB_SHOWREF;Preview ΔE +TP_LOCALLAB_SHOWSTRUC;Show structure Spot +TP_LOCALLAB_SHOWSTRUCEX;Show structure Spot +TP_LOCALLAB_SHOWT;Mask and modifications +TP_LOCALLAB_SHOWVI;Mask and modifications +TP_LOCALLAB_SHRESFRA;Shadows/Highlights +TP_LOCALLAB_SHTRC_TOOLTIP;Modifies the tones of the image by acting on a TRC (Tone Response Curve).\nGamma acts mainly on light tones.\nSlope acts mainly on dark tones +TP_LOCALLAB_SIGMAWAV;Attenuation Response +TP_LOCALLAB_SIM;Simple +TP_LOCALLAB_SLOMASKCOL;Slope mask +TP_LOCALLAB_SLOSH;Slope +TP_LOCALLAB_SOFT;Soft Light - Original Retinex +TP_LOCALLAB_SOFTM;Soft Light +TP_LOCALLAB_SOFTMETHOD_TOOLTIP;Original Retinex is very different from others Retinex method.\nIts acts on grey and balance luminance.\nIt is an emulation of "Dodge" and "Burn" +TP_LOCALLAB_SOFTRADIUSCOL;Soft radius +TP_LOCALLAB_SOFTRETI;Reduce artifact ΔE +TP_LOCALLAB_SOFTRETI_TOOLTIP;Take into account deltaE to improve Transmission map +TP_LOCALLAB_SOURCE_GRAY;Value +TP_LOCALLAB_SPECIAL;Special use of RGB curves +TP_LOCALLAB_SPECIAL_TOOLTIP;Only for this RGB curve, disabled (or reduce effects) of Scope, mask...for example, if you want to have a negative effect. +TP_LOCALLAB_SPECCASE; Specific cases +TP_LOCALLAB_SPOTNAME;Control Spot # +TP_LOCALLAB_STD;Standard +TP_LOCALLAB_STR;Strength +TP_LOCALLAB_STRBL;Strength +TP_LOCALLAB_STREN;Compression Strength +TP_LOCALLAB_STRENG;Strength +TP_LOCALLAB_STRENGR;Strength +TP_LOCALLAB_STRENGTH;Noise +TP_LOCALLAB_STRGRID;Strength +TP_LOCALLAB_STRRETI_TOOLTIP;if Strength Retinex < 0.2 only Dehaze is enabled.\nif Strength Retinex >= 0.1 Dehaze is in luminance mode. +TP_LOCALLAB_STRUC;Structure +TP_LOCALLAB_STRUCCOL1;Structure Spot +TP_LOCALLAB_STRUCT_TOOLTIP;Use Sobel algorithm to take into account structure in shape detection.\nyou can have a preview by activating "mask and modifications - Show structure spot".\n\nCan be used in conjunction with masks (expert) structure, blur, wavelet to improve edge detection.\n\nNeeds maskless adjustments to be activated (lightness, exposure...) +TP_LOCALLAB_STRUCCOL;Structure +TP_LOCALLAB_STRUMASKCOL;Structure mask strength +TP_LOCALLAB_STRUMASK_TOOLTIP;Generate a structure mask with difference between surface areas and reliefs.\nIf structure mask as tool is enabled, this mask is used in addition to the other tools (gamma, slope, contrast curve ...) +TP_LOCALLAB_STYPE;Shape method +TP_LOCALLAB_STYPE_TOOLTIP;You can choose between:\nSymmetrical - left handle linked to right, top handle linked to bottom.\nIndependent - all handles are independent. +TP_LOCALLAB_SYM;Symmetrical (mouse) +TP_LOCALLAB_SYMSL;Symmetrical (mouse + sliders) +TP_LOCALLAB_TARGET_GRAY;Target Gray Point +TP_LOCALLAB_THRES;Threshold structure +TP_LOCALLAB_THRESDELTAE;Threshold ΔE-scope +TP_LOCALLAB_THRESRETI;Threshold +TP_LOCALLAB_THRESWAV;Balance Threshold +TP_LOCALLAB_TLABEL2;TM Effective Tm=%1 TM=%2 +TP_LOCALLAB_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4 (Threshold) +TP_LOCALLAB_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map.\nYou can act on Threshold to normalize +TP_LOCALLAB_TM;Tone Mapping - Texture +TP_LOCALLAB_TM_MASK;Use transmission map +TP_LOCALLAB_TONEMAPESTOP_TOOLTIP;This parameter affects sensitivity to edges.\n The greater it is the more likely an illumination change is to be considered an "edge".\n If set to zero tone mapping will have an effect similar to unsharp masking. +TP_LOCALLAB_TONEMAPREWEI_TOOLTIP;In some cases tone mapping may result in a cartoonish appearance, and in some rare cases soft but wide halos may appear.\n Increasing the number of reweighting iterates will help fight some of these problems. +TP_LOCALLAB_TONEMASCALE_TOOLTIP;This control gives meaning to the difference between "local" and "global" contrast.\nThe greater it is the larger a detail needs to be in order to be boosted +TP_LOCALLAB_TONEMAPGAM_TOOLTIP;Gamma moves the action of tone-mapping to shadows or highlights. +TP_LOCALLAB_TOOLCOL;Structure mask as tool +TP_LOCALLAB_TONEMAP_TOOLTIP;Tone Mapping - main menu must be disabled +TP_LOCALLAB_TOOLMASK;Tools +TP_LOCALLAB_TRANSIT;Transition Gradient +TP_LOCALLAB_TRANSITGRAD;Transition differentiation XY +TP_LOCALLAB_TRANSITGRAD_TOOLTIP;Changes the transition of the abscissa to that of the ordinate +TP_LOCALLAB_TRANSITVALUE;Transition value +TP_LOCALLAB_TRANSITWEAK;Transition weakening +TP_LOCALLAB_TRANSITWEAK_TOOLTIP;Adjust transition weakening : change smoothness process - 1 linear - 2 parabolic - 3 cubic - ^25.\nCan be used in conjunction with very low transition values to reduce defects (CBDL, Wavelet, Color & Light) +TP_LOCALLAB_TRANSIT_TOOLTIP;Adjust smoothness of transition between affected and unaffected areas, as a percentage of the "radius" +TP_LOCALLAB_TRANSMISSIONGAIN;Transmission gain +TP_LOCALLAB_TRANSMISSIONMAP;Transmission map +TP_LOCALLAB_TRANSMISSION_TOOLTIP;Transmission according to transmission.\nAbscissa: transmission from negative values (min), mean, and positives values (max).\nOrdinate: amplification or reduction.\nYou can act on this curve to change Transmission and reduce the artifacts +TP_LOCALLAB_USEMASK;Use mask +TP_LOCALLAB_VART;Variance (contrast) +TP_LOCALLAB_VIBRANCE;Vibrance - Warm & Cool +TP_LOCALLAB_VIS_TOOLTIP;Click to show/hide selected Control Spot.\nCtrl+click to show/hide all Control Spot. +TP_LOCALLAB_LIST_NAME;Add tool to current spot... +TP_LOCALLAB_LIST_TOOLTIP;Choose a tool and then its level of complexity "Normal" or "Expert".\nThe number reflects the place of the tool in the process of each RT-Spot +TP_LOCALLAB_COLOR_TOOLNAME;Color&Light (Defects) - 11 +TP_LOCALLAB_EXP_TOOLNAME;Exposure - Dynamic Range Compression - 10 +TP_LOCALLAB_SH_TOOLNAME;Shadows Highlight & Tone Equalizer - 5 +TP_LOCALLAB_VIB_TOOLNAME;Vibrance - Warm & Cool - 3 +TP_LOCALLAB_SOFT_TOOLNAME;Soft Light & Original Retinex - 6 +TP_LOCALLAB_BLUR_TOOLNAME;Smooth Blur Gain & Denoise - 1 +TP_LOCALLAB_TONE_TOOLNAME;Tone Mapping - 4 +TP_LOCALLAB_RET_TOOLNAME;Dehaze & Retinex - 9 +TP_LOCALLAB_SHARP_TOOLNAME;Sharpening - 8 +TP_LOCALLAB_LC_TOOLNAME;Local Constrast & Wavelet (Defects) - 7 +TP_LOCALLAB_CBDL_TOOLNAME;CBDL (Defects) - 2 +TP_LOCALLAB_LOG_TOOLNAME;Encoding log - 0 +TP_LOCALLAB_WAMASKCOL;Ψ Mask Wavelet level +TP_LOCALLAB_WARM;Warm - Cool & Color artifacts +TP_LOCALLAB_WARM_TOOLTIP;This slider use Ciecam algorithm and acts as White Balance, it can warm or cool the area selected.\nIt can also in some cases reduce color artifacts. +TP_LOCALLAB_WAV;Levels local contrast +TP_LOCALLAB_WAVMASK_TOOLTIP;Allows fine work on mask levels contrasts (structure) +TP_LOCALLAB_WAVBLUR_TOOLTIP;Performs a blur for each level of decomposition, as well as the residual image. +TP_LOCALLAB_WAVEDG;Local contrast +TP_LOCALLAB_WAVCOMP;Compression by Level +TP_LOCALLAB_WAVCOMP_TOOLTIP;Achive local contrast in function of the direction wavelet decomposition : horizontal, vertical, diagonal +TP_LOCALLAB_WAVCOMPRE;(un)Compression by Level +TP_LOCALLAB_WAVCOMPRE_TOOLTIP;Achieve a Tone-mapping or reduction local contrast by levels.\nOn abscissa levels +TP_LOCALLAB_WAVCON;Contrast by Level +TP_LOCALLAB_WAVCONTF_TOOLTIP;Similar to Contrast By Detail Levels : on abscissa levels. +TP_LOCALLAB_WAVDEN;Luminance denoise by level (0 1 2 + 3 and more) +TP_LOCALLAB_WASDEN_TOOLTIP;Denoise luminance for the 3 first levels (fine).\nThe right limit of the curve correspond to coarse : level 3 and beyond +TP_LOCALLAB_WAVE;Ψ Wavelet +TP_LOCALLAB_WAVEEDG_TOOLTIP;Achieves a sharpness taking into account the notion of edges wavelet.\nRequires that at least the first 4 levels are usable +TP_LOCALLAB_WAVGRAD_TOOLTIP;Graduated filter for Local contrast "luminance" +TP_LOCALLAB_WAVHIGH;Ψ Wavelet high +TP_LOCALLAB_WAVLEV;Blur by Level +TP_LOCALLAB_WAVLOW;Ψ Wavelet low +TP_LOCALLAB_WAVMASK;Ψ Mask Levels local contrast +TP_LOCALLAB_WAVMED;Ψ Wavelet normal +TP_LOCALLAB_WEDIANHI;Median Hi +TP_LOCALLAB_WHITE_EV;White Ev +TP_LOCALLAB_BLNOI_EXP;Blur & Noise +TP_LOCALLAB_DENOI_EXP;Denoise +TP_LOCAL_HEIGHT;Bottom +TP_LOCAL_HEIGHT_T;Top +TP_LOCAL_WIDTH;Right +TP_LOCAL_WIDTH_L;Left +TP_LOCRETI_METHOD_TOOLTIP;Low = Reinforce low light.\nUniform = Equalize action.\nHigh = Reinforce high light.\n TP_LOCALCONTRAST_AMOUNT;Amount TP_LOCALCONTRAST_DARKNESS;Darkness level TP_LOCALCONTRAST_LABEL;Local Contrast @@ -1861,8 +2870,26 @@ TP_PCVIGNETTE_ROUNDNESS_TOOLTIP;Roundness:\n0 = rectangle,\n50 = fitted ellipse, TP_PCVIGNETTE_STRENGTH;Strength TP_PCVIGNETTE_STRENGTH_TOOLTIP;Filter strength in stops (reached in corners). TP_PDSHARPENING_LABEL;Capture Sharpening +TP_PERSPECTIVE_CAMERA_CROP_FACTOR;Crop factor +TP_PERSPECTIVE_CAMERA_FRAME;Correction +TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH;Focal length +TP_PERSPECTIVE_CAMERA_PITCH;Vertical +TP_PERSPECTIVE_CAMERA_ROLL;Rotation +TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL;Horizontal shift +TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL;Vertical shift +TP_PERSPECTIVE_CAMERA_YAW;Horizontal TP_PERSPECTIVE_HORIZONTAL;Horizontal TP_PERSPECTIVE_LABEL;Perspective +TP_PERSPECTIVE_METHOD;Method +TP_PERSPECTIVE_METHOD_SIMPLE;Simple +TP_PERSPECTIVE_METHOD_CAMERA_BASED;Camera-based +TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME;Post-correction adjustment +TP_PERSPECTIVE_PROJECTION_PITCH;Vertical +TP_PERSPECTIVE_PROJECTION_ROTATE;Rotation +TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL;Horizontal shift +TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL;Vertical shift +TP_PERSPECTIVE_PROJECTION_YAW;Horizontal +TP_PERSPECTIVE_RECOVERY_FRAME;Recovery TP_PERSPECTIVE_VERTICAL;Vertical TP_PFCURVE_CURVEEDITOR_CH;Hue TP_PFCURVE_CURVEEDITOR_CH_TOOLTIP;Controls defringe strength by color.\nHigher = more,\nLower = less. @@ -2040,8 +3067,8 @@ TP_RETINEX_MAP_NONE;None TP_RETINEX_MEDIAN;Transmission median filter TP_RETINEX_METHOD;Method TP_RETINEX_METHOD_TOOLTIP;Low = Reinforce low light.\nUniform = Equalize action.\nHigh = Reinforce high light.\nHighlights = Remove magenta in highlights. -TP_RETINEX_MLABEL;Restored haze-free Min=%1 Max=%2 -TP_RETINEX_MLABEL_TOOLTIP;Should be near min=0 max=32768\nRestored image with no mixture. +TP_RETINEX_MLABEL;Restored data Min=%1 Max=%2 +TP_RETINEX_MLABEL_TOOLTIP;'Should be' near min=0 max=32768 (log mode) but others values are possible\nRestored image with no mixture. TP_RETINEX_NEIGHBOR;Radius TP_RETINEX_NEUTRAL;Reset TP_RETINEX_NEUTRAL_TIP;Reset all sliders and curves to their default values. @@ -2054,11 +3081,11 @@ TP_RETINEX_SLOPE;Free gamma slope TP_RETINEX_STRENGTH;Strength TP_RETINEX_THRESHOLD;Threshold TP_RETINEX_THRESHOLD_TOOLTIP;Limits in/out.\nIn = image source,\nOut = image gauss. -TP_RETINEX_TLABEL;TM Min=%1 Max=%2 Mean=%3 Sigma=%4 -TP_RETINEX_TLABEL2;TM Tm=%1 TM=%2 -TP_RETINEX_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nMean and Sigma.\nTm=Min TM=Max of transmission map. +TP_RETINEX_TLABEL;TM Datas Min=%1 Max=%2 Mean=%3 Sigma=%4 +TP_RETINEX_TLABEL2;TM Effective Tm=%1 TM=%2 +TP_RETINEX_TLABEL_TOOLTIP;Transmission map result.\nMin and Max are used by Variance.\nTm=Min TM=Max of Transmission Map. TP_RETINEX_TRANF;Transmission -TP_RETINEX_TRANSMISSION;Transmission map +TP_RETINEX_TRANSMISSION;Transmission map TP_RETINEX_TRANSMISSION_TOOLTIP;Transmission according to transmission.\nAbscissa: transmission from negative values (min), mean, and positives values (max).\nOrdinate: amplification or reduction. TP_RETINEX_UNIFORM;Uniform TP_RETINEX_VARIANCE;Contrast @@ -2119,7 +3146,7 @@ TP_SOFTLIGHT_LABEL;Soft Light TP_SOFTLIGHT_STRENGTH;Strength TP_TM_FATTAL_AMOUNT;Amount TP_TM_FATTAL_ANCHOR;Anchor -TP_TM_FATTAL_LABEL;Dynamic Range Compression +TP_TM_FATTAL_LABEL;Dynamic Range Compression ƒ TP_TM_FATTAL_THRESHOLD;Detail TP_VIBRANCE_AVOIDCOLORSHIFT;Avoid color shift TP_VIBRANCE_CURVEEDITOR_SKINTONES;HH @@ -2165,14 +3192,14 @@ TP_WAVELET_BALANCE;Contrast balance d/v-h TP_WAVELET_BALANCE_TOOLTIP;Alters the balance between the wavelet directions: vertical-horizontal and diagonal.\nIf contrast, chroma or residual tone mapping are activated, the effect due to balance is amplified. TP_WAVELET_BALCHRO;Chrominance balance TP_WAVELET_BALCHRO_TOOLTIP;If enabled, the 'Contrast balance' curve or slider also modifies chroma balance. -TP_WAVELET_BALCHROM;Denoise Equalizer Blue-Red +TP_WAVELET_BALCHROM;Denoise Equalizer Blue-yellow Red-green TP_WAVELET_BALLUM;Denoise Equalizer White-Black TP_WAVELET_BANONE;None TP_WAVELET_BASLI;Slider TP_WAVELET_BATYPE;Contrast balance method TP_WAVELET_BLCURVE;Blur by levels TP_WAVELET_BLURFRAME;Blur -TP_WAVELET_BLUWAV;Damper +TP_WAVELET_BLUWAV;Attenuation Response TP_WAVELET_CBENAB;Toning and Color Balance TP_WAVELET_CB_TOOLTIP;For strong values product color-toning by combining it or not with levels decomposition 'toning'\nFor low values you can change the white balance of the background (sky, ...) without changing that of the front plane, generally more contrasted TP_WAVELET_CCURVE;Local contrast @@ -2217,6 +3244,7 @@ TP_WAVELET_CURVEEDITOR_HH;HH TP_WAVELET_CURVEEDITOR_HH_TOOLTIP;Modifies the residual image's hue as a function of hue. TP_WAVELET_DALL;All directions TP_WAVELET_DAUB;Edge performance +TP_WAVELET_DAUBLOCAL;Wavelet Edge performance TP_WAVELET_DAUB2;D2 - low TP_WAVELET_DAUB4;D4 - standard TP_WAVELET_DAUB6;D6 - standard plus @@ -2237,7 +3265,7 @@ TP_WAVELET_EDGEDETECTTHR;Threshold low (noise) TP_WAVELET_EDGEDETECTTHR2;Threshold high (detection) TP_WAVELET_EDGEDETECTTHR_TOOLTIP;This adjuster lets you target edge detection for example to avoid applying edge sharpness to fine details, such as noise in the sky. TP_WAVELET_EDGEDETECT_TOOLTIP;Moving the slider to the right increases edge sensitivity. This affects local contrast, edge settings and noise. -TP_WAVELET_EDEFFECT;Damper +TP_WAVELET_EDEFFECT;Attenuation Response TP_WAVELET_EDEFFECT_TOOLTIP;This slider controls how wide the range of contrast values are that receive the maximum effect from the tool.\nMaximum value (2.5) disabled the tool TP_WAVELET_EDGESENSI;Edge sensitivity TP_WAVELET_EDGREINF_TOOLTIP;Reinforce or reduce the action of the first level, do the opposite to the second level, and leave the rest unchanged. @@ -2320,9 +3348,9 @@ TP_WAVELET_SETTINGS;Wavelet Settings TP_WAVELET_SHA;Sharp mask TP_WAVELET_SHFRAME;Shadows/Highlights TP_WAVELET_SHOWMASK;Show wavelet 'mask' -TP_WAVELET_SIGMA;Damper -TP_WAVELET_SIGMAFIN;Damper -TP_WAVELET_SIGMA_TOOLTIP;The effect of the contrast sliders is stronger in medium contrast details, and weaker in high and low contrast details.\n With this slider you can control how quickly the effect dampens towards the extreme contrasts.\n The higher the slider is set, the wider the range of contrasts which will get a strong change, and the higher the risk to generate artifacts.\n The lower it is, the more pinpoint will the effect be applied to a narrow range of contrast values. +TP_WAVELET_SIGMA;Attenuation Response +TP_WAVELET_SIGMAFIN;Attenuation Response +TP_WAVELET_SIGMA_TOOLTIP;The effect of the contrast sliders is stronger in medium contrast details, and weaker in high and low contrast details.\n With this slider you can control how quickly the effect dampens towards the extreme contrasts.\n The higher the slider is set, the wider the range of contrasts which will get a strong change, and the higher the risk to generate artifacts.\n .The lower it is, the more the effect will be pinpointed towards a narrow range of contrast values TP_WAVELET_SKIN;Skin targetting/protection TP_WAVELET_SKIN_TOOLTIP;At -100 skin-tones are targetted.\nAt 0 all tones are treated equally.\nAt +100 skin-tones are protected while all other tones are affected. TP_WAVELET_SKY;Sky targetting/protection @@ -2334,8 +3362,8 @@ TP_WAVELET_SUPE;Extra TP_WAVELET_THR;Shadows threshold TP_WAVELET_THRESHOLD;Finer levels TP_WAVELET_THRESHOLD2;Coarser levels -TP_WAVELET_THRESHOLD_TOOLTIP;Only levels beyond the chosen value will be affected by the highlight luminance range. Other levels will be fully treated. The chosen value here limits the highest possible value of the shadow levels. : All levels from level 1 up to the chosen value will only be affected within the Finer levels luminance range.\nAll other levels will have the whole range of luminances affected, unless the Coarser levels setting limits it.\nThe chosen value in this slider becomes the minimum possible value of the Coarser levels. -TP_WAVELET_THRESHOLD2_TOOLTIP;Only levels between 9 and 9 minus the value will be affected by the shadow luminance range. Other levels will be fully treated. The highest level possible is limited by the highlight level value (9 minus highlight level value). : Only levels between the chosen value and level 9/Extra will be affected by the Coarser levels luminance range.\nAll other levels will have the whole range of luminances affected, unless the Finer levels setting limits it.\nThe lower level possible that will be considered by the algorithm is limited by the Finer levels value. +TP_WAVELET_THRESHOLD_TOOLTIP;Only levels below and including the chosen value will be affected by the Highlight luminance range. +TP_WAVELET_THRESHOLD2_TOOLTIP;Only levels from the chosen value to the selected number of ‘wavelet levels’ will be affected by the Shadow luminance range. TP_WAVELET_THRESWAV;Balance Threshold TP_WAVELET_THRH;Highlights threshold TP_WAVELET_TILESBIG;Tiles @@ -2358,7 +3386,7 @@ TP_WAVELET_USHARP_TOOLTIP;Origin : the source file is the file before Wavelet.\n TP_WAVELET_USH_TOOLTIP;If you select Sharp-mask, wavelet settings will be automatically positioned :\nBackground=black, Process=below, level=3...you can change level between 1 and 4.\n\nIf you select Clarity, wavelet settings will be automatically positioned :\nBackground=residual, Process=above, level=7..you can change level between 5 and 10 and wavelet levels. TP_WAVELET_WAVLOWTHR;Low contrast threshold TP_WAVELET_WAVOFFSET;Offset -TP_WAVELET_OFFSET_TOOLTIP;Offset modifies the balance between shadows and highlights.\nHigh values here will amplify the contrast change of the highlights, whereas low values will amplify the contrast change of the shadows.\nAlong with a low Damper value you will able to select the contrasts that will be enhanced. +TP_WAVELET_OFFSET_TOOLTIP;Offset modifies the balance between shadows and highlights.\nHigh values here will amplify the contrast change of the highlights, whereas low values will amplify the contrast change of the shadows.\nAlong with a low Attenuation Response value you will able to select the contrasts that will be enhanced. TP_WBALANCE_AUTO;Auto TP_WBALANCE_AUTOITCGREEN;Temperature correlation TP_WBALANCE_AUTOOLD;RGB grey diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css index bdadc00db..76f0004ee 100644 --- a/rtdata/themes/RawTherapee-GTK3-20_.css +++ b/rtdata/themes/RawTherapee-GTK3-20_.css @@ -745,6 +745,7 @@ flowboxchild:selected { background-color: #363636; } +#LocallabToolPanel frame, #ExpanderBox frame, #ExpanderBox2 frame, #ExpanderBox3 frame { @@ -761,18 +762,22 @@ flowboxchild:selected { padding: 0.25em; } +#LocallabToolPanel frame > label, #LocallabToolPanel frame frame > label, #ExpanderBox frame > label, #ExpanderBox frame frame > label, #ExpanderBox2 frame > label, #ExpanderBox2 frame frame > label, #ExpanderBox3 frame > label, #ExpanderBox3 frame frame > label { margin-left: 7pt; margin-top: 0; } + +#LocallabToolPanel frame > box, #LocallabToolPanel frame frame > box, #LocallabToolPanel frame > grid, #LocallabToolPanel frame frame > grid, #ExpanderBox frame > box, #ExpanderBox frame frame > box, #ExpanderBox frame > grid, #ExpanderBox frame frame > grid, #ExpanderBox2 frame > box, #ExpanderBox2 frame frame > box, #ExpanderBox2 frame > grid, #ExpanderBox2 frame frame > grid, #ExpanderBox3 frame > box, #ExpanderBox3 frame frame > box, #ExpanderBox3 frame > grid, #ExpanderBox3 frame frame > grid { margin: 0.1666666666666666em; } +#LocallabToolPanel > box > checkbutton, #LocallabToolPanel > box > box, #LocallabToolPanel > grid > checkbutton, #LocallabToolPanel > box > grid, #LocallabToolPanel > grid > grid, #LocallabToolPanel frame > box > grid, #LocallabToolPanel frame > grid > grid, #LocallabToolPanel frame > grid > box, #ExpanderBox > box > checkbutton, #ExpanderBox > box > box, #ExpanderBox > grid > checkbutton, #ExpanderBox > box > grid, #ExpanderBox > grid > grid, #ExpanderBox frame > box > grid, #ExpanderBox frame > grid > grid, #ExpanderBox frame > grid > box, #ExpanderBox2 > box > checkbutton, #ExpanderBox2 > box > box, #ExpanderBox2 > grid > checkbutton, #ExpanderBox2 > box > grid, #ExpanderBox2 > grid > grid, #ExpanderBox2 frame > box > grid, #ExpanderBox2 frame > grid > grid, #ExpanderBox2 frame > grid > box, #ExpanderBox3 > box > checkbutton, #ExpanderBox3 > box > box, #ExpanderBox3 > grid > checkbutton, #ExpanderBox3 > box > grid, #ExpanderBox3 > grid > grid, #ExpanderBox3 frame > box > grid, #ExpanderBox3 frame > grid > grid, #ExpanderBox3 frame > grid > box { @@ -796,6 +801,7 @@ flowboxchild:selected { } /* Sub-tool (MyExpander) background */ +#LocallabToolPanel > box, #LocallabToolPanel > grid, #ExpanderBox2 > box, #ExpanderBox2 > grid { background-color: #3B3B3B; border: 0.0833333333333333em solid #2A2A2A; @@ -804,10 +810,12 @@ flowboxchild:selected { padding: 0.25em; } +#LocallabToolPanel drawingarea, #ExpanderBox2 drawingarea { background-color: #3B3B3B; } +#LocallabToolPanel frame > border, #ExpanderBox2 frame > border { background-color: #414141; border: 0.0833333333333333em solid #373737; @@ -816,10 +824,12 @@ flowboxchild:selected { padding: 0.25em; } +#LocallabToolPanel frame drawingarea, #ExpanderBox2 frame drawingarea { background-color: #414141; } +#LocallabToolPanel frame frame > border, #ExpanderBox2 frame frame > border { background-color: #474747; border: 0.0833333333333333em solid #3D3D3D; @@ -828,6 +838,7 @@ flowboxchild:selected { padding: 0.25em; } +#LocallabToolPanel frame frame drawingarea, #ExpanderBox2 frame frame drawingarea { background-color: #474747; } @@ -854,7 +865,7 @@ flowboxchild:selected { color: #D8D8D8; } -#ExpanderBox2 separator, #ExpanderBox3 separator { +#LocallabToolPanel separator, #ExpanderBox2 separator, #ExpanderBox3 separator { color: #292929; } diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 0450ee29f..543cd4841 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -118,7 +118,9 @@ set(RTENGINESOURCEFILES impulse_denoise.cc init.cc ipdehaze.cc + ipgrain.cc iplab2rgb.cc + iplocallab.cc iplabregions.cc iplocalcontrast.cc ipresize.cc @@ -141,6 +143,7 @@ set(RTENGINESOURCEFILES myfile.cc panasonic_decoders.cc pdaflinesfilter.cc + perspectivecorrection.cc PF_correct_RT.cc pipettebuffer.cc pixelshift.cc diff --git a/rtengine/EdgePreservingDecomposition.cc b/rtengine/EdgePreservingDecomposition.cc index 04459e88a..34ac2440b 100644 --- a/rtengine/EdgePreservingDecomposition.cc +++ b/rtengine/EdgePreservingDecomposition.cc @@ -916,7 +916,7 @@ void EdgePreservingDecomposition::CompressDynamicRange(float *Source, float Scal float temp; if(DetailBoost > 0.f) { - float betemp = expf(-(2.f - DetailBoost + 0.694f)) - 1.f; //0.694 = log(2) + float betemp = expf(-(2.f - DetailBoost + 0.693147f)) - 1.f; //0.694 = log(2) temp = 1.2f * xlogf( -betemp); } else { temp = CompressionExponent - 1.0f; @@ -939,7 +939,7 @@ void EdgePreservingDecomposition::CompressDynamicRange(float *Source, float Scal cev = xexpf(LVFU(Source[i]) + LVFU(u[i]) * (tempv)) - epsv; uev = xexpf(LVFU(u[i])) - epsv; sourcev = xexpf(LVFU(Source[i])) - epsv; - _mm_storeu_ps( &Source[i], cev + DetailBoostv * (sourcev - uev) ); + _mm_storeu_ps( &Source[i], cev + DetailBoostv * (sourcev - uev)); } } diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc index 89e4c1b8d..fca26987f 100644 --- a/rtengine/FTblockDN.cc +++ b/rtengine/FTblockDN.cc @@ -43,7 +43,6 @@ #include "procparams.h" #include "rt_math.h" #include "sleef.h" - #include "../rtgui/threadutils.h" #include "../rtgui/options.h" @@ -483,11 +482,9 @@ enum nrquality {QUALITY_STANDARD, QUALITY_HIGH}; void ImProcFunctions::RGB_denoise(int kall, Imagefloat * src, Imagefloat * dst, Imagefloat * calclum, float * ch_M, float *max_r, float *max_b, bool isRAW, const procparams::DirPyrDenoiseParams & dnparams, const double expcomp, const NoiseCurve & noiseLCurve, const NoiseCurve & noiseCCurve, float &nresi, float &highresi) { BENCHFUN -//#ifdef _DEBUG MyTime t1e, t2e; t1e.set(); -//#endif if (dnparams.luma == 0 && dnparams.chroma == 0 && !dnparams.median && !noiseLCurve && !noiseCCurve) { //nothing to do; copy src to dst or do nothing in case src == dst if (src != dst) { @@ -955,13 +952,8 @@ BENCHFUN labdn->b[i1][j1] = B_ < 65535.f ? gamcurve[B_] : Color::gammanf(B_ / 65535.f, gam) * 32768.f; if (((i1 | j1) & 1) == 0) { - if (numTries == 1) { - noisevarlum[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL; - noisevarchrom[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f; - } else { - noisevarlum[(i1 >> 1) * width2 + (j1 >> 1)] = lumcalc[i >> 1][j >> 1]; - noisevarchrom[(i1 >> 1) * width2 + (j1 >> 1)] = ccalc[i >> 1][j >> 1]; - } + noisevarlum[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL; + noisevarchrom[(i1 >> 1) * width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f; } //end chroma @@ -993,13 +985,8 @@ BENCHFUN labdn->b[i1][j1] = (Y - Z); if (((i1 | j1) & 1) == 0) { - if (numTries == 1) { - noisevarlum[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL; - noisevarchrom[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f; - } else { - noisevarlum[(i1 >> 1)*width2 + (j1 >> 1)] = lumcalc[i >> 1][j >> 1]; - noisevarchrom[(i1 >> 1)*width2 + (j1 >> 1)] = ccalc[i >> 1][j >> 1]; - } + noisevarlum[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseLCurve ? lumcalc[i >> 1][j >> 1] : noisevarL; + noisevarchrom[(i1 >> 1)*width2 + (j1 >> 1)] = useNoiseCCurve ? maxNoiseVarab * ccalc[i >> 1][j >> 1] : 1.f; } } } @@ -1111,9 +1098,6 @@ BENCHFUN } if (execwavelet) {//gain time if user choose only median sliders L <=1 slider chrom master < 1 - wavelet_decomposition* Ldecomp; - wavelet_decomposition* adecomp; - int levwav = 5; float maxreal = max(realred, realblue); @@ -1154,9 +1138,9 @@ BENCHFUN levwav = min(maxlev2, levwav); // if (settings->verbose) printf("levwavelet=%i noisevarA=%f noisevarB=%f \n",levwav, noisevarab_r, noisevarab_b); - Ldecomp = new wavelet_decomposition(labdn->L[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels)); + const std::unique_ptr Ldecomp(new wavelet_decomposition(labdn->L[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels))); - if (Ldecomp->memoryAllocationFailed) { + if (Ldecomp->memory_allocation_failed()) { memoryAllocationFailed = true; } @@ -1175,7 +1159,7 @@ BENCHFUN int Wlvl_L = Ldecomp->level_W(lvl); int Hlvl_L = Ldecomp->level_H(lvl); - float ** WavCoeffs_L = Ldecomp->level_coeffs(lvl); + const float* const* WavCoeffs_L = Ldecomp->level_coeffs(lvl); if (!denoiseMethodRgb) { madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); @@ -1192,9 +1176,9 @@ BENCHFUN float chmaxresid = 0.f; float chmaxresidtemp = 0.f; - adecomp = new wavelet_decomposition(labdn->a[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels)); + std::unique_ptr adecomp(new wavelet_decomposition(labdn->a[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels))); - if (adecomp->memoryAllocationFailed) { + if (adecomp->memory_allocation_failed()) { memoryAllocationFailed = true; } @@ -1226,12 +1210,12 @@ BENCHFUN adecomp->reconstruct(labdn->a[0]); } - delete adecomp; + adecomp.reset(); if (!memoryAllocationFailed) { - wavelet_decomposition* bdecomp = new wavelet_decomposition(labdn->b[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels)); + std::unique_ptr bdecomp(new wavelet_decomposition(labdn->b[0], labdn->W, labdn->H, levwav, 1, 1, max(1, denoiseNestedLevels))); - if (bdecomp->memoryAllocationFailed) { + if (bdecomp->memory_allocation_failed()) { memoryAllocationFailed = true; } @@ -1266,7 +1250,7 @@ BENCHFUN bdecomp->reconstruct(labdn->b[0]); } - delete bdecomp; + bdecomp.reset(); if (!memoryAllocationFailed) { if (denoiseLuminance) { @@ -1306,8 +1290,6 @@ BENCHFUN } } } - - delete Ldecomp; } if (!memoryAllocationFailed) { @@ -2035,14 +2017,10 @@ BENCHFUN delete[] ccalc; } -//#ifdef _DEBUG if (settings->verbose) { t2e.set(); printf("Denoise performed in %d usec:\n", t2e.etime(t1e)); } - -//#endif - }//end of main RGB_denoise @@ -2194,7 +2172,7 @@ void ImProcFunctions::Noise_residualAB(const wavelet_decomposition &WaveletCoeff const int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl); const int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl); - float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); + const float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); const float madC = SQR(denoiseMethodRgb ? MadRgb(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab) : Mad(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab)); resid += madC; @@ -2209,7 +2187,7 @@ void ImProcFunctions::Noise_residualAB(const wavelet_decomposition &WaveletCoeff chmaxresid = maxresid; } -bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels) +bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(wavelet_decomposition& WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels) { int maxlvl = min(WaveletCoeffs_L.maxlevel(), 5); const float eps = 0.01f; @@ -2260,7 +2238,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &W int Wlvl_L = WaveletCoeffs_L.level_W(lvl); int Hlvl_L = WaveletCoeffs_L.level_H(lvl); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); + float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); if (lvl == maxlvl - 1) { // int edge = 0; @@ -2392,8 +2370,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &W } -bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels) - +bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels) { int maxlvl = WaveletCoeffs_L.maxlevel(); @@ -2449,7 +2426,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition & // compute median absolute deviation (MAD) of detail coefficients as robust noise estimator int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl); int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl); - float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); + const float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); if (!denoiseMethodRgb) { madab[lvl][dir - 1] = SQR(Mad(WavCoeffs_ab[dir], Wlvl_ab * Hlvl_ab)); @@ -2468,8 +2445,8 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition & int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl); int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); - float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); + float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); + float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); if (lvl == maxlvl - 1) { ShrinkAllAB(WaveletCoeffs_L, WaveletCoeffs_ab, buffer, lvl, dir, noisevarchrom, noisevar_ab, useNoiseCCurve, autoch, denoiseMethodRgb, madL[lvl], nullptr, 0, madab[lvl], true); @@ -2563,7 +2540,7 @@ bool ImProcFunctions::WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition & } -bool ImProcFunctions::WaveletDenoiseAllL(const wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels)//mod JD +bool ImProcFunctions::WaveletDenoiseAllL(wavelet_decomposition& WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels)//mod JD { @@ -2624,7 +2601,7 @@ bool ImProcFunctions::WaveletDenoiseAllL(const wavelet_decomposition &WaveletCoe } -bool ImProcFunctions::WaveletDenoiseAllAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab, +bool ImProcFunctions::WaveletDenoiseAllAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels)//mod JD { @@ -2688,7 +2665,7 @@ bool ImProcFunctions::WaveletDenoiseAllAB(const wavelet_decomposition &WaveletCo -void ImProcFunctions::ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, float **buffer, int level, int dir, +void ImProcFunctions::ShrinkAllL(wavelet_decomposition& WaveletCoeffs_L, float **buffer, int level, int dir, float *noisevarlum, float * madL, float * vari, int edge) { @@ -2702,7 +2679,7 @@ void ImProcFunctions::ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, f const int W_L = WaveletCoeffs_L.level_W(level); const int H_L = WaveletCoeffs_L.level_H(level); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level); + float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level); const float mad_L = madL[dir - 1] ; const float levelFactor = mad_L * 5.f / static_cast(level + 1); @@ -2779,7 +2756,7 @@ void ImProcFunctions::ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, f } -void ImProcFunctions::ShrinkAllAB(const wavelet_decomposition & WaveletCoeffs_L, const wavelet_decomposition & WaveletCoeffs_ab, float **buffer, int level, int dir, +void ImProcFunctions::ShrinkAllAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float **buffer, int level, int dir, float * noisevarchrom, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, float * madL, float * variC, int local, float * madaab, bool madCalculated) @@ -2798,8 +2775,8 @@ void ImProcFunctions::ShrinkAllAB(const wavelet_decomposition & WaveletCoeffs_L, int W_ab = WaveletCoeffs_ab.level_W(level); int H_ab = WaveletCoeffs_ab.level_H(level); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level); - float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(level); + float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(level); + float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(level); float madab; float mad_L = madL[dir - 1]; @@ -2918,7 +2895,7 @@ void ImProcFunctions::ShrinkAllAB(const wavelet_decomposition & WaveletCoeffs_L, delete [] nvc; } -void ImProcFunctions::ShrinkAll_info(float ** WavCoeffs_a, float ** WavCoeffs_b, +void ImProcFunctions::ShrinkAll_info(const float* const* WavCoeffs_a, const float* const* WavCoeffs_b, int W_ab, int H_ab, float **noisevarlum, float **noisevarchrom, float **noisevarhue, float & chaut, int &Nb, float & redaut, float & blueaut, float & maxredaut, float & maxblueaut, float & minredaut, float & minblueaut, int schoice, int lvl, float & chromina, float & sigma, float & lumema, float & sigma_L, float & redyel, float & skinc, float & nsknc, float & maxchred, float & maxchblue, float & minchred, float & minchblue, int &nb, float & chau, float & chred, float & chblue, bool denoiseMethodRgb) @@ -3048,8 +3025,8 @@ void ImProcFunctions::WaveletDenoiseAll_info(int levwav, const wavelet_decomposi int Wlvl_ab = WaveletCoeffs_a.level_W(lvl); int Hlvl_ab = WaveletCoeffs_a.level_H(lvl); - float ** WavCoeffs_a = WaveletCoeffs_a.level_coeffs(lvl); - float ** WavCoeffs_b = WaveletCoeffs_b.level_coeffs(lvl); + const float* const* WavCoeffs_a = WaveletCoeffs_a.level_coeffs(lvl); + const float* const* WavCoeffs_b = WaveletCoeffs_b.level_coeffs(lvl); ShrinkAll_info(WavCoeffs_a, WavCoeffs_b, Wlvl_ab, Hlvl_ab, noisevarlum, noisevarchrom, noisevarhue, chaut, Nb, redaut, blueaut, maxredaut, maxblueaut, minredaut, minblueaut, diff --git a/rtengine/ashift_dt.c b/rtengine/ashift_dt.c new file mode 100644 index 000000000..3894a1c1b --- /dev/null +++ b/rtengine/ashift_dt.c @@ -0,0 +1,5127 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Alberto Griggio + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +using namespace std; + +// taken from darktable (src/iop/ashift.c) +/* + This file is part of darktable, + copyright (c) 2016 Ulrich Pegelow. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ + +// Inspiration to this module comes from the program ShiftN (http://www.shiftn.de) by +// Marcus Hebel. + +// Thanks to Marcus for his support when implementing part of the ShiftN functionality +// to darktable. + +#define ROTATION_RANGE 10 // allowed min/max default range for rotation parameter +#define ROTATION_RANGE_SOFT 20 // allowed min/max range for rotation parameter with manual adjustment +#define LENSSHIFT_RANGE 0.5 // allowed min/max default range for lensshift parameters +#define LENSSHIFT_RANGE_SOFT 1 // allowed min/max range for lensshift parameters with manual adjustment +#define SHEAR_RANGE 0.2 // allowed min/max range for shear parameter +#define SHEAR_RANGE_SOFT 0.5 // allowed min/max range for shear parameter with manual adjustment +#define CAMERA_ANGLE_RANGE_SOFT 80 +#define MIN_LINE_LENGTH 5 // the minimum length of a line in pixels to be regarded as relevant +#define MAX_TANGENTIAL_DEVIATION 30 // by how many degrees a line may deviate from the +/-180 and +/-90 to be regarded as relevant +#define LSD_SCALE 0.99 // LSD: scaling factor for line detection +#define LSD_SIGMA_SCALE 0.6 // LSD: sigma for Gaussian filter is computed as sigma = sigma_scale/scale +#define LSD_QUANT 2.0 // LSD: bound to the quantization error on the gradient norm +#define LSD_ANG_TH 22.5 // LSD: gradient angle tolerance in degrees +#define LSD_LOG_EPS 0.0 // LSD: detection threshold: -log10(NFA) > log_eps +#define LSD_DENSITY_TH 0.7 // LSD: minimal density of region points in rectangle +#define LSD_N_BINS 1024 // LSD: number of bins in pseudo-ordering of gradient modulus +#define LSD_GAMMA 0.45 // gamma correction to apply on raw images prior to line detection +#define RANSAC_RUNS 400 // how many iterations to run in ransac +#define RANSAC_EPSILON 2 // starting value for ransac epsilon (in -log10 units) +#define RANSAC_EPSILON_STEP 1 // step size of epsilon optimization (log10 units) +#define RANSAC_ELIMINATION_RATIO 60 // percentage of lines we try to eliminate as outliers +#define RANSAC_OPTIMIZATION_STEPS 5 // home many steps to optimize epsilon +#define RANSAC_OPTIMIZATION_DRY_RUNS 50 // how man runs per optimization steps +#define RANSAC_HURDLE 5 // hurdle rate: the number of lines below which we do a complete permutation instead of random sampling +#define MINIMUM_FITLINES 4 // minimum number of lines needed for automatic parameter fit +#define NMS_EPSILON 1e-3 // break criterion for Nelder-Mead simplex +#define NMS_SCALE 1.0 // scaling factor for Nelder-Mead simplex +#define NMS_ITERATIONS 400 // number of iterations for Nelder-Mead simplex +#define NMS_CROP_EPSILON 100.0 // break criterion for Nelder-Mead simplex on crop fitting +#define NMS_CROP_SCALE 0.5 // scaling factor for Nelder-Mead simplex on crop fitting +#define NMS_CROP_ITERATIONS 100 // number of iterations for Nelder-Mead simplex on crop fitting +#define NMS_ALPHA 1.0 // reflection coefficient for Nelder-Mead simplex +#define NMS_BETA 0.5 // contraction coefficient for Nelder-Mead simplex +#define NMS_GAMMA 2.0 // expansion coefficient for Nelder-Mead simplex +#define DEFAULT_F_LENGTH 28.0 // focal length we assume if no exif data are available + +/* // define to get debugging output */ +/* #undef ASHIFT_DEBUG */ + +#define SQR(a) ((a) * (a)) + +// For line detection we use the LSD algorithm as published by Rafael Grompone: +// +// "LSD: a Line Segment Detector" by Rafael Grompone von Gioi, +// Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall, +// Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd +// http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd +#include "ashift_lsd.c" + +// For parameter optimization we are using the Nelder-Mead simplex method +// implemented by Michael F. Hutt. +#include "ashift_nmsimplex.c" + +#include "homogeneouscoordinates.h" + + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +DT_MODULE_INTROSPECTION(4, dt_iop_ashift_params_t) + + +const char *name() +{ + return _("perspective correction"); +} + +int flags() +{ + return IOP_FLAGS_ALLOW_TILING | IOP_FLAGS_TILING_FULL_ROI | IOP_FLAGS_ONE_INSTANCE; +} + +int groups() +{ + return dt_iop_get_group("perspective correction", IOP_GROUP_CORRECT); +} + +int operation_tags() +{ + return IOP_TAG_DISTORT; +} + +int operation_tags_filter() +{ + // switch off clipping and decoration, we want to see the full image. + return IOP_TAG_DECORATION | IOP_TAG_CLIPPING; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +typedef enum dt_iop_ashift_homodir_t +{ + ASHIFT_HOMOGRAPH_FORWARD, + ASHIFT_HOMOGRAPH_INVERTED +} dt_iop_ashift_homodir_t; + +//typedef enum dt_iop_ashift_linetype_t +enum +{ + ASHIFT_LINE_IRRELEVANT = 0, // the line is found to be not interesting + // eg. too short, or not horizontal or vertical + ASHIFT_LINE_RELEVANT = 1 << 0, // the line is relevant for us + ASHIFT_LINE_DIRVERT = 1 << 1, // the line is (mostly) vertical, else (mostly) horizontal + ASHIFT_LINE_SELECTED = 1 << 2, // the line is selected for fitting + ASHIFT_LINE_VERTICAL_NOT_SELECTED = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_DIRVERT, + ASHIFT_LINE_HORIZONTAL_NOT_SELECTED = ASHIFT_LINE_RELEVANT, + ASHIFT_LINE_VERTICAL_SELECTED = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_DIRVERT | ASHIFT_LINE_SELECTED, + ASHIFT_LINE_HORIZONTAL_SELECTED = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED, + ASHIFT_LINE_MASK = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_DIRVERT | ASHIFT_LINE_SELECTED +}; //dt_iop_ashift_linetype_t; +typedef unsigned int dt_iop_ashift_linetype_t; + +typedef enum dt_iop_ashift_linecolor_t +{ + ASHIFT_LINECOLOR_GREY = 0, + ASHIFT_LINECOLOR_GREEN = 1, + ASHIFT_LINECOLOR_RED = 2, + ASHIFT_LINECOLOR_BLUE = 3, + ASHIFT_LINECOLOR_YELLOW = 4 +} dt_iop_ashift_linecolor_t; + +//typedef enum dt_iop_ashift_fitaxis_t +enum +{ + ASHIFT_FIT_NONE = 0, // none + ASHIFT_FIT_ROTATION = 1 << 0, // flag indicates to fit rotation angle + ASHIFT_FIT_LENS_VERT = 1 << 1, // flag indicates to fit vertical lens shift + ASHIFT_FIT_LENS_HOR = 1 << 2, // flag indicates to fit horizontal lens shift + ASHIFT_FIT_SHEAR = 1 << 3, // flag indicates to fit shear parameter + ASHIFT_FIT_LINES_VERT = 1 << 4, // use vertical lines for fitting + ASHIFT_FIT_LINES_HOR = 1 << 5, // use horizontal lines for fitting + ASHIFT_FIT_LENS_BOTH = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR, + ASHIFT_FIT_LINES_BOTH = ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_VERTICALLY = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LINES_VERT, + ASHIFT_FIT_HORIZONTALLY = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_HOR | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_BOTH = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR | + ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_VERTICALLY_NO_ROTATION = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LINES_VERT, + ASHIFT_FIT_HORIZONTALLY_NO_ROTATION = ASHIFT_FIT_LENS_HOR | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_BOTH_NO_ROTATION = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR | + ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_BOTH_SHEAR = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR | + ASHIFT_FIT_SHEAR | ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_ROTATION_VERTICAL_LINES = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LINES_VERT, + ASHIFT_FIT_ROTATION_HORIZONTAL_LINES = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_ROTATION_BOTH_LINES = ASHIFT_FIT_ROTATION | ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR, + ASHIFT_FIT_FLIP = ASHIFT_FIT_LENS_VERT | ASHIFT_FIT_LENS_HOR | ASHIFT_FIT_LINES_VERT | ASHIFT_FIT_LINES_HOR +}; //dt_iop_ashift_fitaxis_t; +typedef unsigned int dt_iop_ashift_fitaxis_t; + +typedef enum dt_iop_ashift_nmsresult_t +{ + NMS_SUCCESS = 0, + NMS_NOT_ENOUGH_LINES = 1, + NMS_DID_NOT_CONVERGE = 2, + NMS_INSANE = 3 +} dt_iop_ashift_nmsresult_t; + +typedef enum dt_iop_ashift_enhance_t +{ + ASHIFT_ENHANCE_NONE = 0, + ASHIFT_ENHANCE_EDGES = 1 << 0, + ASHIFT_ENHANCE_DETAIL = 1 << 1, + ASHIFT_ENHANCE_HORIZONTAL = 0x100, + ASHIFT_ENHANCE_VERTICAL = 0x200 +} dt_iop_ashift_enhance_t; + +typedef enum dt_iop_ashift_mode_t +{ + ASHIFT_MODE_GENERIC = 0, + ASHIFT_MODE_SPECIFIC = 1 +} dt_iop_ashift_mode_t; + +typedef enum dt_iop_ashift_crop_t +{ + ASHIFT_CROP_OFF = 0, + ASHIFT_CROP_LARGEST = 1, + ASHIFT_CROP_ASPECT = 2 +} dt_iop_ashift_crop_t; + +typedef enum dt_iop_ashift_bounding_t +{ + ASHIFT_BOUNDING_OFF = 0, + ASHIFT_BOUNDING_SELECT = 1, + ASHIFT_BOUNDING_DESELECT = 2 +} dt_iop_ashift_bounding_t; + +typedef enum dt_iop_ashift_jobcode_t +{ + ASHIFT_JOBCODE_NONE = 0, + ASHIFT_JOBCODE_GET_STRUCTURE = 1, + ASHIFT_JOBCODE_FIT = 2 +} dt_iop_ashift_jobcode_t; + +typedef struct dt_iop_ashift_params1_t +{ + float rotation; + float lensshift_v; + float lensshift_h; + int toggle; +} dt_iop_ashift_params1_t; + +typedef struct dt_iop_ashift_params2_t +{ + float rotation; + float lensshift_v; + float lensshift_h; + float f_length; + float crop_factor; + float orthocorr; + float aspect; + dt_iop_ashift_mode_t mode; + int toggle; +} dt_iop_ashift_params2_t; + +typedef struct dt_iop_ashift_params3_t +{ + float rotation; + float lensshift_v; + float lensshift_h; + float f_length; + float crop_factor; + float orthocorr; + float aspect; + dt_iop_ashift_mode_t mode; + int toggle; + dt_iop_ashift_crop_t cropmode; + float cl; + float cr; + float ct; + float cb; +} dt_iop_ashift_params3_t; + +typedef struct dt_iop_ashift_params_t +{ + float rotation; + float lensshift_v; + float lensshift_h; + float shear; + float f_length; + float crop_factor; + float orthocorr; + float aspect; + dt_iop_ashift_mode_t mode; + int toggle; + dt_iop_ashift_crop_t cropmode; + float cl; + float cr; + float ct; + float cb; + float camera_pitch; + float camera_yaw; +} dt_iop_ashift_params_t; + +typedef struct dt_iop_ashift_line_t +{ + float p1[3]; + float p2[3]; + float length; + float width; + float weight; + dt_iop_ashift_linetype_t type; + // homogeneous coordinates: + float L[3]; +} dt_iop_ashift_line_t; + +typedef struct dt_iop_ashift_points_idx_t +{ + size_t offset; + int length; + int near; + int bounded; + dt_iop_ashift_linetype_t type; + dt_iop_ashift_linecolor_t color; + // bounding box: + float bbx, bby, bbX, bbY; +} dt_iop_ashift_points_idx_t; + +typedef struct dt_iop_ashift_fit_params_t +{ + int params_count; + dt_iop_ashift_linetype_t linetype; + dt_iop_ashift_linetype_t linemask; + dt_iop_ashift_line_t *lines; + int lines_count; + int width; + int height; + float weight; + float f_length_kb; + float orthocorr; + float aspect; + float rotation; + float lensshift_v; + float lensshift_h; + float shear; + float camera_pitch; + float camera_yaw; + float rotation_range; + float lensshift_v_range; + float lensshift_h_range; + float shear_range; + float camera_pitch_range; + float camera_yaw_range; +} dt_iop_ashift_fit_params_t; + +typedef struct dt_iop_ashift_cropfit_params_t +{ + int width; + int height; + float x; + float y; + float alpha; + float homograph[3][3]; + float edges[4][3]; +} dt_iop_ashift_cropfit_params_t; + +typedef struct dt_iop_ashift_gui_data_t +{ + /* GtkWidget *rotation; */ + /* GtkWidget *lensshift_v; */ + /* GtkWidget *lensshift_h; */ + /* GtkWidget *shear; */ + /* GtkWidget *guide_lines; */ + /* GtkWidget *cropmode; */ + /* GtkWidget *mode; */ + /* GtkWidget *f_length; */ + /* GtkWidget *crop_factor; */ + /* GtkWidget *orthocorr; */ + /* GtkWidget *aspect; */ + /* GtkWidget *fit_v; */ + /* GtkWidget *fit_h; */ + /* GtkWidget *fit_both; */ + /* GtkWidget *structure; */ + /* GtkWidget *clean; */ + /* GtkWidget *eye; */ + int lines_suppressed; + int fitting; + int isflipped; + int show_guides; + int isselecting; + int isdeselecting; + dt_iop_ashift_bounding_t isbounding; + float near_delta; + int selecting_lines_version; + float rotation_range; + float lensshift_v_range; + float lensshift_h_range; + float shear_range; + float camera_pitch_range; + float camera_yaw_range; + dt_iop_ashift_line_t *lines; + int lines_in_width; + int lines_in_height; + int lines_x_off; + int lines_y_off; + int lines_count; + int vertical_count; + int horizontal_count; + int lines_version; + float vertical_weight; + float horizontal_weight; + float *points; + dt_iop_ashift_points_idx_t *points_idx; + int points_lines_count; + int points_version; + float *buf; + int buf_width; + int buf_height; + int buf_x_off; + int buf_y_off; + float buf_scale; + uint64_t lines_hash; + uint64_t grid_hash; + uint64_t buf_hash; + dt_iop_ashift_fitaxis_t lastfit; + float lastx; + float lasty; + float crop_cx; + float crop_cy; + dt_iop_ashift_jobcode_t jobcode; + int jobparams; + /* dt_pthread_mutex_t lock; */ + MyMutex lock; + gboolean adjust_crop; +} dt_iop_ashift_gui_data_t; + +typedef struct dt_iop_ashift_data_t +{ + float rotation; + float lensshift_v; + float lensshift_h; + float shear; + float f_length_kb; + float orthocorr; + float aspect; + float cl; + float cr; + float ct; + float cb; +} dt_iop_ashift_data_t; + +typedef struct dt_iop_ashift_global_data_t +{ + int kernel_ashift_bilinear; + int kernel_ashift_bicubic; + int kernel_ashift_lanczos2; + int kernel_ashift_lanczos3; +} dt_iop_ashift_global_data_t; + +typedef struct dt_iop_module_t +{ + dt_iop_ashift_gui_data_t *gui_data; + int is_raw; +} dt_iop_module_t; + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, + void *new_params, const int new_version) +{ + if(old_version == 1 && new_version == 4) + { + const dt_iop_ashift_params1_t *old = old_params; + dt_iop_ashift_params_t *new = new_params; + new->rotation = old->rotation; + new->lensshift_v = old->lensshift_v; + new->lensshift_h = old->lensshift_h; + new->shear = 0.0f; + new->toggle = old->toggle; + new->f_length = DEFAULT_F_LENGTH; + new->crop_factor = 1.0f; + new->orthocorr = 100.0f; + new->aspect = 1.0f; + new->mode = ASHIFT_MODE_GENERIC; + new->cropmode = ASHIFT_CROP_OFF; + new->cl = 0.0f; + new->cr = 1.0f; + new->ct = 0.0f; + new->cb = 1.0f; + return 0; + } + if(old_version == 2 && new_version == 4) + { + const dt_iop_ashift_params2_t *old = old_params; + dt_iop_ashift_params_t *new = new_params; + new->rotation = old->rotation; + new->lensshift_v = old->lensshift_v; + new->lensshift_h = old->lensshift_h; + new->shear = 0.0f; + new->toggle = old->toggle; + new->f_length = old->f_length; + new->crop_factor = old->crop_factor; + new->orthocorr = old->orthocorr; + new->aspect = old->aspect; + new->mode = old->mode; + new->cropmode = ASHIFT_CROP_OFF; + new->cl = 0.0f; + new->cr = 1.0f; + new->ct = 0.0f; + new->cb = 1.0f; + return 0; + } + if(old_version == 3 && new_version == 4) + { + const dt_iop_ashift_params3_t *old = old_params; + dt_iop_ashift_params_t *new = new_params; + new->rotation = old->rotation; + new->lensshift_v = old->lensshift_v; + new->lensshift_h = old->lensshift_h; + new->shear = 0.0f; + new->toggle = old->toggle; + new->f_length = old->f_length; + new->crop_factor = old->crop_factor; + new->orthocorr = old->orthocorr; + new->aspect = old->aspect; + new->mode = old->mode; + new->cropmode = old->cropmode; + new->cl = old->cl; + new->cr = old->cr; + new->ct = old->ct; + new->cb = old->cb; + return 0; + } + + return 1; +} + +void init_key_accels(dt_iop_module_so_t *self) +{ + dt_accel_register_slider_iop(self, FALSE, NC_("accel", "rotation")); + dt_accel_register_slider_iop(self, FALSE, NC_("accel", "lens shift (v)")); + dt_accel_register_slider_iop(self, FALSE, NC_("accel", "lens shift (h)")); + dt_accel_register_slider_iop(self, FALSE, NC_("accel", "shear")); +} + +void connect_key_accels(dt_iop_module_t *self) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + dt_accel_connect_slider_iop(self, "rotation", GTK_WIDGET(g->rotation)); + dt_accel_connect_slider_iop(self, "lens shift (v)", GTK_WIDGET(g->lensshift_v)); + dt_accel_connect_slider_iop(self, "lens shift (h)", GTK_WIDGET(g->lensshift_h)); + dt_accel_connect_slider_iop(self, "shear", GTK_WIDGET(g->shear)); +} +#endif // if 0 +//----------------------------------------------------------------------------- + +// multiply 3x3 matrix with 3x1 vector +// dst needs to be different from v +static inline void mat3mulv(float *dst, const float *const mat, const float *const v) +{ + for(int k = 0; k < 3; k++) + { + float x = 0.0f; + for(int i = 0; i < 3; i++) x += mat[3 * k + i] * v[i]; + dst[k] = x; + } +} + +// multiply two 3x3 matrices +// dst needs to be different from m1 and m2 +static inline void mat3mul(float *dst, const float *const m1, const float *const m2) +{ + for(int k = 0; k < 3; k++) + { + for(int i = 0; i < 3; i++) + { + float x = 0.0f; + for(int j = 0; j < 3; j++) x += m1[3 * k + j] * m2[3 * j + i]; + dst[3 * k + i] = x; + } + } +} + +// normalized product of two 3x1 vectors +// dst needs to be different from v1 and v2 +static inline void vec3prodn(float *dst, const float *const v1, const float *const v2) +{ + const float l1 = v1[1] * v2[2] - v1[2] * v2[1]; + const float l2 = v1[2] * v2[0] - v1[0] * v2[2]; + const float l3 = v1[0] * v2[1] - v1[1] * v2[0]; + + // normalize so that l1^2 + l2^2 + l3^3 = 1 + const float sq = sqrt(l1 * l1 + l2 * l2 + l3 * l3); + + const float f = sq > 0.0f ? 1.0f / sq : 1.0f; + + dst[0] = l1 * f; + dst[1] = l2 * f; + dst[2] = l3 * f; +} + +// normalize a 3x1 vector so that x^2 + y^2 + z^2 = 1 +// dst and v may be the same +static inline void vec3norm(float *dst, const float *const v) +{ + const float sq = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + + // special handling for an all-zero vector + const float f = sq > 0.0f ? 1.0f / sq : 1.0f; + + dst[0] = v[0] * f; + dst[1] = v[1] * f; + dst[2] = v[2] * f; +} + +// normalize a 3x1 vector so that x^2 + y^2 = 1; a useful normalization for +// lines in homogeneous coordinates +// dst and v may be the same +static inline void vec3lnorm(float *dst, const float *const v) +{ + const float sq = sqrt(v[0] * v[0] + v[1] * v[1]); + + // special handling for a point vector of the image center + const float f = sq > 0.0f ? 1.0f / sq : 1.0f; + + dst[0] = v[0] * f; + dst[1] = v[1] * f; + dst[2] = v[2] * f; +} + + +// scalar product of two 3x1 vectors +static inline float vec3scalar(const float *const v1, const float *const v2) +{ + return (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]); +} + +// check if 3x1 vector is (very close to) null +static inline int vec3isnull(const float *const v) +{ + const float eps = 1e-10f; + return (fabs(v[0]) < eps && fabs(v[1]) < eps && fabs(v[2]) < eps); +} + +#ifdef ASHIFT_DEBUG +static void print_roi(const dt_iop_roi_t *roi, const char *label) +{ + printf("{ %5d %5d %5d %5d %.6f } %s\n", roi->x, roi->y, roi->width, roi->height, roi->scale, label); +} +#endif + +#define MAT3SWAP(a, b) { float (*tmp)[3] = (a); (a) = (b); (b) = tmp; } + +/* +static void homography(float *homograph, const float angle, const float shift_v, const float shift_h, + const float shear, const float f_length_kb, const float orthocorr, const float aspect, + const int width, const int height, dt_iop_ashift_homodir_t dir) +*/ +static void homography(float *homograph, const float angle, const float shift_v, + const float shift_h, const float shear, const float camera_pitch, const + float camera_yaw, const float f_length_kb, const float orthocorr, const + float aspect, const int width, const int height, dt_iop_ashift_homodir_t + dir) +{ + // calculate homograph that combines all translations, rotations + // and warping into one single matrix operation. + // this is heavily leaning on ShiftN where the homographic matrix expects + // input in (y : x : 1) format. in the darktable world we want to keep the + // (x : y : 1) convention. therefore we need to flip coordinates first and + // make sure that output is in correct format after corrections are applied. + + const float u = width; + const float v = height; + const float rot = -M_PI * angle / 180.0f; + const float pitch = M_PI * camera_pitch / 180.0f; + const float yaw = M_PI * camera_yaw / 180.0f; + + /* + const float phi = M_PI * angle / 180.0f; + const float cosi = cos(phi); + const float sini = sin(phi); + const float ascale = sqrt(aspect); + + // most of this comes from ShiftN + const float f_global = f_length_kb; + const float horifac = 1.0f - orthocorr / 100.0f; + const float exppa_v = exp(shift_v); + const float fdb_v = f_global / (14.4f + (v / u - 1) * 7.2f); + const float rad_v = fdb_v * (exppa_v - 1.0f) / (exppa_v + 1.0f); + const float alpha_v = CLAMP(atan(rad_v), -1.5f, 1.5f); + const float rt_v = sin(0.5f * alpha_v); + const float r_v = fmax(0.1f, 2.0f * (horifac - 1.0f) * rt_v * rt_v + 1.0f); + + const float vertifac = 1.0f - orthocorr / 100.0f; + const float exppa_h = exp(shift_h); + const float fdb_h = f_global / (14.4f + (u / v - 1) * 7.2f); + const float rad_h = fdb_h * (exppa_h - 1.0f) / (exppa_h + 1.0f); + const float alpha_h = CLAMP(atan(rad_h), -1.5f, 1.5f); + const float rt_h = sin(0.5f * alpha_h); + const float r_h = fmax(0.1f, 2.0f * (vertifac - 1.0f) * rt_h * rt_h + 1.0f); + */ + + const float f = f_length_kb * (sqrt(u*u + v*v) / sqrt(36.0*36.0 + 24.0*24.0)); + + // three intermediate buffers for matrix calculation ... + float m1[3][3]/*, m2[3][3]*/, m3[3][3]; + + // ... and some pointers to handle them more intuitively + float (*mwork)[3] = m1; + //float (*minput)[3] = m2; + float (*moutput)[3] = m3; + + /* + // Step 1: flip x and y coordinates (see above) + memset(minput, 0, 9 * sizeof(float)); + minput[0][1] = 1.0f; + minput[1][0] = 1.0f; + minput[2][2] = 1.0f; + + + // Step 2: rotation of image around its center + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = cosi; + mwork[0][1] = -sini; + mwork[1][0] = sini; + mwork[1][1] = cosi; + mwork[0][2] = -0.5f * v * cosi + 0.5f * u * sini + 0.5f * v; + mwork[1][2] = -0.5f * v * sini - 0.5f * u * cosi + 0.5f * u; + mwork[2][2] = 1.0f; + + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // Step 3: apply shearing + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = 1.0f; + mwork[0][1] = shear; + mwork[1][1] = 1.0f; + mwork[1][0] = shear; + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // Step 4: apply vertical lens shift effect + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = exppa_v; + mwork[1][0] = 0.5f * ((exppa_v - 1.0f) * u) / v; + mwork[1][1] = 2.0f * exppa_v / (exppa_v + 1.0f); + mwork[1][2] = -0.5f * ((exppa_v - 1.0f) * u) / (exppa_v + 1.0f); + mwork[2][0] = (exppa_v - 1.0f) / v; + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // Step 5: horizontal compression + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = 1.0f; + mwork[1][1] = r_v; + mwork[1][2] = 0.5f * u * (1.0f - r_v); + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // Step 6: flip x and y back again + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][1] = 1.0f; + mwork[1][0] = 1.0f; + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // from here output vectors would be in (x : y : 1) format + + // Step 7: now we can apply horizontal lens shift with the same matrix format as above + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = exppa_h; + mwork[1][0] = 0.5f * ((exppa_h - 1.0f) * v) / u; + mwork[1][1] = 2.0f * exppa_h / (exppa_h + 1.0f); + mwork[1][2] = -0.5f * ((exppa_h - 1.0f) * v) / (exppa_h + 1.0f); + mwork[2][0] = (exppa_h - 1.0f) / u; + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // Step 8: vertical compression + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = 1.0f; + mwork[1][1] = r_h; + mwork[1][2] = 0.5f * v * (1.0f - r_h); + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + + + // Step 9: apply aspect ratio scaling + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = 1.0f * ascale; + mwork[1][1] = 1.0f / ascale; + mwork[2][2] = 1.0f; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + */ + + rtengine::homogeneous::Vector center; + center[0] = 0.0f; + center[1] = 0.0f; + center[2] = f; + center[3] = 1.0f; + + using rtengine::operator*; + + // Location of image center after rotations. + const rtengine::homogeneous::Vector camera_center_yaw_pitch = + rtengine::homogeneous::rotationMatrix(pitch, rtengine::homogeneous::Axis::X) * + rtengine::homogeneous::rotationMatrix(yaw, rtengine::homogeneous::Axis::Y) * + center; + + const rtengine::homogeneous::Matrix matrix = + // Perspective correction. + rtengine::homogeneous::projectionMatrix(camera_center_yaw_pitch[2], rtengine::homogeneous::Axis::Z) * + rtengine::homogeneous::rotationMatrix(yaw, rtengine::homogeneous::Axis::Y) * + rtengine::homogeneous::rotationMatrix(pitch, rtengine::homogeneous::Axis::X) * + // Rotation. + rtengine::homogeneous::rotationMatrix(rot, rtengine::homogeneous::Axis::Z) * + // Lens/sensor shift and move to z == camera_focal_length. + rtengine::homogeneous::translationMatrix((0.01f * shift_h - 0.5f) * u, (-0.01f * shift_v - 0.5f) * v, f); + + m3[0][0] = matrix[0][0]; + m3[0][1] = matrix[0][1]; + m3[0][2] = matrix[0][3]; + m3[1][0] = matrix[1][0]; + m3[1][1] = matrix[1][1]; + m3[1][2] = matrix[1][3]; + m3[2][0] = matrix[3][0]; + m3[2][1] = matrix[3][1]; + m3[2][2] = matrix[3][3]; + + /* + // Step 10: find x/y offsets and apply according correction so that + // no negative coordinates occur in output vector + float umin = FLT_MAX, vmin = FLT_MAX; + // visit all four corners + for(int y = 0; y < height; y += height - 1) + for(int x = 0; x < width; x += width - 1) + { + float pi[3], po[3]; + pi[0] = x; + pi[1] = y; + pi[2] = 1.0f; + // moutput expects input in (x:y:1) format and gives output as (x:y:1) + mat3mulv(po, (float *)moutput, pi); + umin = fmin(umin, po[0] / po[2]); + vmin = fmin(vmin, po[1] / po[2]); + } + + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = 1.0f; + mwork[1][1] = 1.0f; + mwork[2][2] = 1.0f; + mwork[0][2] = -umin; + mwork[1][2] = -vmin; + + // moutput (of last calculation) -> minput + MAT3SWAP(minput, moutput); + // multiply mwork * minput -> moutput + mat3mul((float *)moutput, (float *)mwork, (float *)minput); + */ + + + // on request we either keep the final matrix for forward conversions + // or produce an inverted matrix for backward conversions + if(dir == ASHIFT_HOMOGRAPH_FORWARD) + { + // we have what we need -> copy it to the right place + memcpy(homograph, moutput, 9 * sizeof(float)); + } + else + { + // generate inverted homograph (mat3inv function defined in colorspaces.c) + if(mat3inv((float *)homograph, (float *)moutput)) + { + // in case of error we set to unity matrix + memset(mwork, 0, 9 * sizeof(float)); + mwork[0][0] = 1.0f; + mwork[1][1] = 1.0f; + mwork[2][2] = 1.0f; + memcpy(homograph, mwork, 9 * sizeof(float)); + } + } +} +#undef MAT3SWAP + + +// check if module parameters are set to all neutral values in which case the module's +// output is identical to its input +static inline int isneutral(dt_iop_ashift_data_t *data) +{ + // values lower than this have no visible effect + const float eps = 1.0e-4f; + + return(fabs(data->rotation) < eps && + fabs(data->lensshift_v) < eps && + fabs(data->lensshift_h) < eps && + fabs(data->shear) < eps && + fabs(data->aspect - 1.0f) < eps && + data->cl < eps && + 1.0f - data->cr < eps && + data->ct < eps && + 1.0f - data->cb < eps); +} + + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +int distort_transform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points, size_t points_count) +{ + dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data; + + // nothing to be done if parameters are set to neutral values + if(isneutral(data)) return 1; + + float homograph[3][3]; + homography((float *)homograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb, + data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_FORWARD); + + // clipping offset + const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl); + const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct); + const float cx = fullwidth * data->cl; + const float cy = fullheight * data->ct; + +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(points, points_count, homograph) +#endif + for(size_t i = 0; i < points_count * 2; i += 2) + { + float pi[3] = { points[i], points[i + 1], 1.0f }; + float po[3]; + mat3mulv(po, (float *)homograph, pi); + points[i] = po[0] / po[2] - cx; + points[i + 1] = po[1] / po[2] - cy; + } + + return 1; +} + + +int distort_backtransform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points, + size_t points_count) +{ + dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data; + + // nothing to be done if parameters are set to neutral values + if(isneutral(data)) return 1; + + float ihomograph[3][3]; + homography((float *)ihomograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb, + data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED); + + // clipping offset + const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl); + const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct); + const float cx = fullwidth * data->cl; + const float cy = fullheight * data->ct; + +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(points, points_count, ihomograph) +#endif + for(size_t i = 0; i < points_count * 2; i += 2) + { + float pi[3] = { points[i] + cx, points[i + 1] + cy, 1.0f }; + float po[3]; + mat3mulv(po, (float *)ihomograph, pi); + points[i] = po[0] / po[2]; + points[i + 1] = po[1] / po[2]; + } + + return 1; +} + +void modify_roi_out(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, + const dt_iop_roi_t *roi_in) +{ + dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data; + *roi_out = *roi_in; + + // nothing more to be done if parameters are set to neutral values + if(isneutral(data)) return; + + float homograph[3][3]; + homography((float *)homograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb, + data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_FORWARD); + + float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX; + + // go through all four vertices of input roi and convert coordinates to output + for(int y = 0; y < roi_in->height; y += roi_in->height - 1) + { + for(int x = 0; x < roi_in->width; x += roi_in->width - 1) + { + float pin[3], pout[3]; + + // convert from input coordinates to original image coordinates + pin[0] = roi_in->x + x; + pin[1] = roi_in->y + y; + pin[0] /= roi_in->scale; + pin[1] /= roi_in->scale; + pin[2] = 1.0f; + + // apply homograph + mat3mulv(pout, (float *)homograph, pin); + + // convert to output image coordinates + pout[0] /= pout[2]; + pout[1] /= pout[2]; + pout[0] *= roi_out->scale; + pout[1] *= roi_out->scale; + xm = MIN(xm, pout[0]); + xM = MAX(xM, pout[0]); + ym = MIN(ym, pout[1]); + yM = MAX(yM, pout[1]); + } + } + float width = xM - xm + 1; + float height = yM - ym + 1; + + // clipping adjustments + width *= data->cr - data->cl; + height *= data->cb - data->ct; + + roi_out->width = floorf(width); + roi_out->height = floorf(height); + +#ifdef ASHIFT_DEBUG + print_roi(roi_in, "roi_in (going into modify_roi_out)"); + print_roi(roi_out, "roi_out (after modify_roi_out)"); +#endif +} + +void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, + const dt_iop_roi_t *const roi_out, dt_iop_roi_t *roi_in) +{ + dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data; + *roi_in = *roi_out; + + // nothing more to be done if parameters are set to neutral values + if(isneutral(data)) return; + + float ihomograph[3][3]; + homography((float *)ihomograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb, + data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED); + + const float orig_w = roi_in->scale * piece->buf_in.width; + const float orig_h = roi_in->scale * piece->buf_in.height; + + // clipping offset + const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl); + const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct); + const float cx = roi_out->scale * fullwidth * data->cl; + const float cy = roi_out->scale * fullheight * data->ct; + + float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX; + + // go through all four vertices of output roi and convert coordinates to input + for(int y = 0; y < roi_out->height; y += roi_out->height - 1) + { + for(int x = 0; x < roi_out->width; x += roi_out->width - 1) + { + float pin[3], pout[3]; + + // convert from output image coordinates to original image coordinates + pout[0] = roi_out->x + x + cx; + pout[1] = roi_out->y + y + cy; + pout[0] /= roi_out->scale; + pout[1] /= roi_out->scale; + pout[2] = 1.0f; + + // apply homograph + mat3mulv(pin, (float *)ihomograph, pout); + + // convert to input image coordinates + pin[0] /= pin[2]; + pin[1] /= pin[2]; + pin[0] *= roi_in->scale; + pin[1] *= roi_in->scale; + xm = MIN(xm, pin[0]); + xM = MAX(xM, pin[0]); + ym = MIN(ym, pin[1]); + yM = MAX(yM, pin[1]); + } + } + + const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF); + roi_in->x = fmaxf(0.0f, xm - interpolation->width); + roi_in->y = fmaxf(0.0f, ym - interpolation->width); + roi_in->width = fminf(ceilf(orig_w) - roi_in->x, xM - roi_in->x + 1 + interpolation->width); + roi_in->height = fminf(ceilf(orig_h) - roi_in->y, yM - roi_in->y + 1 + interpolation->width); + + // sanity check. + roi_in->x = CLAMP(roi_in->x, 0, (int)floorf(orig_w)); + roi_in->y = CLAMP(roi_in->y, 0, (int)floorf(orig_h)); + roi_in->width = CLAMP(roi_in->width, 1, (int)floorf(orig_w) - roi_in->x); + roi_in->height = CLAMP(roi_in->height, 1, (int)floorf(orig_h) - roi_in->y); +#ifdef ASHIFT_DEBUG + print_roi(roi_out, "roi_out (going into modify_roi_in)"); + print_roi(roi_in, "roi_in (after modify_roi_in)"); +#endif +} +#endif // if 0 +//----------------------------------------------------------------------------- + +// simple conversion of rgb image into greyscale variant suitable for line segment detection +// the lsd routines expect input as *double, roughly in the range [0.0; 256.0] +static void rgb2grey256(const float *in, double *out, const int width, const int height) +{ + const int ch = 4; + +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(in, out) +#endif + for(int j = 0; j < height; j++) + { + const float *inp = in + (size_t)ch * j * width; + double *outp = out + (size_t)j * width; + for(int i = 0; i < width; i++, inp += ch, outp++) + { + *outp = (0.3f * inp[0] + 0.59f * inp[1] + 0.11f * inp[2]) * 256.0; + } + } +} + +// sobel edge enhancement in one direction +static void edge_enhance_1d(const double *in, double *out, const int width, const int height, + dt_iop_ashift_enhance_t dir) +{ + // Sobel kernels for both directions + const double hkernel[3][3] = { { 1.0, 0.0, -1.0 }, { 2.0, 0.0, -2.0 }, { 1.0, 0.0, -1.0 } }; + const double vkernel[3][3] = { { 1.0, 2.0, 1.0 }, { 0.0, 0.0, 0.0 }, { -1.0, -2.0, -1.0 } }; + const int kwidth = 3; + const int khwidth = kwidth / 2; + + // select kernel + const double *kernel = (dir == ASHIFT_ENHANCE_HORIZONTAL) ? (const double *)hkernel : (const double *)vkernel; + +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(in, out, kernel) +#endif + // loop over image pixels and perform sobel convolution + for(int j = khwidth; j < height - khwidth; j++) + { + const double *inp = in + (size_t)j * width + khwidth; + double *outp = out + (size_t)j * width + khwidth; + for(int i = khwidth; i < width - khwidth; i++, inp++, outp++) + { + double sum = 0.0f; + for(int jj = 0; jj < kwidth; jj++) + { + const int k = jj * kwidth; + const int l = (jj - khwidth) * width; + for(int ii = 0; ii < kwidth; ii++) + { + sum += inp[l + ii - khwidth] * kernel[k + ii]; + } + } + *outp = sum; + } + } + +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(out) +#endif + // border fill in output buffer, so we don't get pseudo lines at image frame + for(int j = 0; j < height; j++) + for(int i = 0; i < width; i++) + { + double val = out[j * width + i]; + + if(j < khwidth) + val = out[(khwidth - j) * width + i]; + else if(j >= height - khwidth) + val = out[(j - khwidth) * width + i]; + else if(i < khwidth) + val = out[j * width + (khwidth - i)]; + else if(i >= width - khwidth) + val = out[j * width + (i - khwidth)]; + + out[j * width + i] = val; + + // jump over center of image + if(i == khwidth && j >= khwidth && j < height - khwidth) i = width - khwidth; + } +} + +// edge enhancement in both directions +static int edge_enhance(const double *in, double *out, const int width, const int height) +{ + double *Gx = NULL; + double *Gy = NULL; + + Gx = (double *)malloc((size_t)width * height * sizeof(double)); + if(Gx == NULL) goto error; + + Gy = (double *)malloc((size_t)width * height * sizeof(double)); + if(Gy == NULL) goto error; + + // perform edge enhancement in both directions + edge_enhance_1d(in, Gx, width, height, ASHIFT_ENHANCE_HORIZONTAL); + edge_enhance_1d(in, Gy, width, height, ASHIFT_ENHANCE_VERTICAL); + +// calculate absolute values +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(Gx, Gy, out) +#endif + for(size_t k = 0; k < (size_t)width * height; k++) + { + out[k] = sqrt(Gx[k] * Gx[k] + Gy[k] * Gy[k]); + } + + free(Gx); + free(Gy); + return TRUE; + +error: + if(Gx) free(Gx); + if(Gy) free(Gy); + return FALSE; +} + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +// XYZ -> sRGB matrix +static void XYZ_to_sRGB(const float *XYZ, float *sRGB) +{ + sRGB[0] = 3.1338561f * XYZ[0] - 1.6168667f * XYZ[1] - 0.4906146f * XYZ[2]; + sRGB[1] = -0.9787684f * XYZ[0] + 1.9161415f * XYZ[1] + 0.0334540f * XYZ[2]; + sRGB[2] = 0.0719453f * XYZ[0] - 0.2289914f * XYZ[1] + 1.4052427f * XYZ[2]; +} + +// sRGB -> XYZ matrix +static void sRGB_to_XYZ(const float *sRGB, float *XYZ) +{ + XYZ[0] = 0.4360747f * sRGB[0] + 0.3850649f * sRGB[1] + 0.1430804f * sRGB[2]; + XYZ[1] = 0.2225045f * sRGB[0] + 0.7168786f * sRGB[1] + 0.0606169f * sRGB[2]; + XYZ[2] = 0.0139322f * sRGB[0] + 0.0971045f * sRGB[1] + 0.7141733f * sRGB[2]; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +// detail enhancement via bilateral grid (function arguments in and out may represent identical buffers) +static int detail_enhance(const float *in, float *out, const int width, const int height) +{ + return TRUE; +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 + const float sigma_r = 5.0f; + const float sigma_s = fminf(width, height) * 0.02f; + const float detail = 10.0f; + + int success = TRUE; + + // we need to convert from RGB to Lab first; + // as colors don't matter we are safe to assume data to be sRGB + + // convert RGB input to Lab, use output buffer for intermediate storage +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(in, out) +#endif + for(int j = 0; j < height; j++) + { + const float *inp = in + (size_t)4 * j * width; + float *outp = out + (size_t)4 * j * width; + for(int i = 0; i < width; i++, inp += 4, outp += 4) + { + float XYZ[3]; + sRGB_to_XYZ(inp, XYZ); + dt_XYZ_to_Lab(XYZ, outp); + } + } + + // bilateral grid detail enhancement + dt_bilateral_t *b = dt_bilateral_init(width, height, sigma_s, sigma_r); + + if(b != NULL) + { + dt_bilateral_splat(b, out); + dt_bilateral_blur(b); + dt_bilateral_slice_to_output(b, out, out, detail); + dt_bilateral_free(b); + } + else + success = FALSE; + + // convert resulting Lab to RGB output +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(out) +#endif + for(int j = 0; j < height; j++) + { + float *outp = out + (size_t)4 * j * width; + for(int i = 0; i < width; i++, outp += 4) + { + float XYZ[3]; + dt_Lab_to_XYZ(outp, XYZ); + XYZ_to_sRGB(XYZ, outp); + } + } + + return success; +#endif // if 0 +//----------------------------------------------------------------------------- +} + +// apply gamma correction to RGB buffer (function arguments in and out may represent identical buffers) +static void gamma_correct(const float *in, float *out, const int width, const int height) +{ +#ifdef _OPENMP +#pragma omp parallel for schedule(static) shared(in, out) +#endif + for(int j = 0; j < height; j++) + { + const float *inp = in + (size_t)4 * j * width; + float *outp = out + (size_t)4 * j * width; + for(int i = 0; i < width; i++, inp += 4, outp += 4) + { + for(int c = 0; c < 3; c++) + outp[c] = powf(inp[c], LSD_GAMMA); + } + } +} + +// do actual line_detection based on LSD algorithm and return results according +// to this module's conventions +static int line_detect(float *in, const int width, const int height, const int x_off, const int y_off, + const float scale, dt_iop_ashift_line_t **alines, int *lcount, int *vcount, int *hcount, + float *vweight, float *hweight, dt_iop_ashift_enhance_t enhance, const int is_raw) +{ + double *greyscale = NULL; + double *lsd_lines = NULL; + dt_iop_ashift_line_t *ashift_lines = NULL; + + int vertical_count = 0; + int horizontal_count = 0; + float vertical_weight = 0.0f; + float horizontal_weight = 0.0f; + // + int lines_count; + // we count the lines that we really want to use + int lct = 0; + + // apply gamma correction if image is raw + if(is_raw) + { + gamma_correct(in, in, width, height); + } + + // if requested perform an additional detail enhancement step + if(enhance & ASHIFT_ENHANCE_DETAIL) + { + (void)detail_enhance(in, in, width, height); + } + + // allocate intermediate buffers + greyscale = (double *)malloc((size_t)width * height * sizeof(double)); + if(greyscale == NULL) goto error; + + // convert to greyscale image + rgb2grey256(in, greyscale, width, height); + + // if requested perform an additional edge enhancement step + if(enhance & ASHIFT_ENHANCE_EDGES) + { + (void)edge_enhance(greyscale, greyscale, width, height); + } + + // call the line segment detector LSD; + // LSD stores the number of found lines in lines_count. + // it returns structural details as vector 'double lines[7 * lines_count]' + lsd_lines = LineSegmentDetection(&lines_count, greyscale, width, height, + LSD_SCALE, LSD_SIGMA_SCALE, LSD_QUANT, + LSD_ANG_TH, LSD_LOG_EPS, LSD_DENSITY_TH, + LSD_N_BINS, NULL, NULL, NULL); + + if(lines_count > 0) + { + // aggregate lines data into our own structures + ashift_lines = (dt_iop_ashift_line_t *)malloc((size_t)lines_count * sizeof(dt_iop_ashift_line_t)); + if(ashift_lines == NULL) goto error; + + for(int n = 0; n < lines_count; n++) + { + float x1 = lsd_lines[n * 7 + 0]; + float y1 = lsd_lines[n * 7 + 1]; + float x2 = lsd_lines[n * 7 + 2]; + float y2 = lsd_lines[n * 7 + 3]; + + // check for lines running along image borders and skip them. + // these would likely be false-positives which could result + // from any kind of processing artifacts + if((fabs(x1 - x2) < 1 && fmax(x1, x2) < 2) || + (fabs(x1 - x2) < 1 && fmin(x1, x2) > width - 3) || + (fabs(y1 - y2) < 1 && fmax(y1, y2) < 2) || + (fabs(y1 - y2) < 1 && fmin(y1, y2) > height - 3)) + continue; + + // line position in absolute coordinates + float px1 = x_off + x1; + float py1 = y_off + y1; + float px2 = x_off + x2; + float py2 = y_off + y2; + + // scale back to input buffer + px1 /= scale; + py1 /= scale; + px2 /= scale; + py2 /= scale; + + // store as homogeneous coordinates + ashift_lines[lct].p1[0] = px1; + ashift_lines[lct].p1[1] = py1; + ashift_lines[lct].p1[2] = 1.0f; + ashift_lines[lct].p2[0] = px2; + ashift_lines[lct].p2[1] = py2; + ashift_lines[lct].p2[2] = 1.0f; + + // calculate homogeneous coordinates of connecting line (defined by the two points) + vec3prodn(ashift_lines[lct].L, ashift_lines[lct].p1, ashift_lines[lct].p2); + + // normalaze line coordinates so that x^2 + y^2 = 1 + // (this will always succeed as L is a real line connecting two real points) + vec3lnorm(ashift_lines[lct].L, ashift_lines[lct].L); + + // length and width of rectangle (see LSD) + ashift_lines[lct].length = sqrt((px2 - px1) * (px2 - px1) + (py2 - py1) * (py2 - py1)); + ashift_lines[lct].width = lsd_lines[n * 7 + 4] / scale; + + // ... and weight (= angle precision * length * width) + const float weight = lsd_lines[n * 7 + 5] * ashift_lines[lct].length * ashift_lines[lct].width; + ashift_lines[lct].weight = weight; + + + const float angle = atan2(py2 - py1, px2 - px1) / M_PI * 180.0f; + const int vertical = fabs(fabs(angle) - 90.0f) < MAX_TANGENTIAL_DEVIATION ? 1 : 0; + const int horizontal = fabs(fabs(fabs(angle) - 90.0f) - 90.0f) < MAX_TANGENTIAL_DEVIATION ? 1 : 0; + + const int relevant = ashift_lines[lct].length > MIN_LINE_LENGTH ? 1 : 0; + + // register type of line + dt_iop_ashift_linetype_t type = ASHIFT_LINE_IRRELEVANT; + if(vertical && relevant) + { + type = ASHIFT_LINE_VERTICAL_SELECTED; + vertical_count++; + vertical_weight += weight; + } + else if(horizontal && relevant) + { + type = ASHIFT_LINE_HORIZONTAL_SELECTED; + horizontal_count++; + horizontal_weight += weight; + } + ashift_lines[lct].type = type; + + // the next valid line + lct++; + } + } +#ifdef ASHIFT_DEBUG + printf("%d lines (vertical %d, horizontal %d, not relevant %d)\n", lines_count, vertical_count, + horizontal_count, lct - vertical_count - horizontal_count); + float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN; + for(int n = 0; n < lct; n++) + { + xmin = fmin(xmin, fmin(ashift_lines[n].p1[0], ashift_lines[n].p2[0])); + xmax = fmax(xmax, fmax(ashift_lines[n].p1[0], ashift_lines[n].p2[0])); + ymin = fmin(ymin, fmin(ashift_lines[n].p1[1], ashift_lines[n].p2[1])); + ymax = fmax(ymax, fmax(ashift_lines[n].p1[1], ashift_lines[n].p2[1])); + printf("x1 %.0f, y1 %.0f, x2 %.0f, y2 %.0f, length %.0f, width %f, X %f, Y %f, Z %f, type %d, scalars %f %f\n", + ashift_lines[n].p1[0], ashift_lines[n].p1[1], ashift_lines[n].p2[0], ashift_lines[n].p2[1], + ashift_lines[n].length, ashift_lines[n].width, + ashift_lines[n].L[0], ashift_lines[n].L[1], ashift_lines[n].L[2], ashift_lines[n].type, + vec3scalar(ashift_lines[n].p1, ashift_lines[n].L), + vec3scalar(ashift_lines[n].p2, ashift_lines[n].L)); + } + printf("xmin %.0f, xmax %.0f, ymin %.0f, ymax %.0f\n", xmin, xmax, ymin, ymax); +#endif + + // store results in provided locations + *lcount = lct; + *vcount = vertical_count; + *vweight = vertical_weight; + *hcount = horizontal_count; + *hweight = horizontal_weight; + *alines = ashift_lines; + + // free intermediate buffers + free(lsd_lines); + free(greyscale); + return lct > 0 ? TRUE : FALSE; + +error: + free(lsd_lines); + free(greyscale); + return FALSE; +} + +// get image from buffer, analyze for structure and save results +static int get_structure(dt_iop_module_t *module, dt_iop_ashift_enhance_t enhance) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + float *buffer = NULL; + int width = 0; + int height = 0; + int x_off = 0; + int y_off = 0; + float scale = 0.0f; + + { //dt_pthread_mutex_lock(&g->lock); + MyMutex::MyLock lock(g->lock); + + // read buffer data if they are available + if(g->buf != NULL) + { + width = g->buf_width; + height = g->buf_height; + x_off = g->buf_x_off; + y_off = g->buf_y_off; + scale = g->buf_scale; + + // create a temporary buffer to hold image data + buffer = (float *)malloc((size_t)width * height * 4 * sizeof(float)); + if(buffer != NULL) + memcpy(buffer, g->buf, (size_t)width * height * 4 * sizeof(float)); + } + } /* dt_pthread_mutex_unlock(&g->lock); */ + + if(buffer == NULL) goto error; + + // get rid of old structural data + g->lines_count = 0; + g->vertical_count = 0; + g->horizontal_count = 0; + free(g->lines); + g->lines = NULL; + + dt_iop_ashift_line_t *lines; + int lines_count; + int vertical_count; + int horizontal_count; + float vertical_weight; + float horizontal_weight; + + // get new structural data + if(!line_detect(buffer, width, height, x_off, y_off, scale, &lines, &lines_count, + &vertical_count, &horizontal_count, &vertical_weight, &horizontal_weight, + enhance, module->is_raw))//dt_image_is_raw(&module->dev->image_storage))) + goto error; + + // save new structural data + g->lines_in_width = width; + g->lines_in_height = height; + g->lines_x_off = x_off; + g->lines_y_off = y_off; + g->lines_count = lines_count; + g->vertical_count = vertical_count; + g->horizontal_count = horizontal_count; + g->vertical_weight = vertical_weight; + g->horizontal_weight = horizontal_weight; + g->lines_version++; + g->lines_suppressed = 0; + g->lines = lines; + + free(buffer); + return TRUE; + +error: + free(buffer); + return FALSE; +} + + +// swap two integer values +static inline void swap(int *a, int *b) +{ + int tmp = *a; + *a = *b; + *b = tmp; +} + +// do complete permutations +static int quickperm(int *a, int *p, const int N, int *i) +{ + if(*i >= N) return FALSE; + + p[*i]--; + int j = (*i % 2 == 1) ? p[*i] : 0; + swap(&a[j], &a[*i]); + *i = 1; + while(p[*i] == 0) + { + p[*i] = *i; + (*i)++; + } + return TRUE; +} + +// Fisher-Yates shuffle +static void shuffle(int *a, const int N) +{ + for(int i = 0; i < N; i++) + { + int j = i + rand() % (N - i); + swap(&a[j], &a[i]); + } +} + +// factorial function +static int fact(const int n) +{ + return (n == 1 ? 1 : n * fact(n - 1)); +} + +// We use a pseudo-RANSAC algorithm to elminiate ouliers from our set of lines. The +// original RANSAC works on linear optimization problems. Our model is nonlinear. We +// take advantage of the fact that lines interesting for our model are vantage lines +// that meet in one vantage point for each subset of lines (vertical/horizontal). +// Stragegy: we construct a model by (random) sampling within the subset of lines and +// calculate the vantage point. Then we check the "distance" of all other lines to the +// vantage point. The model that gives highest number of lines combined with the highest +// total weight and lowest overall "distance" wins. +// Disadvantage: compared to the original RANSAC we don't get any model parameters that +// we could use for the following NMS fit. +// Self-tuning: we optimize "epsilon", the hurdle rate to reject a line as an outlier, +// by a number of dry runs first. The target average percentage value of lines to eliminate as +// outliers (without judging on the quality of the model) is given by RANSAC_ELIMINATION_RATIO, +// note: the actual percentage of outliers removed in the final run will be lower because we +// will finally look for the best quality model with the optimized epsilon and that quality value also +// encloses the number of good lines +static void ransac(const dt_iop_ashift_line_t *lines, int *index_set, int *inout_set, + const int set_count, const float total_weight, const int xmin, const int xmax, + const int ymin, const int ymax) +{ + if(set_count < 3) return; + + const size_t set_size = set_count * sizeof(int); + int *best_set = (int *)malloc(set_size); + memcpy(best_set, index_set, set_size); + int *best_inout = (int *)calloc(1, set_size); + + float best_quality = 0.0f; + + // hurdle value epsilon for rejecting a line as an outlier will be self-tuning + // in a number of dry runs + float epsilon = pow(10.0f, -RANSAC_EPSILON); + float epsilon_step = RANSAC_EPSILON_STEP; + // some accounting variables for self-tuning + int lines_eliminated = 0; + int valid_runs = 0; + + // number of runs to optimize epsilon + const int optiruns = RANSAC_OPTIMIZATION_STEPS * RANSAC_OPTIMIZATION_DRY_RUNS; + // go for complete permutations on small set sizes, else for random sample consensus + const int riter = (set_count > RANSAC_HURDLE) ? RANSAC_RUNS : fact(set_count); + + // some data needed for quickperm + int *perm = (int *)malloc((set_count + 1) * sizeof(int)); + for(int n = 0; n < set_count + 1; n++) perm[n] = n; + int piter = 1; + + // inout holds good/bad qualification for each line + int *inout = (int *)malloc(set_size); + + for(int r = 0; r < optiruns + riter; r++) + { + // get random or systematic variation of index set + if(set_count > RANSAC_HURDLE || r < optiruns) + shuffle(index_set, set_count); + else + (void)quickperm(index_set, perm, set_count, &piter); + + // summed quality evaluation of this run + float quality = 0.0f; + + // we build a model ouf of the first two lines + const float *L1 = lines[index_set[0]].L; + const float *L2 = lines[index_set[1]].L; + + // get intersection point (ideally a vantage point) + float V[3]; + vec3prodn(V, L1, L2); + + // catch special cases: + // a) L1 and L2 are identical -> V is NULL -> no valid vantage point + // b) vantage point lies inside image frame (no chance to correct for this case) + if(vec3isnull(V) || + (fabs(V[2]) > 0.0f && + V[0]/V[2] >= xmin && + V[1]/V[2] >= ymin && + V[0]/V[2] <= xmax && + V[1]/V[2] <= ymax)) + { + // no valid model + quality = 0.0f; + } + else + { + // valid model + + // normalize V so that x^2 + y^2 + z^2 = 1 + vec3norm(V, V); + + // the two lines constituting the model are part of the set + inout[0] = 1; + inout[1] = 1; + + // go through all remaining lines, check if they are within the model, and + // mark that fact in inout[]. + // summarize a quality parameter for all lines within the model + for(int n = 2; n < set_count; n++) + { + // L is normalized so that x^2 + y^2 = 1 + const float *L3 = lines[index_set[n]].L; + + // we take the absolute value of the dot product of V and L as a measure + // of the "distance" between point and line. Note that this is not the real euclidian + // distance but - with the given normalization - just a pragmatically selected number + // that goes to zero if V lies on L and increases the more V and L are apart + const float d = fabs(vec3scalar(V, L3)); + + // depending on d we either include or exclude the point from the set + inout[n] = (d < epsilon) ? 1 : 0; + + float q; + + if(inout[n] == 1) + { + // a quality parameter that depends 1/3 on the number of lines within the model, + // 1/3 on their weight, and 1/3 on their weighted distance d to the vantage point + q = 0.33f / (float)set_count + + 0.33f * lines[index_set[n]].weight / total_weight + + 0.33f * (1.0f - d / epsilon) * (float)set_count * lines[index_set[n]].weight / total_weight; + } + else + { + q = 0.0f; + lines_eliminated++; + } + + quality += q; + } + valid_runs++; + } + + if(r < optiruns) + { + // on last run of each self-tuning step + if((r % RANSAC_OPTIMIZATION_DRY_RUNS) == (RANSAC_OPTIMIZATION_DRY_RUNS - 1) && (valid_runs > 0)) + { +#ifdef ASHIFT_DEBUG + printf("ransac self-tuning (run %d): epsilon %f", r, epsilon); +#endif + // average ratio of lines that we eliminated with the given epsilon + float ratio = 100.0f * (float)lines_eliminated / ((float)set_count * valid_runs); + // adjust epsilon accordingly + if(ratio < RANSAC_ELIMINATION_RATIO) + epsilon = pow(10.0f, log10(epsilon) - epsilon_step); + else if(ratio > RANSAC_ELIMINATION_RATIO) + epsilon = pow(10.0f, log10(epsilon) + epsilon_step); +#ifdef ASHIFT_DEBUG + printf(" (elimination ratio %f) -> %f\n", ratio, epsilon); +#endif + // reduce step-size for next optimization round + epsilon_step /= 2.0f; + lines_eliminated = 0; + valid_runs = 0; + } + } + else + { + // in the "real" runs check against the best model found so far + if(quality > best_quality) + { + memcpy(best_set, index_set, set_size); + memcpy(best_inout, inout, set_size); + best_quality = quality; + } + } + +#ifdef ASHIFT_DEBUG + // report some statistics + int count = 0, lastcount = 0; + for(int n = 0; n < set_count; n++) count += best_inout[n]; + for(int n = 0; n < set_count; n++) lastcount += inout[n]; + printf("ransac run %d: best qual %.6f, eps %.6f, line count %d of %d (this run: qual %.5f, count %d (%2f%%))\n", r, + best_quality, epsilon, count, set_count, quality, lastcount, 100.0f * lastcount / (float)set_count); +#endif + } + + // store back best set + memcpy(index_set, best_set, set_size); + memcpy(inout_set, best_inout, set_size); + + free(inout); + free(perm); + free(best_inout); + free(best_set); +} + + +// try to clean up structural data by eliminating outliers and thereby increasing +// the chance of a convergent fitting +static int remove_outliers(dt_iop_module_t *module) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + const int width = g->lines_in_width; + const int height = g->lines_in_height; + const int xmin = g->lines_x_off; + const int ymin = g->lines_y_off; + const int xmax = xmin + width; + const int ymax = ymin + height; + + // holds the index set of lines we want to work on + int *lines_set = (int *)malloc(g->lines_count * sizeof(int)); + // holds the result of ransac + int *inout_set = (int *)malloc(g->lines_count * sizeof(int)); + + // some accounting variables + int vnb = 0, vcount = 0; + int hnb = 0, hcount = 0; + + // just to be on the safe side + if(g->lines == NULL) goto error; + + // generate index list for the vertical lines + for(int n = 0; n < g->lines_count; n++) + { + // is this a selected vertical line? + if((g->lines[n].type & ASHIFT_LINE_MASK) != ASHIFT_LINE_VERTICAL_SELECTED) + continue; + + lines_set[vnb] = n; + inout_set[vnb] = 0; + vnb++; + } + + // it only makes sense to call ransac if we have more than two lines + if(vnb > 2) + ransac(g->lines, lines_set, inout_set, vnb, g->vertical_weight, + xmin, xmax, ymin, ymax); + + // adjust line selected flag according to the ransac results + for(int n = 0; n < vnb; n++) + { + const int m = lines_set[n]; + if(inout_set[n] == 1) + { + g->lines[m].type |= ASHIFT_LINE_SELECTED; + vcount++; + } + else + g->lines[m].type &= ~ASHIFT_LINE_SELECTED; + } + // update number of vertical lines + g->vertical_count = vcount; + g->lines_version++; + + // now generate index list for the horizontal lines + for(int n = 0; n < g->lines_count; n++) + { + // is this a selected horizontal line? + if((g->lines[n].type & ASHIFT_LINE_MASK) != ASHIFT_LINE_HORIZONTAL_SELECTED) + continue; + + lines_set[hnb] = n; + inout_set[hnb] = 0; + hnb++; + } + + // it only makes sense to call ransac if we have more than two lines + if(hnb > 2) + ransac(g->lines, lines_set, inout_set, hnb, g->horizontal_weight, + xmin, xmax, ymin, ymax); + + // adjust line selected flag according to the ransac results + for(int n = 0; n < hnb; n++) + { + const int m = lines_set[n]; + if(inout_set[n] == 1) + { + g->lines[m].type |= ASHIFT_LINE_SELECTED; + hcount++; + } + else + g->lines[m].type &= ~ASHIFT_LINE_SELECTED; + } + // update number of horizontal lines + g->horizontal_count = hcount; + g->lines_version++; + + free(inout_set); + free(lines_set); + + return TRUE; + +error: + free(inout_set); + free(lines_set); + return FALSE; +} + +// utility function to map a variable in [min; max] to [-INF; + INF] +static inline double logit(double x, double min, double max) +{ + const double eps = 1.0e-6; + // make sure p does not touch the borders of its definition area, + // not critical for data accuracy as logit() is only used on initial fit parameters + double p = CLAMP((x - min) / (max - min), eps, 1.0 - eps); + + return (2.0 * atanh(2.0 * p - 1.0)); +} + +// inverted function to logit() +static inline double ilogit(double L, double min, double max) +{ + double p = 0.5 * (1.0 + tanh(0.5 * L)); + + return (p * (max - min) + min); +} + +// helper function for simplex() return quality parameter for the given model +// strategy: +// * generate homography matrix out of fixed parameters and fitting parameters +// * apply homography to all end points of affected lines +// * generate new line out of transformed end points +// * calculate scalar product s of line with perpendicular axis +// * sum over weighted s^2 values +static double model_fitness(double *params, void *data) +{ + dt_iop_ashift_fit_params_t *fit = (dt_iop_ashift_fit_params_t *)data; + + // just for convenience: get shorter names + dt_iop_ashift_line_t *lines = fit->lines; + const int lines_count = fit->lines_count; + const int width = fit->width; + const int height = fit->height; + const float f_length_kb = fit->f_length_kb; + const float orthocorr = fit->orthocorr; + const float aspect = fit->aspect; + + float rotation = fit->rotation; + float lensshift_v = fit->lensshift_v; + float lensshift_h = fit->lensshift_h; + float shear = fit->shear; + float camera_pitch = fit->camera_pitch; + float camera_yaw = fit->camera_yaw; + float rotation_range = fit->rotation_range; + /* + float lensshift_v_range = fit->lensshift_v_range; + float lensshift_h_range = fit->lensshift_h_range; + float shear_range = fit->shear_range; + */ + float camera_pitch_range = fit->camera_pitch_range; + float camera_yaw_range = fit->camera_yaw_range; + + int pcount = 0; + + // fill in fit parameters from params[]. Attention: order matters!!! + if(isnan(rotation)) + { + rotation = ilogit(params[pcount], -rotation_range, rotation_range); + pcount++; + } + + /* + if(isnan(lensshift_v)) + { + lensshift_v = ilogit(params[pcount], -lensshift_v_range, lensshift_v_range); + pcount++; + } + + if(isnan(lensshift_h)) + { + lensshift_h = ilogit(params[pcount], -lensshift_h_range, lensshift_h_range); + pcount++; + } + + if(isnan(shear)) + { + shear = ilogit(params[pcount], -shear_range, shear_range); + pcount++; + } + */ + + if(isnan(camera_pitch)) + { + camera_pitch = ilogit(params[pcount], -camera_pitch_range, camera_pitch_range); + pcount++; + } + + if(isnan(camera_yaw)) + { + camera_yaw = ilogit(params[pcount], -camera_yaw_range, camera_yaw_range); + pcount++; + } + + assert(pcount == fit->params_count); + + // the possible reference axes + const float Av[3] = { 1.0f, 0.0f, 0.0f }; + const float Ah[3] = { 0.0f, 1.0f, 0.0f }; + + // generate homograph out of the parameters + float homograph[3][3]; + homography((float *)homograph, rotation, lensshift_v, lensshift_h, shear, camera_pitch, camera_yaw, f_length_kb, + orthocorr, aspect, width, height, ASHIFT_HOMOGRAPH_FORWARD); + + // accounting variables + double sumsq_v = 0.0; + double sumsq_h = 0.0; + double weight_v = 0.0; + double weight_h = 0.0; + int count_v = 0; + int count_h = 0; + int count = 0; + + // iterate over all lines + for(int n = 0; n < lines_count; n++) + { + // check if this is a line which we must skip + if((lines[n].type & fit->linemask) != fit->linetype) + continue; + + // the direction of this line (vertical?) + const int isvertical = lines[n].type & ASHIFT_LINE_DIRVERT; + + // select the perpendicular reference axis + const float *A = isvertical ? Ah : Av; + + // apply homographic transformation to the end points + float P1[3], P2[3]; + mat3mulv(P1, (float *)homograph, lines[n].p1); + mat3mulv(P2, (float *)homograph, lines[n].p2); + + // get line connecting the two points + float L[3]; + vec3prodn(L, P1, P2); + + // normalize L so that x^2 + y^2 = 1; makes sure that + // y^2 = 1 / (1 + m^2) and x^2 = m^2 / (1 + m^2) with m defining the slope of the line + vec3lnorm(L, L); + + // get scalar product of line L with orthogonal axis A -> gives 0 if line is perpendicular + float s = vec3scalar(L, A); + + // sum up weighted s^2 for both directions individually + sumsq_v += isvertical ? (double)s * s * lines[n].weight : 0.0; + weight_v += isvertical ? lines[n].weight : 0.0; + count_v += isvertical ? 1 : 0; + sumsq_h += !isvertical ? (double)s * s * lines[n].weight : 0.0; + weight_h += !isvertical ? lines[n].weight : 0.0; + count_h += !isvertical ? 1 : 0; + count++; + } + + const double v = weight_v > 0.0f && count > 0 ? sumsq_v / weight_v * (float)count_v / count : 0.0; + const double h = weight_h > 0.0f && count > 0 ? sumsq_h / weight_h * (float)count_h / count : 0.0; + + double sum = sqrt(1.0 - (1.0 - v) * (1.0 - h)) * 1.0e6; + //double sum = sqrt(v + h) * 1.0e6; + +#ifdef ASHIFT_DEBUG + /* + printf("fitness with rotation %f, lensshift_v %f, lensshift_h %f, shear %f -> lines %d, quality %10f\n", + rotation, lensshift_v, lensshift_h, shear, count, sum); + */ + printf("fitness with rotation %f, camera_pitch %f, camera_yaw %f -> lines %d, quality %10f\n", + rotation, camera_pitch, camera_yaw, count, sum); +#endif + + return sum; +} + +// setup all data structures for fitting and call NM simplex +static dt_iop_ashift_nmsresult_t nmsfit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + if(!g->lines) return NMS_NOT_ENOUGH_LINES; + if(dir == ASHIFT_FIT_NONE) return NMS_SUCCESS; + + double params[4]; + int pcount = 0; + int enough_lines = TRUE; + + // initialize fit parameters + dt_iop_ashift_fit_params_t fit; + fit.lines = g->lines; + fit.lines_count = g->lines_count; + fit.width = g->lines_in_width; + fit.height = g->lines_in_height; + fit.f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? (float)DEFAULT_F_LENGTH : p->f_length * p->crop_factor; + fit.orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr; + fit.aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect; + fit.rotation = p->rotation; + fit.lensshift_v = p->lensshift_v; + fit.lensshift_h = p->lensshift_h; + fit.shear = p->shear; + fit.camera_pitch = p->camera_pitch; + fit.camera_yaw = p->camera_yaw; + fit.rotation_range = g->rotation_range; + fit.lensshift_v_range = g->lensshift_v_range; + fit.lensshift_h_range = g->lensshift_h_range; + fit.shear_range = g->shear_range; + fit.camera_pitch_range = g->camera_pitch_range; + fit.camera_yaw_range = g->camera_yaw_range; + fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED; + fit.linemask = ASHIFT_LINE_MASK; + fit.params_count = 0; + fit.weight = 0.0f; + + // if the image is flipped and if we do not want to fit both lens shift + // directions or none at all, then we need to change direction + dt_iop_ashift_fitaxis_t mdir = dir; + if((mdir & ASHIFT_FIT_LENS_BOTH) != ASHIFT_FIT_LENS_BOTH && + (mdir & ASHIFT_FIT_LENS_BOTH) != 0) + { + // flip all directions + mdir ^= g->isflipped ? ASHIFT_FIT_FLIP : 0; + // special case that needs to be corrected + mdir |= (mdir & ASHIFT_FIT_LINES_BOTH) == 0 ? ASHIFT_FIT_LINES_BOTH : 0; + } + + + // prepare fit structure and starting parameters for simplex fit. + // note: the sequence of parameters in params[] needs to match the + // respective order in dt_iop_ashift_fit_params_t. Parameters which are + // to be fittet are marked with NAN in the fit structure. Non-NAN + // parameters are assumed to be constant. + if(mdir & ASHIFT_FIT_ROTATION) + { + // we fit rotation + fit.params_count++; + params[pcount] = logit(0, -fit.rotation_range, fit.rotation_range); + pcount++; + fit.rotation = NAN; + } + + /* + if(mdir & ASHIFT_FIT_LENS_VERT) + { + // we fit vertical lens shift + fit.params_count++; + params[pcount] = logit(fit.lensshift_v, -fit.lensshift_v_range, fit.lensshift_v_range); + pcount++; + fit.lensshift_v = NAN; + } + + if(mdir & ASHIFT_FIT_LENS_HOR) + { + // we fit horizontal lens shift + fit.params_count++; + params[pcount] = logit(fit.lensshift_h, -fit.lensshift_h_range, fit.lensshift_h_range); + pcount++; + fit.lensshift_h = NAN; + } + + if(mdir & ASHIFT_FIT_SHEAR) + { + // we fit the shear parameter + fit.params_count++; + params[pcount] = logit(fit.shear, -fit.shear_range, fit.shear_range); + pcount++; + fit.shear = NAN; + } + */ + + if(mdir & ASHIFT_FIT_LENS_VERT) + { + // we fit pitch + fit.params_count++; + params[pcount] = logit(0, -fit.camera_pitch_range, fit.camera_pitch_range); + pcount++; + fit.camera_pitch = NAN; + } + + if(mdir & ASHIFT_FIT_LENS_HOR) + { + // we fit yaw + fit.params_count++; + params[pcount] = logit(0, -fit.camera_yaw_range, fit.camera_yaw_range); + pcount++; + fit.camera_yaw = NAN; + } + + if(mdir & ASHIFT_FIT_LINES_VERT) + { + // we use vertical lines for fitting + fit.linetype |= ASHIFT_LINE_DIRVERT; + fit.weight += g->vertical_weight; + enough_lines = enough_lines && (g->vertical_count >= MINIMUM_FITLINES); + } + + if(mdir & ASHIFT_FIT_LINES_HOR) + { + // we use horizontal lines for fitting + fit.linetype |= 0; + fit.weight += g->horizontal_weight; + enough_lines = enough_lines && (g->horizontal_count >= MINIMUM_FITLINES); + } + + // this needs to come after ASHIFT_FIT_LINES_VERT and ASHIFT_FIT_LINES_HOR + if((mdir & ASHIFT_FIT_LINES_BOTH) == ASHIFT_FIT_LINES_BOTH) + { + // if we use fitting in both directions we need to + // adjust fit.linetype and fit.linemask to match all selected lines + fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED; + fit.linemask = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED; + } + + // error case: we do not run simplex if there are not enough lines + if(!enough_lines) + { +#ifdef ASHIFT_DEBUG + printf("optimization not possible: insufficient number of lines\n"); +#endif + return NMS_NOT_ENOUGH_LINES; + } + + // start the simplex fit + int iter = simplex(model_fitness, params, fit.params_count, NMS_EPSILON, NMS_SCALE, NMS_ITERATIONS, NULL, (void*)&fit); + + // error case: the fit did not converge + if(iter >= NMS_ITERATIONS) + { +#ifdef ASHIFT_DEBUG + printf("optimization not successful: maximum number of iterations reached (%d)\n", iter); +#endif + return NMS_DID_NOT_CONVERGE; + } + + // fit was successful: now consolidate the results (order matters!!!) + pcount = 0; + fit.rotation = isnan(fit.rotation) ? ilogit(params[pcount++], -fit.rotation_range, fit.rotation_range) : fit.rotation; + /* + fit.lensshift_v = isnan(fit.lensshift_v) ? ilogit(params[pcount++], -fit.lensshift_v_range, fit.lensshift_v_range) : fit.lensshift_v; + fit.lensshift_h = isnan(fit.lensshift_h) ? ilogit(params[pcount++], -fit.lensshift_h_range, fit.lensshift_h_range) : fit.lensshift_h; + fit.shear = isnan(fit.shear) ? ilogit(params[pcount++], -fit.shear_range, fit.shear_range) : fit.shear; + */ + fit.camera_pitch = isnan(fit.camera_pitch) ? ilogit(params[pcount++], -fit.camera_pitch_range, fit.camera_pitch_range) : fit.camera_pitch; + fit.camera_yaw = isnan(fit.camera_yaw) ? ilogit(params[pcount++], -fit.camera_yaw_range, fit.camera_yaw_range) : fit.camera_yaw; +#ifdef ASHIFT_DEBUG + /* + printf("params after optimization (%d iterations): rotation %f, lensshift_v %f, lensshift_h %f, shear %f\n", + iter, fit.rotation, fit.lensshift_v, fit.lensshift_h, fit.shear); + */ + printf("params after optimization (%d iterations): rotation %f, camera_pitch %f, camera_yaw %f\n", + iter, fit.rotation, fit.camera_pitch, fit.camera_yaw); +#endif + + /* + // sanity check: in case of extreme values the image gets distorted so strongly that it spans an insanely huge area. we check that + // case and assume values that increase the image area by more than a factor of 4 as being insane. + float homograph[3][3]; + homography((float *)homograph, fit.rotation, fit.lensshift_v, fit.lensshift_h, fit.shear, fit.f_length_kb, + fit.orthocorr, fit.aspect, fit.width, fit.height, ASHIFT_HOMOGRAPH_FORWARD); + + // visit all four corners and find maximum span + float xm = FLT_MAX, xM = -FLT_MAX, ym = FLT_MAX, yM = -FLT_MAX; + for(int y = 0; y < fit.height; y += fit.height - 1) + for(int x = 0; x < fit.width; x += fit.width - 1) + { + float pi[3], po[3]; + pi[0] = x; + pi[1] = y; + pi[2] = 1.0f; + mat3mulv(po, (float *)homograph, pi); + po[0] /= po[2]; + po[1] /= po[2]; + xm = fmin(xm, po[0]); + ym = fmin(ym, po[1]); + xM = fmax(xM, po[0]); + yM = fmax(yM, po[1]); + } + + if((xM - xm) * (yM - ym) > 4.0f * fit.width * fit.height) + { +#ifdef ASHIFT_DEBUG + printf("optimization not successful: degenerate case with area growth factor (%f) exceeding limits\n", + (xM - xm) * (yM - ym) / (fit.width * fit.height)); +#endif + return NMS_INSANE; + } + */ + + // now write the results into structure p + p->rotation = fit.rotation; + /* + p->lensshift_v = fit.lensshift_v; + p->lensshift_h = fit.lensshift_h; + p->shear = fit.shear; + */ + p->camera_pitch = fit.camera_pitch; + p->camera_yaw = fit.camera_yaw; + return NMS_SUCCESS; +} + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +#ifdef ASHIFT_DEBUG +// only used in development phase. call model_fitness() with current parameters and +// print some useful information +static void model_probe(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + if(!g->lines) return; + if(dir == ASHIFT_FIT_NONE) return; + + double params[4]; + int enough_lines = TRUE; + + // initialize fit parameters + dt_iop_ashift_fit_params_t fit; + fit.lines = g->lines; + fit.lines_count = g->lines_count; + fit.width = g->lines_in_width; + fit.height = g->lines_in_height; + fit.f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor; + fit.orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr; + fit.aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect; + fit.rotation = p->rotation; + fit.lensshift_v = p->lensshift_v; + fit.lensshift_h = p->lensshift_h; + fit.shear = p->shear; + fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED; + fit.linemask = ASHIFT_LINE_MASK; + fit.params_count = 0; + fit.weight = 0.0f; + + // if the image is flipped and if we do not want to fit both lens shift + // directions or none at all, then we need to change direction + dt_iop_ashift_fitaxis_t mdir = dir; + if((mdir & ASHIFT_FIT_LENS_BOTH) != ASHIFT_FIT_LENS_BOTH && + (mdir & ASHIFT_FIT_LENS_BOTH) != 0) + { + // flip all directions + mdir ^= g->isflipped ? ASHIFT_FIT_FLIP : 0; + // special case that needs to be corrected + mdir |= (mdir & ASHIFT_FIT_LINES_BOTH) == 0 ? ASHIFT_FIT_LINES_BOTH : 0; + } + + if(mdir & ASHIFT_FIT_LINES_VERT) + { + // we use vertical lines for fitting + fit.linetype |= ASHIFT_LINE_DIRVERT; + fit.weight += g->vertical_weight; + enough_lines = enough_lines && (g->vertical_count >= MINIMUM_FITLINES); + } + + if(mdir & ASHIFT_FIT_LINES_HOR) + { + // we use horizontal lines for fitting + fit.linetype |= 0; + fit.weight += g->horizontal_weight; + enough_lines = enough_lines && (g->horizontal_count >= MINIMUM_FITLINES); + } + + // this needs to come after ASHIFT_FIT_LINES_VERT and ASHIFT_FIT_LINES_HOR + if((mdir & ASHIFT_FIT_LINES_BOTH) == ASHIFT_FIT_LINES_BOTH) + { + // if we use fitting in both directions we need to + // adjust fit.linetype and fit.linemask to match all selected lines + fit.linetype = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED; + fit.linemask = ASHIFT_LINE_RELEVANT | ASHIFT_LINE_SELECTED; + } + + double quality = model_fitness(params, (void *)&fit); + + printf("model fitness: %.8f (rotation %f, lensshift_v %f, lensshift_h %f, shear %f)\n", + quality, p->rotation, p->lensshift_v, p->lensshift_h, p->shear); +} +#endif +#endif // if 0 +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT (no crop support yet) +//----------------------------------------------------------------------------- +#if 0 +// function to keep crop fitting parameters within constraints +static void crop_constraint(double *params, int pcount) +{ + if(pcount > 0) params[0] = fabs(params[0]); + if(pcount > 1) params[1] = fabs(params[1]); + if(pcount > 2) params[2] = fabs(params[2]); + + if(pcount > 0 && params[0] > 1.0) params[0] = 1.0 - params[0]; + if(pcount > 1 && params[1] > 1.0) params[1] = 1.0 - params[1]; + if(pcount > 2 && params[2] > 0.5*M_PI) params[2] = 0.5*M_PI - params[2]; +} + +// helper function for getting the best fitting crop area; +// returns the negative area of the largest rectangle that fits within the +// defined image with a given rectangle's center and its aspect angle; +// the trick: the rectangle center coordinates are given in the input +// image coordinates so we know for sure that it also lies within the image after +// conversion to the output coordinates +static double crop_fitness(double *params, void *data) +{ + dt_iop_ashift_cropfit_params_t *cropfit = (dt_iop_ashift_cropfit_params_t *)data; + + const float wd = cropfit->width; + const float ht = cropfit->height; + + // get variable and constant parameters, respectively + const float x = isnan(cropfit->x) ? params[0] : cropfit->x; + const float y = isnan(cropfit->y) ? params[1] : cropfit->y; + const float alpha = isnan(cropfit->alpha) ? params[2] : cropfit->alpha; + + // the center of the rectangle in input image coordinates + const float Pc[3] = { x * wd, y * ht, 1.0f }; + + // convert to the output image coordinates and normalize + float P[3]; + mat3mulv(P, (float *)cropfit->homograph, Pc); + P[0] /= P[2]; + P[1] /= P[2]; + P[2] = 1.0f; + + // two auxiliary points (some arbitrary distance away from P) to construct the diagonals + const float Pa[2][3] = { { P[0] + 10.0f * cos(alpha), P[1] + 10.0f * sin(alpha), 1.0f }, + { P[0] + 10.0f * cos(alpha), P[1] - 10.0f * sin(alpha), 1.0f } }; + + // the two diagonals: D = P x Pa + float D[2][3]; + vec3prodn(D[0], P, Pa[0]); + vec3prodn(D[1], P, Pa[1]); + + // find all intersection points of all four edges with both diagonals (I = E x D); + // the shortest distance d2min of the intersection point I to the crop area center P determines + // the size of the crop area that still fits into the image (for the given center and aspect angle) + float d2min = FLT_MAX; + for(int k = 0; k < 4; k++) + for(int l = 0; l < 2; l++) + { + // the intersection point + float I[3]; + vec3prodn(I, cropfit->edges[k], D[l]); + + // special case: I is all null -> E and D are identical -> P lies on E -> d2min = 0 + if(vec3isnull(I)) + { + d2min = 0.0f; + break; + } + + // special case: I[2] is 0.0f -> E and D are parallel and intersect at infinity -> no relevant point + if(I[2] == 0.0f) + continue; + + // the default case -> normalize I + I[0] /= I[2]; + I[1] /= I[2]; + + // calculate distance from I to P + const float d2 = SQR(P[0] - I[0]) + SQR(P[1] - I[1]); + + // the minimum distance over all intersection points + d2min = MIN(d2min, d2); + } + + // calculate the area of the rectangle + const float A = 2.0f * d2min * sin(2.0f * alpha); + +#ifdef ASHIFT_DEBUG + printf("crop fitness with x %f, y %f, angle %f -> distance %f, area %f\n", + x, y, alpha, d2min, A); +#endif + // and return -A to allow Nelder-Mead simplex to search for the minimum + return -A; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +// strategy: for a given center of the crop area and a specific aspect angle +// we calculate the largest crop area that still lies within the output image; +// now we allow a Nelder-Mead simplex to search for the center coordinates +// (and optionally the aspect angle) that delivers the largest overall crop area. +static void do_crop(dt_iop_module_t *module, dt_iop_ashift_params_t *p) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + // skip if fitting is still running + if(g->fitting) return; + + // reset fit margins if auto-cropping is off + if(p->cropmode == ASHIFT_CROP_OFF) + { + p->cl = 0.0f; + p->cr = 1.0f; + p->ct = 0.0f; + p->cb = 1.0f; + return; + } + + g->fitting = 1; + + double params[3]; + int pcount; + + // get parameters for the homograph + const float f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor; + const float orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr; + const float aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect; + const float rotation = p->rotation; + const float lensshift_v = p->lensshift_v; + const float lensshift_h = p->lensshift_h; + const float shear = p->shear; + + // prepare structure of constant parameters + dt_iop_ashift_cropfit_params_t cropfit; + cropfit.width = g->buf_width; + cropfit.height = g->buf_height; + homography((float *)cropfit.homograph, rotation, lensshift_v, lensshift_h, shear, f_length_kb, + orthocorr, aspect, cropfit.width, cropfit.height, ASHIFT_HOMOGRAPH_FORWARD); + + const float wd = cropfit.width; + const float ht = cropfit.height; + + // the four vertices of the image in input image coordinates + const float Vc[4][3] = { { 0.0f, 0.0f, 1.0f }, + { 0.0f, ht, 1.0f }, + { wd, ht, 1.0f }, + { wd, 0.0f, 1.0f } }; + + // convert the vertices to output image coordinates + float V[4][3]; + for(int n = 0; n < 4; n++) + mat3mulv(V[n], (float *)cropfit.homograph, Vc[n]); + + // get width and height of output image for later use + float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN; + for(int n = 0; n < 4; n++) + { + // normalize V + V[n][0] /= V[n][2]; + V[n][1] /= V[n][2]; + V[n][2] = 1.0f; + xmin = MIN(xmin, V[n][0]); + xmax = MAX(xmax, V[n][0]); + ymin = MIN(ymin, V[n][1]); + ymax = MAX(ymax, V[n][1]); + } + const float owd = xmax - xmin; + const float oht = ymax - ymin; + + // calculate the lines defining the four edges of the image area: E = V[n] x V[n+1] + for(int n = 0; n < 4; n++) + vec3prodn(cropfit.edges[n], V[n], V[(n + 1) % 4]); + + // initial fit parameters: crop area is centered and aspect angle is that of the original image + // number of parameters: fit only crop center coordinates with a fixed aspect ratio, or fit all three variables + if(p->cropmode == ASHIFT_CROP_LARGEST) + { + params[0] = 0.5; + params[1] = 0.5; + params[2] = atan2((float)cropfit.height, (float)cropfit.width); + cropfit.x = NAN; + cropfit.y = NAN; + cropfit.alpha = NAN; + pcount = 3; + } + else //(p->cropmode == ASHIFT_CROP_ASPECT) + { + params[0] = 0.5; + params[1] = 0.5; + cropfit.x = NAN; + cropfit.y = NAN; + cropfit.alpha = atan2((float)cropfit.height, (float)cropfit.width); + pcount = 2; + } + + // start the simplex fit + const int iter = simplex(crop_fitness, params, pcount, NMS_CROP_EPSILON, NMS_CROP_SCALE, NMS_CROP_ITERATIONS, + crop_constraint, (void*)&cropfit); + + float A; // RT + float d; // RT + float Pc[3] = { 0.f, 0.f, 1.f }; // RT + + // in case the fit did not converge -> failed + if(iter >= NMS_CROP_ITERATIONS) goto failed; + + // the fit did converge -> get clipping margins out of params: + cropfit.x = isnan(cropfit.x) ? params[0] : cropfit.x; + cropfit.y = isnan(cropfit.y) ? params[1] : cropfit.y; + cropfit.alpha = isnan(cropfit.alpha) ? params[2] : cropfit.alpha; + + // the area of the best fitting rectangle + /*RT const float*/ A = fabs(crop_fitness(params, (void*)&cropfit)); + + // unlikely to happen but we need to catch this case + if(A == 0.0f) goto failed; + + // we need the half diagonal of that rectangle (this is in output image dimensions); + // no need to check for division by zero here as this case implies A == 0.0f, caught above + /*RT const float*/ d = sqrt(A / (2.0f * sin(2.0f * cropfit.alpha))); + + // the rectangle's center in input image (homogeneous) coordinates + // RT const float Pc[3] = { cropfit.x * wd, cropfit.y * ht, 1.0f }; + Pc[0] = cropfit.x * wd; // RT + Pc[1] = cropfit.y * ht; // RT + + // convert rectangle center to output image coordinates and normalize + float P[3]; + mat3mulv(P, (float *)cropfit.homograph, Pc); + P[0] /= P[2]; + P[1] /= P[2]; + + // calculate clipping margins relative to output image dimensions + p->cl = CLAMP((P[0] - d * cos(cropfit.alpha)) / owd, 0.0f, 1.0f); + p->cr = CLAMP((P[0] + d * cos(cropfit.alpha)) / owd, 0.0f, 1.0f); + p->ct = CLAMP((P[1] - d * sin(cropfit.alpha)) / oht, 0.0f, 1.0f); + p->cb = CLAMP((P[1] + d * sin(cropfit.alpha)) / oht, 0.0f, 1.0f); + + // final sanity check + if(p->cr - p->cl <= 0.0f || p->cb - p->ct <= 0.0f) goto failed; + + g->fitting = 0; + +#ifdef ASHIFT_DEBUG + printf("margins after crop fitting: iter %d, x %f, y %f, angle %f, crop area (%f %f %f %f), width %f, height %f\n", + iter, cropfit.x, cropfit.y, cropfit.alpha, p->cl, p->cr, p->ct, p->cb, wd, ht); +#endif +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 + dt_control_queue_redraw_center(); +#endif // if 0 +//----------------------------------------------------------------------------- + + return; + +failed: + // in case of failure: reset clipping margins, set "automatic cropping" parameter + // to "off" state, and display warning message + p->cl = 0.0f; + p->cr = 1.0f; + p->ct = 0.0f; + p->cb = 1.0f; + p->cropmode = ASHIFT_CROP_OFF; +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 + dt_bauhaus_combobox_set(g->cropmode, p->cropmode); +#endif // if 0 +//----------------------------------------------------------------------------- + g->fitting = 0; + dt_control_log(_("automatic cropping failed")); + return; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT (no crop support yet) +//----------------------------------------------------------------------------- +#if 0 +// manually adjust crop area by shifting its center +static void crop_adjust(dt_iop_module_t *module, dt_iop_ashift_params_t *p, const float newx, const float newy) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + // skip if fitting is still running + if(g->fitting) return; + + // get parameters for the homograph + const float f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor; + const float orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr; + const float aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect; + const float rotation = p->rotation; + const float lensshift_v = p->lensshift_v; + const float lensshift_h = p->lensshift_h; + const float shear = p->shear; + + const float wd = g->buf_width; + const float ht = g->buf_height; + + const float alpha = atan2(ht, wd); + + float homograph[3][3]; + homography((float *)homograph, rotation, lensshift_v, lensshift_h, shear, f_length_kb, + orthocorr, aspect, wd, ht, ASHIFT_HOMOGRAPH_FORWARD); + + // the four vertices of the image in input image coordinates + const float Vc[4][3] = { { 0.0f, 0.0f, 1.0f }, + { 0.0f, ht, 1.0f }, + { wd, ht, 1.0f }, + { wd, 0.0f, 1.0f } }; + + // convert the vertices to output image coordinates + float V[4][3]; + for(int n = 0; n < 4; n++) + mat3mulv(V[n], (float *)homograph, Vc[n]); + + // get width and height of output image + float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN; + for(int n = 0; n < 4; n++) + { + // normalize V + V[n][0] /= V[n][2]; + V[n][1] /= V[n][2]; + V[n][2] = 1.0f; + xmin = MIN(xmin, V[n][0]); + xmax = MAX(xmax, V[n][0]); + ymin = MIN(ymin, V[n][1]); + ymax = MAX(ymax, V[n][1]); + } + const float owd = xmax - xmin; + const float oht = ymax - ymin; + + // calculate the lines defining the four edges of the image area: E = V[n] x V[n+1] + float E[4][3]; + for(int n = 0; n < 4; n++) + vec3prodn(E[n], V[n], V[(n + 1) % 4]); + + // the center of the rectangle in output image coordinates + const float P[3] = { newx * owd, newy * oht, 1.0f }; + + // two auxiliary points (some arbitrary distance away from P) to construct the diagonals + const float Pa[2][3] = { { P[0] + 10.0f * cos(alpha), P[1] + 10.0f * sin(alpha), 1.0f }, + { P[0] + 10.0f * cos(alpha), P[1] - 10.0f * sin(alpha), 1.0f } }; + + // the two diagonals: D = P x Pa + float D[2][3]; + vec3prodn(D[0], P, Pa[0]); + vec3prodn(D[1], P, Pa[1]); + + // find all intersection points of all four edges with both diagonals (I = E x D); + // the shortest distance d2min of the intersection point I to the crop area center P determines + // the size of the crop area that still fits into the image (for the given center and aspect angle) + float d2min = FLT_MAX; + for(int k = 0; k < 4; k++) + for(int l = 0; l < 2; l++) + { + // the intersection point + float I[3]; + vec3prodn(I, E[k], D[l]); + + // special case: I is all null -> E and D are identical -> P lies on E -> d2min = 0 + if(vec3isnull(I)) + { + d2min = 0.0f; + break; + } + + // special case: I[2] is 0.0f -> E and D are parallel and intersect at infinity -> no relevant point + if(I[2] == 0.0f) + continue; + + // the default case -> normalize I + I[0] /= I[2]; + I[1] /= I[2]; + + // calculate distance from I to P + const float d2 = SQR(P[0] - I[0]) + SQR(P[1] - I[1]); + + // the minimum distance over all intersection points + d2min = MIN(d2min, d2); + } + + const float d = sqrt(d2min); + + // do not allow crop area to drop below 1% of input image area + const float A = 2.0f * d * d * sin(2.0f * alpha); + if(A < 0.01f * wd * ht) return; + + // calculate clipping margins relative to output image dimensions + p->cl = CLAMP((P[0] - d * cos(alpha)) / owd, 0.0f, 1.0f); + p->cr = CLAMP((P[0] + d * cos(alpha)) / owd, 0.0f, 1.0f); + p->ct = CLAMP((P[1] - d * sin(alpha)) / oht, 0.0f, 1.0f); + p->cb = CLAMP((P[1] + d * sin(alpha)) / oht, 0.0f, 1.0f); + +#ifdef ASHIFT_DEBUG + printf("margins after crop adjustment: x %f, y %f, angle %f, crop area (%f %f %f %f), width %f, height %f\n", + 0.5f * (p->cl + p->cr), 0.5f * (p->ct + p->cb), alpha, p->cl, p->cr, p->ct, p->cb, wd, ht); +#endif + dt_control_queue_redraw_center(); + return; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +// helper function to start analysis for structural data and report about errors +static int do_get_structure(dt_iop_module_t *module, dt_iop_ashift_params_t *p, + dt_iop_ashift_enhance_t enhance) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + if(g->fitting) return FALSE; + + g->fitting = 1; + + float *b = NULL; + { + MyMutex::MyLock lock(g->lock); + b = g->buf; + } + /* dt_pthread_mutex_lock(&g->lock); */ + /* float *b = g->buf; */ + /* dt_pthread_mutex_unlock(&g->lock); */ + + if(b == NULL) + { + dt_control_log(_("data pending - please repeat")); + goto error; + } + + if(!get_structure(module, enhance)) + { + dt_control_log(_("could not detect structural data in image")); +#ifdef ASHIFT_DEBUG + // find out more + printf("do_get_structure: buf %p, buf_hash %lu, buf_width %d, buf_height %d, lines %p, lines_count %d\n", + g->buf, g->buf_hash, g->buf_width, g->buf_height, g->lines, g->lines_count); +#endif + goto error; + } + + if(!remove_outliers(module)) + { + dt_control_log(_("could not run outlier removal")); +#ifdef ASHIFT_DEBUG + // find out more + printf("remove_outliers: buf %p, buf_hash %lu, buf_width %d, buf_height %d, lines %p, lines_count %d\n", + g->buf, g->buf_hash, g->buf_width, g->buf_height, g->lines, g->lines_count); +#endif + goto error; + } + + g->fitting = 0; + return TRUE; + +error: + g->fitting = 0; + return FALSE; +} + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +// helper function to clean structural data +static int do_clean_structure(dt_iop_module_t *module, dt_iop_ashift_params_t *p) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + if(g->fitting) return FALSE; + + g->fitting = 1; + g->lines_count = 0; + g->vertical_count = 0; + g->horizontal_count = 0; + free(g->lines); + g->lines = NULL; + g->lines_version++; + g->lines_suppressed = 0; + g->fitting = 0; + return TRUE; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +// helper function to start parameter fit and report about errors +static int do_fit(dt_iop_module_t *module, dt_iop_ashift_params_t *p, dt_iop_ashift_fitaxis_t dir) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + dt_iop_ashift_nmsresult_t res; + + if(g->fitting) return FALSE; + + // if no structure available get it + if(g->lines == NULL) + if(!do_get_structure(module, p, ASHIFT_ENHANCE_NONE)) goto error; + + g->fitting = 1; + + res = nmsfit(module, p, dir); + + switch(res) + { + case NMS_NOT_ENOUGH_LINES: + dt_control_log(_("not enough structure for automatic correction")); + goto error; + break; + case NMS_DID_NOT_CONVERGE: + case NMS_INSANE: + dt_control_log(_("automatic correction failed, please correct manually")); + goto error; + break; + case NMS_SUCCESS: + default: + break; + } + + g->fitting = 0; + + /* + // finally apply cropping + do_crop(module, p); + */ + + return TRUE; + +error: + g->fitting = 0; + return FALSE; +} + +//----------------------------------------------------------------------------- +// RT: BEGIN COMMENT +#if 0 +//----------------------------------------------------------------------------- +void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, + void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) +{ + dt_iop_ashift_data_t *data = (dt_iop_ashift_data_t *)piece->data; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + const int ch = piece->colors; + const int ch_width = ch * roi_in->width; + + // only for preview pipe: collect input buffer data and do some other evaluations + if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW) + { + // we want to find out if the final output image is flipped in relation to this iop + // so we can adjust the gui labels accordingly + + const int width = roi_in->width; + const int height = roi_in->height; + const int x_off = roi_in->x; + const int y_off = roi_in->y; + const float scale = roi_in->scale; + + // origin of image and opposite corner as reference points + float points[4] = { 0.0f, 0.0f, (float)piece->buf_in.width, (float)piece->buf_in.height }; + float ivec[2] = { points[2] - points[0], points[3] - points[1] }; + float ivecl = sqrt(ivec[0] * ivec[0] + ivec[1] * ivec[1]); + + // where do they go? + dt_dev_distort_backtransform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999, points, + 2); + + float ovec[2] = { points[2] - points[0], points[3] - points[1] }; + float ovecl = sqrt(ovec[0] * ovec[0] + ovec[1] * ovec[1]); + + // angle between input vector and output vector + float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f)); + + // we are interested if |alpha| is in the range of 90° +/- 45° -> we assume the image is flipped + int isflipped = fabs(fmod(alpha + M_PI, M_PI) - M_PI / 2.0f) < M_PI / 4.0f ? 1 : 0; + + // did modules prior to this one in pixelpipe have changed? -> check via hash value + uint64_t hash = dt_dev_hash_plus(self->dev, self->dev->preview_pipe, 0, self->priority - 1); + + dt_pthread_mutex_lock(&g->lock); + g->isflipped = isflipped; + + // save a copy of preview input buffer for parameter fitting + if(g->buf == NULL || (size_t)g->buf_width * g->buf_height < (size_t)width * height) + { + // if needed to allocate buffer + free(g->buf); // a no-op if g->buf is NULL + // only get new buffer if no old buffer available or old buffer does not fit in terms of size + g->buf = malloc((size_t)width * height * 4 * sizeof(float)); + } + + if(g->buf /* && hash != g->buf_hash */) + { + // copy data + memcpy(g->buf, ivoid, (size_t)width * height * ch * sizeof(float)); + + g->buf_width = width; + g->buf_height = height; + g->buf_x_off = x_off; + g->buf_y_off = y_off; + g->buf_scale = scale; + g->buf_hash = hash; + } + + dt_pthread_mutex_unlock(&g->lock); + } + + // if module is set to neutral parameters we just copy input->output and are done + if(isneutral(data)) + { + memcpy(ovoid, ivoid, (size_t)roi_out->width * roi_out->height * ch * sizeof(float)); + return; + } + + const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF); + + float ihomograph[3][3]; + homography((float *)ihomograph, data->rotation, data->lensshift_v, data->lensshift_h, data->shear, data->f_length_kb, + data->orthocorr, data->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED); + + // clipping offset + const float fullwidth = (float)piece->buf_out.width / (data->cr - data->cl); + const float fullheight = (float)piece->buf_out.height / (data->cb - data->ct); + const float cx = roi_out->scale * fullwidth * data->cl; + const float cy = roi_out->scale * fullheight * data->ct; + + +#ifdef _OPENMP +//#pragma omp parallel for schedule(static) shared(ihomograph, interpolation) +#endif + // go over all pixels of output image + for(int j = 0; j < roi_out->height; j++) + { + float *out = ((float *)ovoid) + (size_t)ch * j * roi_out->width; + for(int i = 0; i < roi_out->width; i++, out += ch) + { + float pin[3], pout[3]; + + /* if (j == roi_out->height - 1) { */ + /* printf("HERE\n"); */ + /* } */ + + // convert output pixel coordinates to original image coordinates + pout[0] = roi_out->x + i + cx; + pout[1] = roi_out->y + j + cy; + pout[0] /= roi_out->scale; + pout[1] /= roi_out->scale; + pout[2] = 1.0f; + + // apply homograph + mat3mulv(pin, (float *)ihomograph, pout); + + // convert to input pixel coordinates + pin[0] /= pin[2]; + pin[1] /= pin[2]; + pin[0] *= roi_in->scale; + pin[1] *= roi_in->scale; + + /* if (pin[0] < 0 || pin[1] < 0) { */ + /* printf("NEGATIVE: %f %f -> %f %f\n", pout[0], pout[1], pin[0], pin[1]); */ + /* fflush(stdout); */ + /* } */ + + + pin[0] -= roi_in->x; + pin[1] -= roi_in->y; + + // get output values by interpolation from input image + dt_interpolation_compute_pixel4c(interpolation, (float *)ivoid, out, pin[0], pin[1], roi_in->width, + roi_in->height, ch_width); + } + } +} + +#ifdef HAVE_OPENCL +int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out, + const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) +{ + dt_iop_ashift_data_t *d = (dt_iop_ashift_data_t *)piece->data; + dt_iop_ashift_global_data_t *gd = (dt_iop_ashift_global_data_t *)self->data; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + const int devid = piece->pipe->devid; + const int iwidth = roi_in->width; + const int iheight = roi_in->height; + const int width = roi_out->width; + const int height = roi_out->height; + + cl_int err = -999; + cl_mem dev_homo = NULL; + + // only for preview pipe: collect input buffer data and do some other evaluations + if(self->dev->gui_attached && g && piece->pipe->type == DT_DEV_PIXELPIPE_PREVIEW) + { + // we want to find out if the final output image is flipped in relation to this iop + // so we can adjust the gui labels accordingly + + const int x_off = roi_in->x; + const int y_off = roi_in->y; + const float scale = roi_in->scale; + + // origin of image and opposite corner as reference points + float points[4] = { 0.0f, 0.0f, (float)piece->buf_in.width, (float)piece->buf_in.height }; + float ivec[2] = { points[2] - points[0], points[3] - points[1] }; + float ivecl = sqrt(ivec[0] * ivec[0] + ivec[1] * ivec[1]); + + // where do they go? + dt_dev_distort_backtransform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999, points, + 2); + + float ovec[2] = { points[2] - points[0], points[3] - points[1] }; + float ovecl = sqrt(ovec[0] * ovec[0] + ovec[1] * ovec[1]); + + // angle between input vector and output vector + float alpha = acos(CLAMP((ivec[0] * ovec[0] + ivec[1] * ovec[1]) / (ivecl * ovecl), -1.0f, 1.0f)); + + // we are interested if |alpha| is in the range of 90° +/- 45° -> we assume the image is flipped + int isflipped = fabs(fmod(alpha + M_PI, M_PI) - M_PI / 2.0f) < M_PI / 4.0f ? 1 : 0; + + // do modules coming before this one in pixelpipe have changed? -> check via hash value + uint64_t hash = dt_dev_hash_plus(self->dev, self->dev->preview_pipe, 0, self->priority - 1); + + dt_pthread_mutex_lock(&g->lock); + g->isflipped = isflipped; + + // save a copy of preview input buffer for parameter fitting + if(g->buf == NULL || (size_t)g->buf_width * g->buf_height < (size_t)iwidth * iheight) + { + // if needed allocate buffer + free(g->buf); // a no-op if g->buf is NULL + // only get new buffer if no old buffer or old buffer does not fit in terms of size + g->buf = malloc((size_t)iwidth * iheight * 4 * sizeof(float)); + } + + if(g->buf /* && hash != g->buf_hash */) + { + // copy data + err = dt_opencl_copy_device_to_host(devid, g->buf, dev_in, iwidth, iheight, 4 * sizeof(float)); + + g->buf_width = iwidth; + g->buf_height = iheight; + g->buf_x_off = x_off; + g->buf_y_off = y_off; + g->buf_scale = scale; + g->buf_hash = hash; + } + dt_pthread_mutex_unlock(&g->lock); + if(err != CL_SUCCESS) goto error; + } + + // if module is set to neutral parameters we just copy input->output and are done + if(isneutral(d)) + { + size_t origin[] = { 0, 0, 0 }; + size_t region[] = { width, height, 1 }; + err = dt_opencl_enqueue_copy_image(devid, dev_in, dev_out, origin, origin, region); + if(err != CL_SUCCESS) goto error; + return TRUE; + } + + float ihomograph[3][3]; + homography((float *)ihomograph, d->rotation, d->lensshift_v, d->lensshift_h, d->shear, d->f_length_kb, d->camera_pitch, d->camera_yaw, + d->orthocorr, d->aspect, piece->buf_in.width, piece->buf_in.height, ASHIFT_HOMOGRAPH_INVERTED); + + // clipping offset + const float fullwidth = (float)piece->buf_out.width / (d->cr - d->cl); + const float fullheight = (float)piece->buf_out.height / (d->cb - d->ct); + const float cx = roi_out->scale * fullwidth * d->cl; + const float cy = roi_out->scale * fullheight * d->ct; + + dev_homo = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 9, ihomograph); + if(dev_homo == NULL) goto error; + + const int iroi[2] = { roi_in->x, roi_in->y }; + const int oroi[2] = { roi_out->x, roi_out->y }; + const float in_scale = roi_in->scale; + const float out_scale = roi_out->scale; + const float clip[2] = { cx, cy }; + + size_t sizes[] = { ROUNDUPWD(width), ROUNDUPHT(height), 1 }; + + const struct dt_interpolation *interpolation = dt_interpolation_new(DT_INTERPOLATION_USERPREF); + + int ldkernel = -1; + + switch(interpolation->id) + { + case DT_INTERPOLATION_BILINEAR: + ldkernel = gd->kernel_ashift_bilinear; + break; + case DT_INTERPOLATION_BICUBIC: + ldkernel = gd->kernel_ashift_bicubic; + break; + case DT_INTERPOLATION_LANCZOS2: + ldkernel = gd->kernel_ashift_lanczos2; + break; + case DT_INTERPOLATION_LANCZOS3: + ldkernel = gd->kernel_ashift_lanczos3; + break; + default: + goto error; + } + + dt_opencl_set_kernel_arg(devid, ldkernel, 0, sizeof(cl_mem), (void *)&dev_in); + dt_opencl_set_kernel_arg(devid, ldkernel, 1, sizeof(cl_mem), (void *)&dev_out); + dt_opencl_set_kernel_arg(devid, ldkernel, 2, sizeof(int), (void *)&width); + dt_opencl_set_kernel_arg(devid, ldkernel, 3, sizeof(int), (void *)&height); + dt_opencl_set_kernel_arg(devid, ldkernel, 4, sizeof(int), (void *)&iwidth); + dt_opencl_set_kernel_arg(devid, ldkernel, 5, sizeof(int), (void *)&iheight); + dt_opencl_set_kernel_arg(devid, ldkernel, 6, 2 * sizeof(int), (void *)iroi); + dt_opencl_set_kernel_arg(devid, ldkernel, 7, 2 * sizeof(int), (void *)oroi); + dt_opencl_set_kernel_arg(devid, ldkernel, 8, sizeof(float), (void *)&in_scale); + dt_opencl_set_kernel_arg(devid, ldkernel, 9, sizeof(float), (void *)&out_scale); + dt_opencl_set_kernel_arg(devid, ldkernel, 10, 2 * sizeof(float), (void *)clip); + dt_opencl_set_kernel_arg(devid, ldkernel, 11, sizeof(cl_mem), (void *)&dev_homo); + err = dt_opencl_enqueue_kernel_2d(devid, ldkernel, sizes); + if(err != CL_SUCCESS) goto error; + + dt_opencl_release_mem_object(dev_homo); + return TRUE; + +error: + dt_opencl_release_mem_object(dev_homo); + dt_print(DT_DEBUG_OPENCL, "[opencl_ashift] couldn't enqueue kernel! %d\n", err); + return FALSE; +} +#endif + +// gather information about "near"-ness in g->points_idx +static void get_near(const float *points, dt_iop_ashift_points_idx_t *points_idx, const int lines_count, + float pzx, float pzy, float delta) +{ + const float delta2 = delta * delta; + + for(int n = 0; n < lines_count; n++) + { + points_idx[n].near = 0; + + // skip irrelevant lines + if(points_idx[n].type == ASHIFT_LINE_IRRELEVANT) + continue; + + // first check if the mouse pointer is outside the bounding box of the line -> skip this line + if(pzx < points_idx[n].bbx - delta && + pzx > points_idx[n].bbX + delta && + pzy < points_idx[n].bby - delta && + pzy > points_idx[n].bbY + delta) + continue; + + // pointer is inside bounding box + size_t offset = points_idx[n].offset; + const int length = points_idx[n].length; + + // sanity check (this should not happen) + if(length < 2) continue; + + // check line point by point + for(int l = 0; l < length; l++, offset++) + { + float dx = pzx - points[offset * 2]; + float dy = pzy - points[offset * 2 + 1]; + + if(dx * dx + dy * dy < delta2) + { + points_idx[n].near = 1; + break; + } + } + } +} + +// mark lines which are inside a rectangular area in isbounding mode +static void get_bounded_inside(const float *points, dt_iop_ashift_points_idx_t *points_idx, + const int points_lines_count, float pzx, float pzy, float pzx2, float pzy2, + dt_iop_ashift_bounding_t mode) +{ + // get bounding box coordinates + float ax = pzx; + float ay = pzy; + float bx = pzx2; + float by = pzy2; + if(pzx > pzx2) + { + ax = pzx2; + bx = pzx; + } + if(pzy > pzy2) + { + ay = pzy2; + by = pzy; + } + + // we either look for the selected or the deselected lines + dt_iop_ashift_linetype_t mask = ASHIFT_LINE_SELECTED; + dt_iop_ashift_linetype_t state = (mode == ASHIFT_BOUNDING_DESELECT) ? ASHIFT_LINE_SELECTED : 0; + + for(int n = 0; n < points_lines_count; n++) + { + // mark line as "not near" and "not bounded" + points_idx[n].near = 0; + points_idx[n].bounded = 0; + + // skip irrelevant lines + if(points_idx[n].type == ASHIFT_LINE_IRRELEVANT) + continue; + + // is the line inside the box ? + if(points_idx[n].bbx >= ax && points_idx[n].bbx <= bx && points_idx[n].bbX >= ax + && points_idx[n].bbX <= bx && points_idx[n].bby >= ay && points_idx[n].bby <= by + && points_idx[n].bbY >= ay && points_idx[n].bbY <= by) + { + points_idx[n].bounded = 1; + // only mark "near"-ness of those lines we are interested in + points_idx[n].near = ((points_idx[n].type & mask) != state) ? 0 : 1; + } + } +} + +// generate hash value for lines taking into account only the end point coordinates +static uint64_t get_lines_hash(const dt_iop_ashift_line_t *lines, const int lines_count) +{ + uint64_t hash = 5381; + for(int n = 0; n < lines_count; n++) + { + float v[4] = { lines[n].p1[0], lines[n].p1[1], lines[n].p2[0], lines[n].p2[1] }; + + for(int i = 0; i < 4; i++) + hash = ((hash << 5) + hash) ^ ((uint32_t *)v)[i]; + } + return hash; +} + +// update color information in points_idx if lines have changed in terms of type (but not in terms +// of number or position) +static int update_colors(struct dt_iop_module_t *self, dt_iop_ashift_points_idx_t *points_idx, + int points_lines_count) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + // is the display flipped relative to the original image? + const int isflipped = g->isflipped; + + // go through all lines + for(int n = 0; n < points_lines_count; n++) + { + const dt_iop_ashift_linetype_t type = points_idx[n].type; + + // set line color according to line type/orientation + // note: if the screen display is flipped versus the original image we need + // to respect that fact in the color selection + if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_SELECTED) + points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_BLUE : ASHIFT_LINECOLOR_GREEN; + else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_NOT_SELECTED) + points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_YELLOW : ASHIFT_LINECOLOR_RED; + else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_SELECTED) + points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_GREEN : ASHIFT_LINECOLOR_BLUE; + else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_NOT_SELECTED) + points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_RED : ASHIFT_LINECOLOR_YELLOW; + else + points_idx[n].color = ASHIFT_LINECOLOR_GREY; + } + + return TRUE; +} + +// get all the points to display lines in the gui +static int get_points(struct dt_iop_module_t *self, const dt_iop_ashift_line_t *lines, const int lines_count, + const int lines_version, float **points, dt_iop_ashift_points_idx_t **points_idx, + int *points_lines_count) +{ + dt_develop_t *dev = self->dev; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + dt_iop_ashift_points_idx_t *my_points_idx = NULL; + float *my_points = NULL; + + // is the display flipped relative to the original image? + const int isflipped = g->isflipped; + + // allocate new index array + my_points_idx = (dt_iop_ashift_points_idx_t *)malloc(lines_count * sizeof(dt_iop_ashift_points_idx_t)); + if(my_points_idx == NULL) goto error; + + // account for total number of points + size_t total_points = 0; + + // first step: basic initialization of my_points_idx and counting of total_points + for(int n = 0; n < lines_count; n++) + { + const int length = lines[n].length; + + total_points += length; + + my_points_idx[n].length = length; + my_points_idx[n].near = 0; + my_points_idx[n].bounded = 0; + + const dt_iop_ashift_linetype_t type = lines[n].type; + my_points_idx[n].type = type; + + // set line color according to line type/orientation + // note: if the screen display is flipped versus the original image we need + // to respect that fact in the color selection + if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_SELECTED) + my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_BLUE : ASHIFT_LINECOLOR_GREEN; + else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_NOT_SELECTED) + my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_YELLOW : ASHIFT_LINECOLOR_RED; + else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_SELECTED) + my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_GREEN : ASHIFT_LINECOLOR_BLUE; + else if((type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_NOT_SELECTED) + my_points_idx[n].color = isflipped ? ASHIFT_LINECOLOR_RED : ASHIFT_LINECOLOR_YELLOW; + else + my_points_idx[n].color = ASHIFT_LINECOLOR_GREY; + } + + // now allocate new points buffer + my_points = (float *)malloc((size_t)2 * total_points * sizeof(float)); + if(my_points == NULL) goto error; + + // second step: generate points for each line + for(int n = 0, offset = 0; n < lines_count; n++) + { + my_points_idx[n].offset = offset; + + float x = lines[n].p1[0]; + float y = lines[n].p1[1]; + const int length = lines[n].length; + + const float dx = (lines[n].p2[0] - x) / (float)(length - 1); + const float dy = (lines[n].p2[1] - y) / (float)(length - 1); + + for(int l = 0; l < length && offset < total_points; l++, offset++) + { + my_points[2 * offset] = x; + my_points[2 * offset + 1] = y; + + x += dx; + y += dy; + } + } + + // third step: transform all points + if(!dt_dev_distort_transform_plus(dev, dev->preview_pipe, self->priority, 9999999, my_points, total_points)) + goto error; + + // fourth step: get bounding box in final coordinates (used later for checking "near"-ness to mouse pointer) + for(int n = 0; n < lines_count; n++) + { + float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN; + + size_t offset = my_points_idx[n].offset; + int length = my_points_idx[n].length; + + for(int l = 0; l < length; l++) + { + xmin = fmin(xmin, my_points[2 * offset]); + xmax = fmax(xmax, my_points[2 * offset]); + ymin = fmin(ymin, my_points[2 * offset + 1]); + ymax = fmax(ymax, my_points[2 * offset + 1]); + } + + my_points_idx[n].bbx = xmin; + my_points_idx[n].bbX = xmax; + my_points_idx[n].bby = ymin; + my_points_idx[n].bbY = ymax; + } + + // check if lines_version has changed in-between -> too bad: we can forget about all we did :( + if(g->lines_version > lines_version) + goto error; + + *points = my_points; + *points_idx = my_points_idx; + *points_lines_count = lines_count; + + return TRUE; + +error: + if(my_points_idx != NULL) free(my_points_idx); + if(my_points != NULL) free(my_points); + return FALSE; +} + +// does this gui have focus? +static int gui_has_focus(struct dt_iop_module_t *self) +{ + return self->dev->gui_module == self; +} + +void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, + int32_t pointerx, int32_t pointery) +{ + dt_develop_t *dev = self->dev; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + + // the usual rescaling stuff + const float wd = dev->preview_pipe->backbuf_width; + const float ht = dev->preview_pipe->backbuf_height; + if(wd < 1.0 || ht < 1.0) return; + const float zoom_y = dt_control_get_dev_zoom_y(); + const float zoom_x = dt_control_get_dev_zoom_x(); + const dt_dev_zoom_t zoom = dt_control_get_dev_zoom(); + const int closeup = dt_control_get_dev_closeup(); + const float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, 1<buf has been processed + if(g->buf && (p->cropmode != ASHIFT_CROP_OFF) && self->enabled) + { + // roi data of the preview pipe input buffer + const float iwd = g->buf_width; + const float iht = g->buf_height; + const float ixo = g->buf_x_off; + const float iyo = g->buf_y_off; + + // the four corners of the input buffer of this module + const float V[4][2] = { { ixo, iyo }, + { ixo, iyo + iht }, + { ixo + iwd, iyo + iht }, + { ixo + iwd, iyo } }; + + // convert coordinates of corners to coordinates of this module's output + if(!dt_dev_distort_transform_plus(self->dev, self->dev->preview_pipe, self->priority, self->priority + 1, + (float *)V, 4)) + return; + + // get x/y-offset as well as width and height of output buffer + float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN; + for(int n = 0; n < 4; n++) + { + xmin = MIN(xmin, V[n][0]); + xmax = MAX(xmax, V[n][0]); + ymin = MIN(ymin, V[n][1]); + ymax = MAX(ymax, V[n][1]); + } + const float owd = xmax - xmin; + const float oht = ymax - ymin; + + // the four clipping corners + const float C[4][2] = { { xmin + p->cl * owd, ymin + p->ct * oht }, + { xmin + p->cl * owd, ymin + p->cb * oht }, + { xmin + p->cr * owd, ymin + p->cb * oht }, + { xmin + p->cr * owd, ymin + p->ct * oht } }; + + // convert clipping corners to final output image + if(!dt_dev_distort_transform_plus(self->dev, self->dev->preview_pipe, self->priority + 1, 9999999, + (float *)C, 4)) + return; + + cairo_save(cr); + + double dashes = DT_PIXEL_APPLY_DPI(5.0) / zoom_scale; + cairo_set_dash(cr, &dashes, 0, 0); + + cairo_rectangle(cr, 0, 0, width, height); + cairo_clip(cr); + + // mask parts of image outside of clipping area in dark grey + cairo_set_source_rgba(cr, .2, .2, .2, .8); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_rectangle(cr, 0, 0, width, height); + cairo_translate(cr, width / 2.0, height / 2.0); + cairo_scale(cr, zoom_scale, zoom_scale); + cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht); + cairo_move_to(cr, C[0][0], C[0][1]); + cairo_line_to(cr, C[1][0], C[1][1]); + cairo_line_to(cr, C[2][0], C[2][1]); + cairo_line_to(cr, C[3][0], C[3][1]); + cairo_close_path(cr); + cairo_fill(cr); + + // draw white outline around clipping area + cairo_set_source_rgb(cr, .7, .7, .7); + cairo_move_to(cr, C[0][0], C[0][1]); + cairo_line_to(cr, C[1][0], C[1][1]); + cairo_line_to(cr, C[2][0], C[2][1]); + cairo_line_to(cr, C[3][0], C[3][1]); + cairo_close_path(cr); + cairo_stroke(cr); + + // if adjusting crop, draw indicator + if (g->adjust_crop && p->cropmode == ASHIFT_CROP_ASPECT) + { + const double xpos = (C[1][0] + C[2][0]) / 2.0f; + const double ypos = (C[0][1] + C[1][1]) / 2.0f; + const double size_circle = (C[2][0] - C[1][0]) / 30.0f; + const double size_line = (C[2][0] - C[1][0]) / 5.0f; + const double size_arrow = (C[2][0] - C[1][0]) / 25.0f; + + cairo_set_line_width(cr, 2.0 / zoom_scale); + cairo_set_source_rgb(cr, .7, .7, .7); + cairo_arc (cr, xpos, ypos, size_circle, 0, 2.0 * M_PI); + cairo_stroke(cr); + cairo_fill(cr); + + cairo_set_line_width(cr, 2.0 / zoom_scale); + cairo_set_source_rgb(cr, .7, .7, .7); + + // horizontal line + cairo_move_to(cr, xpos - size_line, ypos); + cairo_line_to(cr, xpos + size_line, ypos); + + cairo_move_to(cr, xpos - size_line, ypos); + cairo_rel_line_to(cr, size_arrow, size_arrow); + cairo_move_to(cr, xpos - size_line, ypos); + cairo_rel_line_to(cr, size_arrow, -size_arrow); + + cairo_move_to(cr, xpos + size_line, ypos); + cairo_rel_line_to(cr, -size_arrow, size_arrow); + cairo_move_to(cr, xpos + size_line, ypos); + cairo_rel_line_to(cr, -size_arrow, -size_arrow); + + // vertical line + cairo_move_to(cr, xpos, ypos - size_line); + cairo_line_to(cr, xpos, ypos + size_line); + + cairo_move_to(cr, xpos, ypos - size_line); + cairo_rel_line_to(cr, -size_arrow, size_arrow); + cairo_move_to(cr, xpos, ypos - size_line); + cairo_rel_line_to(cr, size_arrow, size_arrow); + + cairo_move_to(cr, xpos, ypos + size_line); + cairo_rel_line_to(cr, -size_arrow, -size_arrow); + cairo_move_to(cr, xpos, ypos + size_line); + cairo_rel_line_to(cr, size_arrow, -size_arrow); + + cairo_stroke(cr); + } + + cairo_restore(cr); + } + + // show guide lines on request + if(g->show_guides) + { + dt_guides_t *guide = (dt_guides_t *)g_list_nth_data(darktable.guides, 0); + double dashes = DT_PIXEL_APPLY_DPI(5.0); + cairo_save(cr); + cairo_rectangle(cr, 0, 0, width, height); + cairo_clip(cr); + cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0)); + cairo_set_source_rgb(cr, .8, .8, .8); + cairo_set_dash(cr, &dashes, 1, 0); + guide->draw(cr, 0, 0, width, height, 1.0, guide->user_data); + cairo_stroke_preserve(cr); + cairo_set_dash(cr, &dashes, 0, 0); + cairo_set_source_rgba(cr, 0.3, .3, .3, .8); + cairo_stroke(cr); + cairo_restore(cr); + } + + // structural data are currently being collected or fit procedure is running? -> skip + if(g->fitting) return; + + // no structural data or visibility switched off? -> stop here + if(g->lines == NULL || g->lines_suppressed || !gui_has_focus(self)) return; + + // get hash value that changes if distortions from here to the end of the pixelpipe changed + uint64_t hash = dt_dev_hash_distort(dev); + // get hash value that changes if coordinates of lines have changed + uint64_t lines_hash = get_lines_hash(g->lines, g->lines_count); + + // points data are missing or outdated, or distortion has changed? + if(g->points == NULL || g->points_idx == NULL || hash != g->grid_hash || + (g->lines_version > g->points_version && g->lines_hash != lines_hash)) + { + // we need to reprocess points + free(g->points); + g->points = NULL; + free(g->points_idx); + g->points_idx = NULL; + g->points_lines_count = 0; + + if(!get_points(self, g->lines, g->lines_count, g->lines_version, &g->points, &g->points_idx, + &g->points_lines_count)) + return; + + g->points_version = g->lines_version; + g->grid_hash = hash; + g->lines_hash = lines_hash; + } + else if(g->lines_hash == lines_hash) + { + // update line type information in points_idx + for(int n = 0; n < g->points_lines_count; n++) + g->points_idx[n].type = g->lines[n].type; + + // coordinates of lines are unchanged -> we only need to update colors + if(!update_colors(self, g->points_idx, g->points_lines_count)) + return; + + g->points_version = g->lines_version; + } + + // a final check + if(g->points == NULL || g->points_idx == NULL) return; + + cairo_save(cr); + cairo_rectangle(cr, 0, 0, width, height); + cairo_clip(cr); + cairo_translate(cr, width / 2.0, height / 2.0); + cairo_scale(cr, zoom_scale, zoom_scale); + cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht); + + // this must match the sequence of enum dt_iop_ashift_linecolor_t! + const float line_colors[5][4] = + { { 0.3f, 0.3f, 0.3f, 0.8f }, // grey (misc. lines) + { 0.0f, 1.0f, 0.0f, 0.8f }, // green (selected vertical lines) + { 0.8f, 0.0f, 0.0f, 0.8f }, // red (de-selected vertical lines) + { 0.0f, 0.0f, 1.0f, 0.8f }, // blue (selected horizontal lines) + { 0.8f, 0.8f, 0.0f, 0.8f } }; // yellow (de-selected horizontal lines) + + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + + // now draw all lines + for(int n = 0; n < g->points_lines_count; n++) + { + // is the near flag set? -> draw line a bit thicker + if(g->points_idx[n].near) + cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(3.0) / zoom_scale); + else + cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.5) / zoom_scale); + + // the color of this line + const float *color = line_colors[g->points_idx[n].color]; + cairo_set_source_rgba(cr, color[0], color[1], color[2], color[3]); + + size_t offset = g->points_idx[n].offset; + const int length = g->points_idx[n].length; + + // sanity check (this should not happen) + if(length < 2) continue; + + // set starting point of multi-segment line + cairo_move_to(cr, g->points[offset * 2], g->points[offset * 2 + 1]); + + offset++; + // draw individual line segments + for(int l = 1; l < length; l++, offset++) + { + cairo_line_to(cr, g->points[offset * 2], g->points[offset * 2 + 1]); + } + + // finally stroke the line + cairo_stroke(cr); + } + + // and we draw the selection box if any + if(g->isbounding != ASHIFT_BOUNDING_OFF) + { + float pzx, pzy; + dt_dev_get_pointer_zoom_pos(dev, pointerx, pointery, &pzx, &pzy); + pzx += 0.5f; + pzy += 0.5f; + + double dashed[] = { 4.0, 4.0 }; + dashed[0] /= zoom_scale; + dashed[1] /= zoom_scale; + int len = sizeof(dashed) / sizeof(dashed[0]); + + cairo_rectangle(cr, g->lastx * wd, g->lasty * ht, (pzx - g->lastx) * wd, (pzy - g->lasty) * ht); + + cairo_set_source_rgba(cr, .3, .3, .3, .8); + cairo_set_line_width(cr, 1.0 / zoom_scale); + cairo_set_dash(cr, dashed, len, 0); + cairo_stroke_preserve(cr); + cairo_set_source_rgba(cr, .8, .8, .8, .8); + cairo_set_dash(cr, dashed, len, 4); + cairo_stroke(cr); + } + + // indicate which area is used for "near"-ness detection when selecting/deselecting lines + if(g->near_delta > 0) + { + float pzx, pzy; + dt_dev_get_pointer_zoom_pos(dev, pointerx, pointery, &pzx, &pzy); + pzx += 0.5f; + pzy += 0.5f; + + double dashed[] = { 4.0, 4.0 }; + dashed[0] /= zoom_scale; + dashed[1] /= zoom_scale; + int len = sizeof(dashed) / sizeof(dashed[0]); + + cairo_arc(cr, pzx * wd, pzy * ht, g->near_delta, 0, 2.0 * M_PI); + + cairo_set_source_rgba(cr, .3, .3, .3, .8); + cairo_set_line_width(cr, 1.0 / zoom_scale); + cairo_set_dash(cr, dashed, len, 0); + cairo_stroke_preserve(cr); + cairo_set_source_rgba(cr, .8, .8, .8, .8); + cairo_set_dash(cr, dashed, len, 4); + cairo_stroke(cr); + } + + cairo_restore(cr); +} + +update the number of selected vertical and horizontal lines +static void update_lines_count(const dt_iop_ashift_line_t *lines, const int lines_count, + int *vertical_count, int *horizontal_count) +{ + int vlines = 0; + int hlines = 0; + + for(int n = 0; n < lines_count; n++) + { + if((lines[n].type & ASHIFT_LINE_MASK) == ASHIFT_LINE_VERTICAL_SELECTED) + vlines++; + else if((lines[n].type & ASHIFT_LINE_MASK) == ASHIFT_LINE_HORIZONTAL_SELECTED) + hlines++; + } + + *vertical_count = vlines; + *horizontal_count = hlines; +} + +int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + int handled = 0; + + const float wd = self->dev->preview_pipe->backbuf_width; + const float ht = self->dev->preview_pipe->backbuf_height; + if(wd < 1.0 || ht < 1.0) return 1; + + float pzx, pzy; + dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy); + pzx += 0.5f; + pzy += 0.5f; + + if (g->adjust_crop) + { + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + const float newx = g->crop_cx + pzx - g->lastx; + const float newy = g->crop_cy + pzy - g->lasty; + crop_adjust(self, p, newx, newy); + dt_dev_add_history_item(darktable.develop, self, TRUE); + return TRUE; + } + + // if in rectangle selecting mode adjust "near"-ness of lines according to + // the rectangular selection + if(g->isbounding != ASHIFT_BOUNDING_OFF) + { + if(wd >= 1.0 && ht >= 1.0) + { + // mark lines inside the rectangle + get_bounded_inside(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->lastx * wd, + g->lasty * ht, g->isbounding); + } + + dt_control_queue_redraw_center(); + return FALSE; + } + + // gather information about "near"-ness in g->points_idx + get_near(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->near_delta); + + // if we are in sweeping mode iterate over lines as we move the pointer and change "selected" state. + if(g->isdeselecting || g->isselecting) + { + for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++) + { + if(g->points_idx[n].near == 0) + continue; + + if(g->isdeselecting) + g->lines[n].type &= ~ASHIFT_LINE_SELECTED; + else if(g->isselecting) + g->lines[n].type |= ASHIFT_LINE_SELECTED; + + handled = 1; + } + } + + if(handled) + { + update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count); + g->lines_version++; + g->selecting_lines_version++; + } + + dt_control_queue_redraw_center(); + + // if not in sweeping mode we need to pass the event + return (g->isdeselecting || g->isselecting); +} + +int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, + uint32_t state) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + int handled = 0; + + float pzx, pzy; + dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy); + pzx += 0.5f; + pzy += 0.5f; + + const float wd = self->dev->preview_pipe->backbuf_width; + const float ht = self->dev->preview_pipe->backbuf_height; + if(wd < 1.0 || ht < 1.0) return 1; + + + // if visibility of lines is switched off or no lines available -> potentially adjust crop area + if(g->lines_suppressed || g->lines == NULL) + { + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + if (p->cropmode == ASHIFT_CROP_ASPECT) + { + dt_control_change_cursor(GDK_HAND1); + g->adjust_crop = TRUE; + g->lastx = pzx; + g->lasty = pzy; + g->crop_cx = 0.5f * (p->cl + p->cr); + g->crop_cy = 0.5f * (p->ct + p->cb); + return TRUE; + } + else + return FALSE; + } + + // remember lines version at this stage so we can continuously monitor if the + // lines have changed in-between + g->selecting_lines_version = g->lines_version; + + // if shift button is pressed go into bounding mode (selecting or deselecting + // in a rectangle area) + if((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) + { + g->lastx = pzx; + g->lasty = pzy; + + g->isbounding = (which == 3) ? ASHIFT_BOUNDING_DESELECT : ASHIFT_BOUNDING_SELECT; + dt_control_change_cursor(GDK_CROSS); + + return TRUE; + } + + dt_dev_zoom_t zoom = dt_control_get_dev_zoom(); + const int closeup = dt_control_get_dev_closeup(); + const float min_scale = dt_dev_get_zoom_scale(self->dev, DT_ZOOM_FIT, 1<dev, zoom, 1<points_lines_count > 0); + + g->near_delta = dt_conf_get_float("plugins/darkroom/ashift/near_delta"); + + // gather information about "near"-ness in g->points_idx + get_near(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->near_delta); + + // iterate over all lines close to the pointer and change "selected" state. + // left-click selects and right-click deselects the line + for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++) + { + if(g->points_idx[n].near == 0) + continue; + + if(which == 3) + g->lines[n].type &= ~ASHIFT_LINE_SELECTED; + else + g->lines[n].type |= ASHIFT_LINE_SELECTED; + + handled = 1; + } + + // we switch into sweeping mode either if we anyhow take control + // or if cursor was close to a line when button was pressed. in other + // cases we hand over the event (for image panning) + if((take_control || handled) && which == 3) + { + dt_control_change_cursor(GDK_PIRATE); + g->isdeselecting = 1; + } + else if(take_control || handled) + { + dt_control_change_cursor(GDK_PLUS); + g->isselecting = 1; + } + + if(handled) + { + update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count); + g->lines_version++; + g->selecting_lines_version++; + } + + return (take_control || handled); +} + +int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + // stop adjust crop + g->adjust_crop = FALSE; + dt_control_change_cursor(GDK_LEFT_PTR); + + // finalize the isbounding mode + // if user has released the shift button in-between -> do nothing + if(g->isbounding != ASHIFT_BOUNDING_OFF && (state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) + { + int handled = 0; + + // we compute the rectangle selection + float pzx, pzy; + dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy); + + pzx += 0.5f; + pzy += 0.5f; + + const float wd = self->dev->preview_pipe->backbuf_width; + const float ht = self->dev->preview_pipe->backbuf_height; + + if(wd >= 1.0 && ht >= 1.0) + { + // mark lines inside the rectangle + get_bounded_inside(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->lastx * wd, + g->lasty * ht, g->isbounding); + + // select or deselect lines within the rectangle according to isbounding state + for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++) + { + if(g->points_idx[n].bounded == 0) continue; + + if(g->isbounding == ASHIFT_BOUNDING_DESELECT) + g->lines[n].type &= ~ASHIFT_LINE_SELECTED; + else + g->lines[n].type |= ASHIFT_LINE_SELECTED; + + handled = 1; + } + + if(handled) + { + update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count); + g->lines_version++; + g->selecting_lines_version++; + } + + dt_control_queue_redraw_center(); + } + } + + // end of sweeping/isbounding mode + dt_control_change_cursor(GDK_LEFT_PTR); + g->isselecting = g->isdeselecting = 0; + g->isbounding = ASHIFT_BOUNDING_OFF; + g->near_delta = 0; + g->lastx = g->lasty = -1.0f; + g->crop_cx = g->crop_cy = -1.0f; + + return 0; +} + +int scrolled(struct dt_iop_module_t *self, double x, double y, int up, uint32_t state) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + // do nothing if visibility of lines is switched off or no lines available + if(g->lines_suppressed || g->lines == NULL) + return FALSE; + + if(g->near_delta > 0 && (g->isdeselecting || g->isselecting)) + { + int handled = 0; + + float pzx, pzy; + dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy); + pzx += 0.5f; + pzy += 0.5f; + + const float wd = self->dev->preview_pipe->backbuf_width; + const float ht = self->dev->preview_pipe->backbuf_height; + + float near_delta = dt_conf_get_float("plugins/darkroom/ashift/near_delta"); + const float amount = up ? 0.8f : 1.25f; + near_delta = MAX(4.0f, MIN(near_delta * amount, 100.0f)); + dt_conf_set_float("plugins/darkroom/ashift/near_delta", near_delta); + g->near_delta = near_delta; + + // gather information about "near"-ness in g->points_idx + get_near(g->points, g->points_idx, g->points_lines_count, pzx * wd, pzy * ht, g->near_delta); + + // iterate over all lines close to the pointer and change "selected" state. + for(int n = 0; g->selecting_lines_version == g->lines_version && n < g->points_lines_count; n++) + { + if(g->points_idx[n].near == 0) + continue; + + if(g->isdeselecting) + g->lines[n].type &= ~ASHIFT_LINE_SELECTED; + else if(g->isselecting) + g->lines[n].type |= ASHIFT_LINE_SELECTED; + + handled = 1; + } + + if(handled) + { + update_lines_count(g->lines, g->lines_count, &g->vertical_count, &g->horizontal_count); + g->lines_version++; + g->selecting_lines_version++; + } + + dt_control_queue_redraw_center(); + return TRUE; + } + + return FALSE; +} + +static void rotation_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->rotation = dt_bauhaus_slider_get(slider); +#ifdef ASHIFT_DEBUG + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + model_probe(self, p, g->lastfit); +#endif + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void lensshift_v_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->lensshift_v = dt_bauhaus_slider_get(slider); +#ifdef ASHIFT_DEBUG + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + model_probe(self, p, g->lastfit); +#endif + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void lensshift_h_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->lensshift_h = dt_bauhaus_slider_get(slider); +#ifdef ASHIFT_DEBUG + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + model_probe(self, p, g->lastfit); +#endif + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void shear_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->shear = dt_bauhaus_slider_get(slider); +#ifdef ASHIFT_DEBUG + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + model_probe(self, p, g->lastfit); +#endif + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void guide_lines_callback(GtkWidget *widget, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + g->show_guides = dt_bauhaus_combobox_get(widget); + dt_iop_request_focus(self); + dt_dev_reprocess_all(self->dev); +} + +static void cropmode_callback(GtkWidget *widget, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + p->cropmode = dt_bauhaus_combobox_get(widget); + if(g->lines != NULL && !g->lines_suppressed) + { + g->lines_suppressed = 1; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->eye), g->lines_suppressed); + } + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void mode_callback(GtkWidget *widget, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + p->mode = dt_bauhaus_combobox_get(widget); + + switch(p->mode) + { + case ASHIFT_MODE_GENERIC: + gtk_widget_hide(g->f_length); + gtk_widget_hide(g->crop_factor); + gtk_widget_hide(g->orthocorr); + gtk_widget_hide(g->aspect); + break; + case ASHIFT_MODE_SPECIFIC: + default: + gtk_widget_show(g->f_length); + gtk_widget_show(g->crop_factor); + gtk_widget_show(g->orthocorr); + gtk_widget_show(g->aspect); + break; + } + + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void f_length_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->f_length = dt_bauhaus_slider_get(slider); + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void crop_factor_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->crop_factor = dt_bauhaus_slider_get(slider); + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void orthocorr_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->orthocorr = dt_bauhaus_slider_get(slider); + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static void aspect_callback(GtkWidget *slider, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(self->dt->gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + p->aspect = dt_bauhaus_slider_get(slider); + do_crop(self, p); + dt_dev_add_history_item(darktable.develop, self, TRUE); +} + +static int fit_v_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(darktable.gui->reset) return FALSE; + + if(event->button == 1) + { + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK; + const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK; + + dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; + + if(control) + g->lastfit = fitaxis = ASHIFT_FIT_ROTATION_VERTICAL_LINES; + else if(shift) + g->lastfit = fitaxis = ASHIFT_FIT_VERTICALLY_NO_ROTATION; + else + g->lastfit = fitaxis = ASHIFT_FIT_VERTICALLY; + + dt_iop_request_focus(self); + dt_dev_reprocess_all(self->dev); + + if(self->enabled) + { + // module is enable -> we process directly + if(do_fit(self, p, fitaxis)) + { + darktable.gui->reset = 1; + dt_bauhaus_slider_set_soft(g->rotation, p->rotation); + dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v); + dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h); + dt_bauhaus_slider_set_soft(g->shear, p->shear); + darktable.gui->reset = 0; + } + } + else + { + // module is not enabled -> invoke it and queue the job to be processed once + // the preview image is ready + g->jobcode = ASHIFT_JOBCODE_FIT; + g->jobparams = g->lastfit = fitaxis; + p->toggle ^= 1; + } + + dt_dev_add_history_item(darktable.develop, self, TRUE); + return TRUE; + } + return FALSE; +} + +static int fit_h_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(darktable.gui->reset) return FALSE; + + if(event->button == 1) + { + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK; + const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK; + + dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; + + if(control) + g->lastfit = fitaxis = ASHIFT_FIT_ROTATION_HORIZONTAL_LINES; + else if(shift) + g->lastfit = fitaxis = ASHIFT_FIT_HORIZONTALLY_NO_ROTATION; + else + g->lastfit = fitaxis = ASHIFT_FIT_HORIZONTALLY; + + dt_iop_request_focus(self); + dt_dev_reprocess_all(self->dev); + + if(self->enabled) + { + // module is enable -> we process directly + if(do_fit(self, p, fitaxis)) + { + darktable.gui->reset = 1; + dt_bauhaus_slider_set_soft(g->rotation, p->rotation); + dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v); + dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h); + dt_bauhaus_slider_set_soft(g->shear, p->shear); + darktable.gui->reset = 0; + } + } + else + { + // module is not enabled -> invoke it and queue the job to be processed once + // the preview image is ready + g->jobcode = ASHIFT_JOBCODE_FIT; + g->jobparams = g->lastfit = fitaxis; + p->toggle ^= 1; + } + + dt_dev_add_history_item(darktable.develop, self, TRUE); + return TRUE; + } + return FALSE; +} + +static int fit_both_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(darktable.gui->reset) return FALSE; + + if(event->button == 1) + { + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK; + const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK; + + dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; + + if(control && shift) + fitaxis = ASHIFT_FIT_BOTH; + else if(control) + fitaxis = ASHIFT_FIT_ROTATION_BOTH_LINES; + else if(shift) + fitaxis = ASHIFT_FIT_BOTH_NO_ROTATION; + else + fitaxis = ASHIFT_FIT_BOTH_SHEAR; + + dt_iop_request_focus(self); + dt_dev_reprocess_all(self->dev); + + if(self->enabled) + { + // module is enable -> we process directly + if(do_fit(self, p, fitaxis)) + { + darktable.gui->reset = 1; + dt_bauhaus_slider_set_soft(g->rotation, p->rotation); + dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v); + dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h); + dt_bauhaus_slider_set_soft(g->shear, p->shear); + darktable.gui->reset = 0; + } + } + else + { + // module is not enabled -> invoke it and queue the job to be processed once + // the preview image is ready + g->jobcode = ASHIFT_JOBCODE_FIT; + g->jobparams = g->lastfit = fitaxis; + p->toggle ^= 1; + } + + dt_dev_add_history_item(darktable.develop, self, TRUE); + return TRUE; + } + return FALSE; +} + +static int structure_button_clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(darktable.gui->reset) return FALSE; + + if(event->button == 1) + { + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + const int control = (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK; + const int shift = (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK; + + dt_iop_ashift_enhance_t enhance; + + if(control && shift) + enhance = ASHIFT_ENHANCE_EDGES | ASHIFT_ENHANCE_DETAIL; + else if(shift) + enhance = ASHIFT_ENHANCE_DETAIL; + else if(control) + enhance = ASHIFT_ENHANCE_EDGES; + else + enhance = ASHIFT_ENHANCE_NONE; + + dt_iop_request_focus(self); + dt_dev_reprocess_all(self->dev); + + if(self->enabled) + { + // module is enabled -> process directly + (void)do_get_structure(self, p, enhance); + } + else + { + // module is not enabled -> invoke it and queue the job to be processed once + // the preview image is ready + g->jobcode = ASHIFT_JOBCODE_GET_STRUCTURE; + g->jobparams = enhance; + p->toggle ^= 1; + } + + dt_dev_add_history_item(darktable.develop, self, TRUE); + return TRUE; + } + return FALSE; +} + +static void clean_button_clicked(GtkButton *button, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + if(darktable.gui->reset) return; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + (void)do_clean_structure(self, p); + dt_iop_request_focus(self); + dt_control_queue_redraw_center(); +} + +static void eye_button_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + if(darktable.gui->reset) return; + if(g->lines == NULL) + { + g->lines_suppressed = 0; + gtk_toggle_button_set_active(togglebutton, 0); + } + else + { + g->lines_suppressed = gtk_toggle_button_get_active(togglebutton); + } + dt_iop_request_focus(self); + dt_control_queue_redraw_center(); +} + +// routine that is called after preview image has been processed. we use it +// to perform structure collection or fitting in case those have been triggered while +// the module had not yet been enabled +static void process_after_preview_callback(gpointer instance, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + + dt_iop_ashift_jobcode_t jobcode = g->jobcode; + int jobparams = g->jobparams; + + // purge + g->jobcode = ASHIFT_JOBCODE_NONE; + g->jobparams = 0; + + if(darktable.gui->reset) return; + + switch(jobcode) + { + case ASHIFT_JOBCODE_GET_STRUCTURE: + (void)do_get_structure(self, p, (dt_iop_ashift_enhance_t)jobparams); + break; + + case ASHIFT_JOBCODE_FIT: + if(do_fit(self, p, (dt_iop_ashift_fitaxis_t)jobparams)) + { + darktable.gui->reset = 1; + dt_bauhaus_slider_set_soft(g->rotation, p->rotation); + dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v); + dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h); + dt_bauhaus_slider_set_soft(g->shear, p->shear); + darktable.gui->reset = 0; + } + dt_dev_add_history_item(darktable.develop, self, TRUE); + break; + + case ASHIFT_JOBCODE_NONE: + default: + break; + } + + dt_control_queue_redraw_center(); +} + +void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, + dt_dev_pixelpipe_iop_t *piece) +{ + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)p1; + dt_iop_ashift_data_t *d = (dt_iop_ashift_data_t *)piece->data; + + d->rotation = p->rotation; + d->lensshift_v = p->lensshift_v; + d->lensshift_h = p->lensshift_h; + d->shear = p->shear; + d->f_length_kb = (p->mode == ASHIFT_MODE_GENERIC) ? DEFAULT_F_LENGTH : p->f_length * p->crop_factor; + d->orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr; + d->aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect; + + if(gui_has_focus(self)) + { + // if gui has focus we want to see the full uncropped image + d->cl = 0.0f; + d->cr = 1.0f; + d->ct = 0.0f; + d->cb = 1.0f; + } + else + { + d->cl = p->cl; + d->cr = p->cr; + d->ct = p->ct; + d->cb = p->cb; + } +} + +void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) +{ + dt_iop_ashift_data_t *d = (dt_iop_ashift_data_t *)calloc(1, sizeof(dt_iop_ashift_data_t)); + piece->data = (void *)d; + self->commit_params(self, self->default_params, pipe, piece); +} + +void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) +{ + free(piece->data); + piece->data = NULL; +} + +void gui_update(struct dt_iop_module_t *self) +{ + dt_iop_module_t *module = (dt_iop_module_t *)self; + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)module->params; + dt_bauhaus_slider_set_soft(g->rotation, p->rotation); + dt_bauhaus_slider_set_soft(g->lensshift_v, p->lensshift_v); + dt_bauhaus_slider_set_soft(g->lensshift_h, p->lensshift_h); + dt_bauhaus_slider_set_soft(g->shear, p->shear); + dt_bauhaus_slider_set_soft(g->f_length, p->f_length); + dt_bauhaus_slider_set_soft(g->crop_factor, p->crop_factor); + dt_bauhaus_slider_set(g->orthocorr, p->orthocorr); + dt_bauhaus_slider_set(g->aspect, p->aspect); + dt_bauhaus_combobox_set(g->mode, p->mode); + dt_bauhaus_combobox_set(g->guide_lines, g->show_guides); + dt_bauhaus_combobox_set(g->cropmode, p->cropmode); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->eye), 0); + + switch(p->mode) + { + case ASHIFT_MODE_GENERIC: + gtk_widget_hide(g->f_length); + gtk_widget_hide(g->crop_factor); + gtk_widget_hide(g->orthocorr); + gtk_widget_hide(g->aspect); + break; + case ASHIFT_MODE_SPECIFIC: + default: + gtk_widget_show(g->f_length); + gtk_widget_show(g->crop_factor); + gtk_widget_show(g->orthocorr); + gtk_widget_show(g->aspect); + break; + } +} + +void init(dt_iop_module_t *module) +{ + module->params = calloc(1, sizeof(dt_iop_ashift_params_t)); + module->default_params = calloc(1, sizeof(dt_iop_ashift_params_t)); + module->default_enabled = 0; + module->priority = 214; // module order created by iop_dependencies.py, do not edit! + module->params_size = sizeof(dt_iop_ashift_params_t); + module->gui_data = NULL; + dt_iop_ashift_params_t tmp = (dt_iop_ashift_params_t){ 0.0f, 0.0f, 0.0f, 0.0f, DEFAULT_F_LENGTH, 1.0f, 100.0f, 1.0f, ASHIFT_MODE_GENERIC, 0, + ASHIFT_CROP_OFF, 0.0f, 1.0f, 0.0f, 1.0f }; + memcpy(module->params, &tmp, sizeof(dt_iop_ashift_params_t)); + memcpy(module->default_params, &tmp, sizeof(dt_iop_ashift_params_t)); +} + +void reload_defaults(dt_iop_module_t *module) +{ + // our module is disabled by default + module->default_enabled = 0; + + int isflipped = 0; + float f_length = DEFAULT_F_LENGTH; + float crop_factor = 1.0f; + + // try to get information on orientation, focal length and crop factor from image data + if(module->dev) + { + const dt_image_t *img = &module->dev->image_storage; + // orientation only needed as a-priori information to correctly label some sliders + // before pixelpipe has been set up. later we will get a definite result by + // assessing the pixelpipe + isflipped = (img->orientation == ORIENTATION_ROTATE_CCW_90_DEG + || img->orientation == ORIENTATION_ROTATE_CW_90_DEG) + ? 1 + : 0; + + // focal length should be available in exif data if lens is electronically coupled to the camera + f_length = isfinite(img->exif_focal_length) && img->exif_focal_length > 0.0f ? img->exif_focal_length : f_length; + // crop factor of the camera is often not available and user will need to set it manually in the gui + crop_factor = isfinite(img->exif_crop) && img->exif_crop > 0.0f ? img->exif_crop : crop_factor; + } + + // init defaults: + dt_iop_ashift_params_t tmp = (dt_iop_ashift_params_t){ 0.0f, 0.0f, 0.0f, 0.0f, f_length, crop_factor, 100.0f, 1.0f, ASHIFT_MODE_GENERIC, 0, + ASHIFT_CROP_OFF, 0.0f, 1.0f, 0.0f, 1.0f }; + memcpy(module->params, &tmp, sizeof(dt_iop_ashift_params_t)); + memcpy(module->default_params, &tmp, sizeof(dt_iop_ashift_params_t)); + + // reset gui elements + if(module->gui_data) + { + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)module->gui_data; + + char string_v[256]; + char string_h[256]; + + snprintf(string_v, sizeof(string_v), _("lens shift (%s)"), isflipped ? _("horizontal") : _("vertical")); + snprintf(string_h, sizeof(string_h), _("lens shift (%s)"), isflipped ? _("vertical") : _("horizontal")); + + dt_bauhaus_widget_set_label(g->lensshift_v, NULL, string_v); + dt_bauhaus_widget_set_label(g->lensshift_h, NULL, string_h); + + dt_bauhaus_slider_set_default(g->f_length, tmp.f_length); + dt_bauhaus_slider_set_default(g->crop_factor, tmp.crop_factor); + + dt_pthread_mutex_lock(&g->lock); + free(g->buf); + g->buf = NULL; + g->buf_width = 0; + g->buf_height = 0; + g->buf_x_off = 0; + g->buf_y_off = 0; + g->buf_scale = 1.0f; + g->buf_hash = 0; + g->isflipped = -1; + g->lastfit = ASHIFT_FIT_NONE; + dt_pthread_mutex_unlock(&g->lock); + + g->fitting = 0; + free(g->lines); + g->lines = NULL; + g->lines_count =0; + g->horizontal_count = 0; + g->vertical_count = 0; + g->grid_hash = 0; + g->lines_hash = 0; + g->rotation_range = ROTATION_RANGE_SOFT; + g->lensshift_v_range = LENSSHIFT_RANGE_SOFT; + g->lensshift_h_range = LENSSHIFT_RANGE_SOFT; + g->shear_range = SHEAR_RANGE_SOFT; + g->lines_suppressed = 0; + g->lines_version = 0; + g->show_guides = 0; + g->isselecting = 0; + g->isdeselecting = 0; + g->isbounding = ASHIFT_BOUNDING_OFF; + g->near_delta = 0; + g->selecting_lines_version = 0; + + free(g->points); + g->points = NULL; + free(g->points_idx); + g->points_idx = NULL; + g->points_lines_count = 0; + g->points_version = 0; + + g->jobcode = ASHIFT_JOBCODE_NONE; + g->jobparams = 0; + g->adjust_crop = FALSE; + g->lastx = g->lasty = -1.0f; + g->crop_cx = g->crop_cy = 1.0f; + } +} + + +void init_global(dt_iop_module_so_t *module) +{ + dt_iop_ashift_global_data_t *gd + = (dt_iop_ashift_global_data_t *)malloc(sizeof(dt_iop_ashift_global_data_t)); + module->data = gd; + + const int program = 2; // basic.cl, from programs.conf + gd->kernel_ashift_bilinear = dt_opencl_create_kernel(program, "ashift_bilinear"); + gd->kernel_ashift_bicubic = dt_opencl_create_kernel(program, "ashift_bicubic"); + gd->kernel_ashift_lanczos2 = dt_opencl_create_kernel(program, "ashift_lanczos2"); + gd->kernel_ashift_lanczos3 = dt_opencl_create_kernel(program, "ashift_lanczos3"); +} + +void cleanup(dt_iop_module_t *module) +{ + free(module->params); + module->params = NULL; +} + +void cleanup_global(dt_iop_module_so_t *module) +{ + dt_iop_ashift_global_data_t *gd = (dt_iop_ashift_global_data_t *)module->data; + dt_opencl_free_kernel(gd->kernel_ashift_bilinear); + dt_opencl_free_kernel(gd->kernel_ashift_bicubic); + dt_opencl_free_kernel(gd->kernel_ashift_lanczos2); + dt_opencl_free_kernel(gd->kernel_ashift_lanczos3); + free(module->data); + module->data = NULL; +} + +// adjust labels of lens shift parameters according to flip status of image +static gboolean draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self) +{ + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + if(darktable.gui->reset) return FALSE; + + dt_pthread_mutex_lock(&g->lock); + const int isflipped = g->isflipped; + dt_pthread_mutex_unlock(&g->lock); + + if(isflipped == -1) return FALSE; + + char string_v[256]; + char string_h[256]; + + snprintf(string_v, sizeof(string_v), _("lens shift (%s)"), isflipped ? _("horizontal") : _("vertical")); + snprintf(string_h, sizeof(string_h), _("lens shift (%s)"), isflipped ? _("vertical") : _("horizontal")); + + darktable.gui->reset = 1; + dt_bauhaus_widget_set_label(g->lensshift_v, NULL, string_v); + dt_bauhaus_widget_set_label(g->lensshift_h, NULL, string_h); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->eye), g->lines_suppressed); + darktable.gui->reset = 0; + + return FALSE; +} + +void gui_focus(struct dt_iop_module_t *self, gboolean in) +{ + if(self->enabled) + dt_dev_reprocess_all(self->dev); +} + +static float log10_callback(GtkWidget *self, float inval, dt_bauhaus_callback_t dir) +{ + float outval; + switch(dir) + { + case DT_BAUHAUS_SET: + outval = log10(fmax(inval, 1e-15f)); + break; + case DT_BAUHAUS_GET: + outval = exp(M_LN10 * inval); + break; + default: + outval = inval; + } + return outval; +} + +static float log2_callback(GtkWidget *self, float inval, dt_bauhaus_callback_t dir) +{ + float outval; + switch(dir) + { + case DT_BAUHAUS_SET: + outval = log(fmax(inval, 1e-15f)) / M_LN2; + break; + case DT_BAUHAUS_GET: + outval = exp(M_LN2 * inval); + break; + default: + outval = inval; + } + return outval; +} + +void gui_init(struct dt_iop_module_t *self) +{ + self->gui_data = malloc(sizeof(dt_iop_ashift_gui_data_t)); + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + dt_iop_ashift_params_t *p = (dt_iop_ashift_params_t *)self->params; + + dt_pthread_mutex_init(&g->lock, NULL); + dt_pthread_mutex_lock(&g->lock); + g->buf = NULL; + g->buf_width = 0; + g->buf_height = 0; + g->buf_x_off = 0; + g->buf_y_off = 0; + g->buf_scale = 1.0f; + g->buf_hash = 0; + g->isflipped = -1; + g->lastfit = ASHIFT_FIT_NONE; + dt_pthread_mutex_unlock(&g->lock); + + g->fitting = 0; + g->lines = NULL; + g->lines_count = 0; + g->vertical_count = 0; + g->horizontal_count = 0; + g->lines_version = 0; + g->lines_suppressed = 0; + g->points = NULL; + g->points_idx = NULL; + g->points_lines_count = 0; + g->points_version = 0; + g->grid_hash = 0; + g->lines_hash = 0; + g->rotation_range = ROTATION_RANGE_SOFT; + g->lensshift_v_range = LENSSHIFT_RANGE_SOFT; + g->lensshift_h_range = LENSSHIFT_RANGE_SOFT; + g->shear_range = SHEAR_RANGE_SOFT; + g->show_guides = 0; + g->isselecting = 0; + g->isdeselecting = 0; + g->isbounding = ASHIFT_BOUNDING_OFF; + g->near_delta = 0; + g->selecting_lines_version = 0; + + g->jobcode = ASHIFT_JOBCODE_NONE; + g->jobparams = 0; + g->adjust_crop = FALSE; + g->lastx = g->lasty = -1.0f; + g->crop_cx = g->crop_cy = 1.0f; + + self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE); + dt_gui_add_help_link(self->widget, dt_get_help_url(self->op)); + + g->rotation = dt_bauhaus_slider_new_with_range(self, -ROTATION_RANGE, ROTATION_RANGE, 0.01*ROTATION_RANGE, p->rotation, 2); + dt_bauhaus_widget_set_label(g->rotation, NULL, _("rotation")); + dt_bauhaus_slider_set_format(g->rotation, "%.2f°"); + dt_bauhaus_slider_enable_soft_boundaries(g->rotation, -ROTATION_RANGE_SOFT, ROTATION_RANGE_SOFT); + gtk_box_pack_start(GTK_BOX(self->widget), g->rotation, TRUE, TRUE, 0); + + g->lensshift_v = dt_bauhaus_slider_new_with_range(self, -LENSSHIFT_RANGE, LENSSHIFT_RANGE, 0.01*LENSSHIFT_RANGE, p->lensshift_v, 3); + dt_bauhaus_widget_set_label(g->lensshift_v, NULL, _("lens shift (vertical)")); + dt_bauhaus_slider_enable_soft_boundaries(g->lensshift_v, -LENSSHIFT_RANGE_SOFT, LENSSHIFT_RANGE_SOFT); + gtk_box_pack_start(GTK_BOX(self->widget), g->lensshift_v, TRUE, TRUE, 0); + + g->lensshift_h = dt_bauhaus_slider_new_with_range(self, -LENSSHIFT_RANGE, LENSSHIFT_RANGE, 0.01*LENSSHIFT_RANGE, p->lensshift_h, 3); + dt_bauhaus_widget_set_label(g->lensshift_h, NULL, _("lens shift (horizontal)")); + dt_bauhaus_slider_enable_soft_boundaries(g->lensshift_h, -LENSSHIFT_RANGE_SOFT, LENSSHIFT_RANGE_SOFT); + gtk_box_pack_start(GTK_BOX(self->widget), g->lensshift_h, TRUE, TRUE, 0); + + g->shear = dt_bauhaus_slider_new_with_range(self, -SHEAR_RANGE, SHEAR_RANGE, 0.01*SHEAR_RANGE, p->shear, 3); + dt_bauhaus_widget_set_label(g->shear, NULL, _("shear")); + dt_bauhaus_slider_enable_soft_boundaries(g->shear, -SHEAR_RANGE_SOFT, SHEAR_RANGE_SOFT); + gtk_box_pack_start(GTK_BOX(self->widget), g->shear, TRUE, TRUE, 0); + + g->guide_lines = dt_bauhaus_combobox_new(self); + dt_bauhaus_widget_set_label(g->guide_lines, NULL, _("guides")); + dt_bauhaus_combobox_add(g->guide_lines, _("off")); + dt_bauhaus_combobox_add(g->guide_lines, _("on")); + gtk_box_pack_start(GTK_BOX(self->widget), g->guide_lines, TRUE, TRUE, 0); + + g->cropmode = dt_bauhaus_combobox_new(self); + dt_bauhaus_widget_set_label(g->cropmode, NULL, _("automatic cropping")); + dt_bauhaus_combobox_add(g->cropmode, _("off")); + dt_bauhaus_combobox_add(g->cropmode, _("largest area")); + dt_bauhaus_combobox_add(g->cropmode, _("original format")); + gtk_box_pack_start(GTK_BOX(self->widget), g->cropmode, TRUE, TRUE, 0); + + g->mode = dt_bauhaus_combobox_new(self); + dt_bauhaus_widget_set_label(g->mode, NULL, _("lens model")); + dt_bauhaus_combobox_add(g->mode, _("generic")); + dt_bauhaus_combobox_add(g->mode, _("specific")); + gtk_box_pack_start(GTK_BOX(self->widget), g->mode, TRUE, TRUE, 0); + + g->f_length = dt_bauhaus_slider_new_with_range(self, 1.0f, 3.0f, 0.01f, 1.0f, 2); + dt_bauhaus_widget_set_label(g->f_length, NULL, _("focal length")); + dt_bauhaus_slider_set_callback(g->f_length, log10_callback); + dt_bauhaus_slider_set_format(g->f_length, "%.0fmm"); + dt_bauhaus_slider_set_default(g->f_length, DEFAULT_F_LENGTH); + dt_bauhaus_slider_set(g->f_length, DEFAULT_F_LENGTH); + dt_bauhaus_slider_enable_soft_boundaries(g->f_length, 1.0f, 2000.0f); + gtk_box_pack_start(GTK_BOX(self->widget), g->f_length, TRUE, TRUE, 0); + + g->crop_factor = dt_bauhaus_slider_new_with_range(self, 1.0f, 2.0f, 0.01f, p->crop_factor, 2); + dt_bauhaus_widget_set_label(g->crop_factor, NULL, _("crop factor")); + dt_bauhaus_slider_enable_soft_boundaries(g->crop_factor, 0.5f, 10.0f); + gtk_box_pack_start(GTK_BOX(self->widget), g->crop_factor, TRUE, TRUE, 0); + + g->orthocorr = dt_bauhaus_slider_new_with_range(self, 0.0f, 100.0f, 1.0f, p->orthocorr, 2); + dt_bauhaus_widget_set_label(g->orthocorr, NULL, _("lens dependence")); + dt_bauhaus_slider_set_format(g->orthocorr, "%.0f%%"); +#if 0 + // this parameter could serve to finetune between generic model (0%) and specific model (100%). + // however, users can more easily get the same effect with the aspect adjust parameter so we keep + // this one hidden. + gtk_box_pack_start(GTK_BOX(self->widget), g->orthocorr, TRUE, TRUE, 0); +#endif + + g->aspect = dt_bauhaus_slider_new_with_range(self, -1.0f, 1.0f, 0.01f, 0.0f, 2); + dt_bauhaus_widget_set_label(g->aspect, NULL, _("aspect adjust")); + dt_bauhaus_slider_set_callback(g->aspect, log2_callback); + dt_bauhaus_slider_set_default(g->aspect, 1.0f); + dt_bauhaus_slider_set(g->aspect, 1.0f); + gtk_box_pack_start(GTK_BOX(self->widget), g->aspect, TRUE, TRUE, 0); + + GtkWidget *grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 2 * DT_BAUHAUS_SPACE); + gtk_grid_set_column_spacing(GTK_GRID(grid), DT_PIXEL_APPLY_DPI(10)); + + GtkWidget *label1 = gtk_label_new(_("automatic fit")); + gtk_widget_set_halign(label1, GTK_ALIGN_START); + gtk_grid_attach(GTK_GRID(grid), label1, 0, 0, 1, 1); + + g->fit_v = dtgtk_button_new(dtgtk_cairo_paint_perspective, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | 1, NULL); + gtk_widget_set_hexpand(GTK_WIDGET(g->fit_v), TRUE); + gtk_widget_set_size_request(g->fit_v, -1, DT_PIXEL_APPLY_DPI(24)); + gtk_grid_attach_next_to(GTK_GRID(grid), g->fit_v, label1, GTK_POS_RIGHT, 1, 1); + + g->fit_h = dtgtk_button_new(dtgtk_cairo_paint_perspective, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | 2, NULL); + gtk_widget_set_hexpand(GTK_WIDGET(g->fit_h), TRUE); + gtk_widget_set_size_request(g->fit_h, -1, DT_PIXEL_APPLY_DPI(24)); + gtk_grid_attach_next_to(GTK_GRID(grid), g->fit_h, g->fit_v, GTK_POS_RIGHT, 1, 1); + + g->fit_both = dtgtk_button_new(dtgtk_cairo_paint_perspective, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER | 3, NULL); + gtk_widget_set_hexpand(GTK_WIDGET(g->fit_both), TRUE); + gtk_widget_set_size_request(g->fit_both, -1, DT_PIXEL_APPLY_DPI(24)); + gtk_grid_attach_next_to(GTK_GRID(grid), g->fit_both, g->fit_h, GTK_POS_RIGHT, 1, 1); + + GtkWidget *label2 = gtk_label_new(_("get structure")); + gtk_widget_set_halign(label2, GTK_ALIGN_START); + gtk_grid_attach(GTK_GRID(grid), label2, 0, 1, 1, 1); + + g->structure = dtgtk_button_new(dtgtk_cairo_paint_structure, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); + gtk_widget_set_hexpand(GTK_WIDGET(g->structure), TRUE); + gtk_grid_attach_next_to(GTK_GRID(grid), g->structure, label2, GTK_POS_RIGHT, 1, 1); + + g->clean = dtgtk_button_new(dtgtk_cairo_paint_cancel, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); + gtk_widget_set_hexpand(GTK_WIDGET(g->clean), TRUE); + gtk_grid_attach_next_to(GTK_GRID(grid), g->clean, g->structure, GTK_POS_RIGHT, 1, 1); + + g->eye = dtgtk_togglebutton_new(dtgtk_cairo_paint_eye_toggle, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); + gtk_widget_set_hexpand(GTK_WIDGET(g->eye), TRUE); + gtk_grid_attach_next_to(GTK_GRID(grid), g->eye, g->clean, GTK_POS_RIGHT, 1, 1); + + gtk_box_pack_start(GTK_BOX(self->widget), grid, TRUE, TRUE, 0); + + gtk_widget_show_all(g->f_length); + gtk_widget_set_no_show_all(g->f_length, TRUE); + gtk_widget_show_all(g->crop_factor); + gtk_widget_set_no_show_all(g->crop_factor, TRUE); + gtk_widget_show_all(g->orthocorr); + gtk_widget_set_no_show_all(g->orthocorr, TRUE); + gtk_widget_show_all(g->aspect); + gtk_widget_set_no_show_all(g->aspect, TRUE); + + + switch(p->mode) + { + case ASHIFT_MODE_GENERIC: + gtk_widget_hide(g->f_length); + gtk_widget_hide(g->crop_factor); + gtk_widget_hide(g->orthocorr); + gtk_widget_hide(g->aspect); + break; + case ASHIFT_MODE_SPECIFIC: + default: + gtk_widget_show(g->f_length); + gtk_widget_show(g->crop_factor); + gtk_widget_show(g->orthocorr); + gtk_widget_show(g->aspect); + break; + } + + gtk_widget_set_tooltip_text(g->rotation, _("rotate image")); + gtk_widget_set_tooltip_text(g->lensshift_v, _("apply lens shift correction in one direction")); + gtk_widget_set_tooltip_text(g->lensshift_h, _("apply lens shift correction in one direction")); + gtk_widget_set_tooltip_text(g->shear, _("shear the image along one diagonal")); + gtk_widget_set_tooltip_text(g->guide_lines, _("display guide lines overlay")); + gtk_widget_set_tooltip_text(g->cropmode, _("automatically crop to avoid black edges")); + gtk_widget_set_tooltip_text(g->mode, _("lens model of the perspective correction: " + "generic or according to the focal length")); + gtk_widget_set_tooltip_text(g->f_length, _("focal length of the lens, " + "default value set from exif data if available")); + gtk_widget_set_tooltip_text(g->crop_factor, _("crop factor of the camera sensor, " + "default value set from exif data if available, " + "manual setting is often required")); + gtk_widget_set_tooltip_text(g->orthocorr, _("the level of lens dependent correction, set to maximum for full lens dependency, " + "set to zero for the generic case")); + gtk_widget_set_tooltip_text(g->aspect, _("adjust aspect ratio of image by horizontal and vertical scaling")); + gtk_widget_set_tooltip_text(g->fit_v, _("automatically correct for vertical perspective distortion\n" + "ctrl-click to only fit rotation\n" + "shift-click to only fit lens shift")); + gtk_widget_set_tooltip_text(g->fit_h, _("automatically correct for horizontal perspective distortion\n" + "ctrl-click to only fit rotation\n" + "shift-click to only fit lens shift")); + gtk_widget_set_tooltip_text(g->fit_both, _("automatically correct for vertical and " + "horizontal perspective distortions; fitting rotation," + "lens shift in both directions, and shear\n" + "ctrl-click to only fit rotation\n" + "shift-click to only fit lens shift\n" + "ctrl-shift-click to only fit rotation and lens shift")); + gtk_widget_set_tooltip_text(g->structure, _("analyse line structure in image\n" + "ctrl-click for an additional edge enhancement\n" + "shift-click for an additional detail enhancement\n" + "ctrl-shift-click for a combination of both methods")); + gtk_widget_set_tooltip_text(g->clean, _("remove line structure information")); + gtk_widget_set_tooltip_text(g->eye, _("toggle visibility of structure lines")); + + g_signal_connect(G_OBJECT(g->rotation), "value-changed", G_CALLBACK(rotation_callback), self); + g_signal_connect(G_OBJECT(g->lensshift_v), "value-changed", G_CALLBACK(lensshift_v_callback), self); + g_signal_connect(G_OBJECT(g->lensshift_h), "value-changed", G_CALLBACK(lensshift_h_callback), self); + g_signal_connect(G_OBJECT(g->shear), "value-changed", G_CALLBACK(shear_callback), self); + g_signal_connect(G_OBJECT(g->guide_lines), "value-changed", G_CALLBACK(guide_lines_callback), self); + g_signal_connect(G_OBJECT(g->cropmode), "value-changed", G_CALLBACK(cropmode_callback), self); + g_signal_connect(G_OBJECT(g->mode), "value-changed", G_CALLBACK(mode_callback), self); + g_signal_connect(G_OBJECT(g->f_length), "value-changed", G_CALLBACK(f_length_callback), self); + g_signal_connect(G_OBJECT(g->crop_factor), "value-changed", G_CALLBACK(crop_factor_callback), self); + g_signal_connect(G_OBJECT(g->orthocorr), "value-changed", G_CALLBACK(orthocorr_callback), self); + g_signal_connect(G_OBJECT(g->aspect), "value-changed", G_CALLBACK(aspect_callback), self); + g_signal_connect(G_OBJECT(g->fit_v), "button-press-event", G_CALLBACK(fit_v_button_clicked), (gpointer)self); + g_signal_connect(G_OBJECT(g->fit_h), "button-press-event", G_CALLBACK(fit_h_button_clicked), (gpointer)self); + g_signal_connect(G_OBJECT(g->fit_both), "button-press-event", G_CALLBACK(fit_both_button_clicked), (gpointer)self); + g_signal_connect(G_OBJECT(g->structure), "button-press-event", G_CALLBACK(structure_button_clicked), (gpointer)self); + g_signal_connect(G_OBJECT(g->clean), "clicked", G_CALLBACK(clean_button_clicked), (gpointer)self); + g_signal_connect(G_OBJECT(g->eye), "toggled", G_CALLBACK(eye_button_toggled), (gpointer)self); + g_signal_connect(G_OBJECT(self->widget), "draw", G_CALLBACK(draw), self); + + /* add signal handler for preview pipe finish to redraw the overlay */ + dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED, + G_CALLBACK(process_after_preview_callback), self); + +} + +void gui_cleanup(struct dt_iop_module_t *self) +{ + dt_control_signal_disconnect(darktable.signals, G_CALLBACK(process_after_preview_callback), self); + + dt_iop_ashift_gui_data_t *g = (dt_iop_ashift_gui_data_t *)self->gui_data; + dt_pthread_mutex_destroy(&g->lock); + free(g->lines); + free(g->buf); + free(g->points); + free(g->points_idx); + free(self->gui_data); + self->gui_data = NULL; +} +#endif // if 0 +//----------------------------------------------------------------------------- + +// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh +// vim: shiftwidth=2 expandtab tabstop=2 cindent +// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified; diff --git a/rtengine/ashift_lsd.c b/rtengine/ashift_lsd.c new file mode 100644 index 000000000..a884da437 --- /dev/null +++ b/rtengine/ashift_lsd.c @@ -0,0 +1,2335 @@ +/* + This file is part of darktable, + copyright (c) 2016 Ulrich Pegelow. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ + + +/* For line detection we are using the LSD code as published below. + * Changes versus the original code: + * do not include "lsd.h" (not needed) + * make all interface functions static + * comment out unsused interface functions + * catch (unlikely) division by zero near line 2035 + * rename rad1 and rad2 to radius1 and radius2 in reduce_region_radius() + * to avoid naming conflict in windows build + * + */ + +/* TODO: LSD employs intensive sanity checks of input data and allocation + * results. In case of errors it will terminate the program. On the one side + * this is not optimal for a module within darktable - it should better + * report errors upstream where they should be handled properly. On the other + * hand the kind of error which could be triggered in LSD would make it very unlikely + * that the darktable process could survive anyhow. + */ + +// clang-format off + +/*================================================================================== + * begin LSD code version 1.6. downloaded on January 30, 2016 + *==================================================================================*/ + +/*---------------------------------------------------------------------------- + + LSD - Line Segment Detector on digital images + + This code is part of the following publication and was subject + to peer review: + + "LSD: a Line Segment Detector" by Rafael Grompone von Gioi, + Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall, + Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd + http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd + + Copyright (c) 2007-2011 rafael grompone von gioi + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + ----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** @file lsd.c + LSD module code + @author rafael grompone von gioi + */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** @mainpage LSD code documentation + + This is an implementation of the Line Segment Detector described + in the paper: + + "LSD: A Fast Line Segment Detector with a False Detection Control" + by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel, + and Gregory Randall, IEEE Transactions on Pattern Analysis and + Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010. + + and in more details in the CMLA Technical Report: + + "LSD: A Line Segment Detector, Technical Report", + by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel, + Gregory Randall, CMLA, ENS Cachan, 2010. + + The version implemented here includes some further improvements + described in the following publication, of which this code is part: + + "LSD: a Line Segment Detector" by Rafael Grompone von Gioi, + Jeremie Jakubowicz, Jean-Michel Morel, and Gregory Randall, + Image Processing On Line, 2012. DOI:10.5201/ipol.2012.gjmr-lsd + http://dx.doi.org/10.5201/ipol.2012.gjmr-lsd + + The module's main function is lsd(). + + The source code is contained in two files: lsd.h and lsd.c. + + HISTORY: + - version 1.6 - nov 2011: + - changes in the interface, + - max_grad parameter removed, + - the factor 11 was added to the number of test + to consider the different precision values + tested, + - a minor bug corrected in the gradient sorting + code, + - the algorithm now also returns p and log_nfa + for each detection, + - a minor bug was corrected in the image scaling, + - the angle comparison in "isaligned" changed + from < to <=, + - "eps" variable renamed "log_eps", + - "lsd_scale_region" interface was added, + - minor changes to comments. + - version 1.5 - dec 2010: Changes in 'refine', -W option added, + and more comments added. + - version 1.4 - jul 2010: lsd_scale interface added and doxygen doc. + - version 1.3 - feb 2010: Multiple bug correction and improved code. + - version 1.2 - dec 2009: First full Ansi C Language version. + - version 1.1 - sep 2009: Systematic subsampling to scale 0.8 and + correction to partially handle "angle problem". + - version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language + version. + + @author rafael grompone von gioi + */ +/*----------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +//#include "lsd.h" + +/** ln(10) */ +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 +#endif /* !M_LN10 */ + +/** PI */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* !M_PI */ + +#ifndef FALSE +#define FALSE 0 +#endif /* !FALSE */ + +#ifndef TRUE +#define TRUE 1 +#endif /* !TRUE */ + +/** Label for pixels with undefined gradient. */ +#define NOTDEF -1024.0 + +/** 3/2 pi */ +#define M_3_2_PI 4.71238898038 + +/** 2 pi */ +#define M_2__PI 6.28318530718 + +/** Label for pixels not used in yet. */ +#define NOTUSED 0 + +/** Label for pixels already used in detection. */ +#define USED 1 + +/*----------------------------------------------------------------------------*/ +/** Chained list of coordinates. + */ +struct coorlist +{ + int x,y; + struct coorlist * next; +}; + +/*----------------------------------------------------------------------------*/ +/** A point (or pixel). + */ +struct point {int x,y;}; + + +/*----------------------------------------------------------------------------*/ +/*------------------------- Miscellaneous functions --------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** Fatal error, print a message to standard-error output and exit. + */ +static void error(const char * msg) +{ + fprintf(stderr,"LSD Error: %s\n",msg); + exit(EXIT_FAILURE); +} + +/*----------------------------------------------------------------------------*/ +/** Doubles relative error factor + */ +#define RELATIVE_ERROR_FACTOR 100.0 + +/*----------------------------------------------------------------------------*/ +/** Compare doubles by relative error. + + The resulting rounding error after floating point computations + depend on the specific operations done. The same number computed by + different algorithms could present different rounding errors. For a + useful comparison, an estimation of the relative rounding error + should be considered and compared to a factor times EPS. The factor + should be related to the cumulated rounding error in the chain of + computation. Here, as a simplification, a fixed factor is used. + */ +static int double_equal(double a, double b) +{ + double abs_diff,aa,bb,abs_max; + + /* trivial case */ + if( a == b ) return TRUE; + + abs_diff = fabs(a-b); + aa = fabs(a); + bb = fabs(b); + abs_max = aa > bb ? aa : bb; + + /* DBL_MIN is the smallest normalized number, thus, the smallest + number whose relative error is bounded by DBL_EPSILON. For + smaller numbers, the same quantization steps as for DBL_MIN + are used. Then, for smaller numbers, a meaningful "relative" + error should be computed by dividing the difference by DBL_MIN. */ + if( abs_max < DBL_MIN ) abs_max = DBL_MIN; + + /* equal if relative error <= factor x eps */ + return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON); +} + +/*----------------------------------------------------------------------------*/ +/** Computes Euclidean distance between point (x1,y1) and point (x2,y2). + */ +static double dist(double x1, double y1, double x2, double y2) +{ + return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ); +} + + +/*----------------------------------------------------------------------------*/ +/*----------------------- 'list of n-tuple' data type ------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** 'list of n-tuple' data type + + The i-th component of the j-th n-tuple of an n-tuple list 'ntl' + is accessed with: + + ntl->values[ i + j * ntl->dim ] + + The dimension of the n-tuple (n) is: + + ntl->dim + + The number of n-tuples in the list is: + + ntl->size + + The maximum number of n-tuples that can be stored in the + list with the allocated memory at a given time is given by: + + ntl->max_size + */ +typedef struct ntuple_list_s +{ + unsigned int size; + unsigned int max_size; + unsigned int dim; + double * values; +} * ntuple_list; + +/*----------------------------------------------------------------------------*/ +/** Free memory used in n-tuple 'in'. + */ +static void free_ntuple_list(ntuple_list in) +{ + if( in == NULL || in->values == NULL ) + error("free_ntuple_list: invalid n-tuple input."); + free( (void *) in->values ); + free( (void *) in ); +} + +/*----------------------------------------------------------------------------*/ +/** Create an n-tuple list and allocate memory for one element. + @param dim the dimension (n) of the n-tuple. + */ +static ntuple_list new_ntuple_list(unsigned int dim) +{ + ntuple_list n_tuple; + + /* check parameters */ + if( dim == 0 ) error("new_ntuple_list: 'dim' must be positive."); + + /* get memory for list structure */ + n_tuple = (ntuple_list) malloc( sizeof(struct ntuple_list_s) ); + if( n_tuple == NULL ) error("not enough memory."); + + /* initialize list */ + n_tuple->size = 0; + n_tuple->max_size = 1; + n_tuple->dim = dim; + + /* get memory for tuples */ + n_tuple->values = (double *) malloc( sizeof(double) * dim*n_tuple->max_size ); + if( n_tuple->values == NULL ) error("not enough memory."); + + return n_tuple; +} + +/*----------------------------------------------------------------------------*/ +/** Enlarge the allocated memory of an n-tuple list. + */ +static void enlarge_ntuple_list(ntuple_list n_tuple) +{ + /* check parameters */ + if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size == 0 ) + error("enlarge_ntuple_list: invalid n-tuple."); + + /* duplicate number of tuples */ + n_tuple->max_size *= 2; + + /* realloc memory */ + n_tuple->values = (double *) realloc( (void *) n_tuple->values, + sizeof(double) * n_tuple->dim * n_tuple->max_size ); + if( n_tuple->values == NULL ) error("not enough memory."); +} + +/*----------------------------------------------------------------------------*/ +/** Add a 7-tuple to an n-tuple list. + */ +static void add_7tuple( ntuple_list out, double v1, double v2, double v3, + double v4, double v5, double v6, double v7 ) +{ + /* check parameters */ + if( out == NULL ) error("add_7tuple: invalid n-tuple input."); + if( out->dim != 7 ) error("add_7tuple: the n-tuple must be a 7-tuple."); + + /* if needed, alloc more tuples to 'out' */ + if( out->size == out->max_size ) enlarge_ntuple_list(out); + if( out->values == NULL ) error("add_7tuple: invalid n-tuple input."); + + /* add new 7-tuple */ + out->values[ out->size * out->dim + 0 ] = v1; + out->values[ out->size * out->dim + 1 ] = v2; + out->values[ out->size * out->dim + 2 ] = v3; + out->values[ out->size * out->dim + 3 ] = v4; + out->values[ out->size * out->dim + 4 ] = v5; + out->values[ out->size * out->dim + 5 ] = v6; + out->values[ out->size * out->dim + 6 ] = v7; + + /* update number of tuples counter */ + out->size++; +} + + +/*----------------------------------------------------------------------------*/ +/*----------------------------- Image Data Types -----------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** char image data type + + The pixel value at (x,y) is accessed by: + + image->data[ x + y * image->xsize ] + + with x and y integer. + */ +typedef struct image_char_s +{ + unsigned char * data; + unsigned int xsize,ysize; +} * image_char; + +/*----------------------------------------------------------------------------*/ +/** Free memory used in image_char 'i'. + */ +static void free_image_char(image_char i) +{ + if( i == NULL || i->data == NULL ) + error("free_image_char: invalid input image."); + free( (void *) i->data ); + free( (void *) i ); +} + +/*----------------------------------------------------------------------------*/ +/** Create a new image_char of size 'xsize' times 'ysize'. + */ +static image_char new_image_char(unsigned int xsize, unsigned int ysize) +{ + image_char image; + + /* check parameters */ + if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size."); + + /* get memory */ + image = (image_char) malloc( sizeof(struct image_char_s) ); + if( image == NULL ) error("not enough memory."); + image->data = (unsigned char *) calloc( (size_t) (xsize*ysize), + sizeof(unsigned char) ); + if( image->data == NULL ) error("not enough memory."); + + /* set image size */ + image->xsize = xsize; + image->ysize = ysize; + + return image; +} + +/*----------------------------------------------------------------------------*/ +/** Create a new image_char of size 'xsize' times 'ysize', + initialized to the value 'fill_value'. + */ +static image_char new_image_char_ini( unsigned int xsize, unsigned int ysize, + unsigned char fill_value ) +{ + image_char image = new_image_char(xsize,ysize); /* create image */ + unsigned int N = xsize*ysize; + unsigned int i; + + /* check parameters */ + if( image == NULL || image->data == NULL ) + error("new_image_char_ini: invalid image."); + + /* initialize */ + for(i=0; idata[i] = fill_value; + + return image; +} + +/*----------------------------------------------------------------------------*/ +/** int image data type + + The pixel value at (x,y) is accessed by: + + image->data[ x + y * image->xsize ] + + with x and y integer. + */ +typedef struct image_int_s +{ + int * data; + unsigned int xsize,ysize; +} * image_int; + +/*----------------------------------------------------------------------------*/ +/** Create a new image_int of size 'xsize' times 'ysize'. + */ +static image_int new_image_int(unsigned int xsize, unsigned int ysize) +{ + image_int image; + + /* check parameters */ + if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size."); + + /* get memory */ + image = (image_int) malloc( sizeof(struct image_int_s) ); + if( image == NULL ) error("not enough memory."); + image->data = (int *) calloc( (size_t) (xsize*ysize), sizeof(int) ); + if( image->data == NULL ) error("not enough memory."); + + /* set image size */ + image->xsize = xsize; + image->ysize = ysize; + + return image; +} + +/*----------------------------------------------------------------------------*/ +/** Create a new image_int of size 'xsize' times 'ysize', + initialized to the value 'fill_value'. + */ +static image_int new_image_int_ini( unsigned int xsize, unsigned int ysize, + int fill_value ) +{ + image_int image = new_image_int(xsize,ysize); /* create image */ + unsigned int N = xsize*ysize; + unsigned int i; + + /* initialize */ + for(i=0; idata[i] = fill_value; + + return image; +} + +/*----------------------------------------------------------------------------*/ +/** double image data type + + The pixel value at (x,y) is accessed by: + + image->data[ x + y * image->xsize ] + + with x and y integer. + */ +typedef struct image_double_s +{ + double * data; + unsigned int xsize,ysize; +} * image_double; + +/*----------------------------------------------------------------------------*/ +/** Free memory used in image_double 'i'. + */ +static void free_image_double(image_double i) +{ + if( i == NULL || i->data == NULL ) + error("free_image_double: invalid input image."); + free( (void *) i->data ); + free( (void *) i ); +} + +/*----------------------------------------------------------------------------*/ +/** Create a new image_double of size 'xsize' times 'ysize'. + */ +static image_double new_image_double(unsigned int xsize, unsigned int ysize) +{ + image_double image; + + /* check parameters */ + if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size."); + + /* get memory */ + image = (image_double) malloc( sizeof(struct image_double_s) ); + if( image == NULL ) error("not enough memory."); + image->data = (double *) calloc( (size_t) (xsize*ysize), sizeof(double) ); + if( image->data == NULL ) error("not enough memory."); + + /* set image size */ + image->xsize = xsize; + image->ysize = ysize; + + return image; +} + +/*----------------------------------------------------------------------------*/ +/** Create a new image_double of size 'xsize' times 'ysize' + with the data pointed by 'data'. + */ +static image_double new_image_double_ptr( unsigned int xsize, + unsigned int ysize, double * data ) +{ + image_double image; + + /* check parameters */ + if( xsize == 0 || ysize == 0 ) + error("new_image_double_ptr: invalid image size."); + if( data == NULL ) error("new_image_double_ptr: NULL data pointer."); + + /* get memory */ + image = (image_double) malloc( sizeof(struct image_double_s) ); + if( image == NULL ) error("not enough memory."); + + /* set image */ + image->xsize = xsize; + image->ysize = ysize; + image->data = data; + + return image; +} + + +/*----------------------------------------------------------------------------*/ +/*----------------------------- Gaussian filter ------------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** Compute a Gaussian kernel of length 'kernel->dim', + standard deviation 'sigma', and centered at value 'mean'. + + For example, if mean=0.5, the Gaussian will be centered + in the middle point between values 'kernel->values[0]' + and 'kernel->values[1]'. + */ +static void gaussian_kernel(ntuple_list kernel, double sigma, double mean) +{ + double sum = 0.0; + double val; + unsigned int i; + + /* check parameters */ + if( kernel == NULL || kernel->values == NULL ) + error("gaussian_kernel: invalid n-tuple 'kernel'."); + if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive."); + + /* compute Gaussian kernel */ + if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel); + kernel->size = 1; + for(i=0;idim;i++) + { + val = ( (double) i - mean ) / sigma; + kernel->values[i] = exp( -0.5 * val * val ); + sum += kernel->values[i]; + } + + /* normalization */ + if( sum >= 0.0 ) for(i=0;idim;i++) kernel->values[i] /= sum; +} + +/*----------------------------------------------------------------------------*/ +/** Scale the input image 'in' by a factor 'scale' by Gaussian sub-sampling. + + For example, scale=0.8 will give a result at 80% of the original size. + + The image is convolved with a Gaussian kernel + @f[ + G(x,y) = \frac{1}{2\pi\sigma^2} e^{-\frac{x^2+y^2}{2\sigma^2}} + @f] + before the sub-sampling to prevent aliasing. + + The standard deviation sigma given by: + - sigma = sigma_scale / scale, if scale < 1.0 + - sigma = sigma_scale, if scale >= 1.0 + + To be able to sub-sample at non-integer steps, some interpolation + is needed. In this implementation, the interpolation is done by + the Gaussian kernel, so both operations (filtering and sampling) + are done at the same time. The Gaussian kernel is computed + centered on the coordinates of the required sample. In this way, + when applied, it gives directly the result of convolving the image + with the kernel and interpolated to that particular position. + + A fast algorithm is done using the separability of the Gaussian + kernel. Applying the 2D Gaussian kernel is equivalent to applying + first a horizontal 1D Gaussian kernel and then a vertical 1D + Gaussian kernel (or the other way round). The reason is that + @f[ + G(x,y) = G(x) * G(y) + @f] + where + @f[ + G(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{x^2}{2\sigma^2}}. + @f] + The algorithm first applies a combined Gaussian kernel and sampling + in the x axis, and then the combined Gaussian kernel and sampling + in the y axis. + */ +static image_double gaussian_sampler( image_double in, double scale, + double sigma_scale ) +{ + image_double aux,out; + ntuple_list kernel; + unsigned int N,M,h,n,x,y,i; + int xc,yc,j,double_x_size,double_y_size; + double sigma,xx,yy,sum,prec; + + /* check parameters */ + if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 ) + error("gaussian_sampler: invalid image."); + if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive."); + if( sigma_scale <= 0.0 ) + error("gaussian_sampler: 'sigma_scale' must be positive."); + + /* compute new image size and get memory for images */ + if( in->xsize * scale > (double) UINT_MAX || + in->ysize * scale > (double) UINT_MAX ) + error("gaussian_sampler: the output image size exceeds the handled size."); + N = (unsigned int) ceil( in->xsize * scale ); + M = (unsigned int) ceil( in->ysize * scale ); + aux = new_image_double(N,in->ysize); + out = new_image_double(N,M); + + /* sigma, kernel size and memory for the kernel */ + sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale; + /* + The size of the kernel is selected to guarantee that the + the first discarded term is at least 10^prec times smaller + than the central value. For that, h should be larger than x, with + e^(-x^2/2sigma^2) = 1/10^prec. + Then, + x = sigma * sqrt( 2 * prec * ln(10) ). + */ + prec = 3.0; + h = (unsigned int) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) ); + n = 1+2*h; /* kernel size */ + kernel = new_ntuple_list(n); + + /* auxiliary double image size variables */ + double_x_size = (int) (2 * in->xsize); + double_y_size = (int) (2 * in->ysize); + + /* First subsampling: x axis */ + for(x=0;xxsize;x++) + { + /* + x is the coordinate in the new image. + xx is the corresponding x-value in the original size image. + xc is the integer value, the pixel coordinate of xx. + */ + xx = (double) x / scale; + /* coordinate (0.0,0.0) is in the center of pixel (0,0), + so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */ + xc = (int) floor( xx + 0.5 ); + gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc ); + /* the kernel must be computed for each x because the fine + offset xx-xc is different in each case */ + + for(y=0;yysize;y++) + { + sum = 0.0; + for(i=0;idim;i++) + { + j = xc - h + i; + + /* symmetry boundary condition */ + while( j < 0 ) j += double_x_size; + while( j >= double_x_size ) j -= double_x_size; + if( j >= (int) in->xsize ) j = double_x_size-1-j; + + sum += in->data[ j + y * in->xsize ] * kernel->values[i]; + } + aux->data[ x + y * aux->xsize ] = sum; + } + } + + /* Second subsampling: y axis */ + for(y=0;yysize;y++) + { + /* + y is the coordinate in the new image. + yy is the corresponding x-value in the original size image. + yc is the integer value, the pixel coordinate of xx. + */ + yy = (double) y / scale; + /* coordinate (0.0,0.0) is in the center of pixel (0,0), + so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */ + yc = (int) floor( yy + 0.5 ); + gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc ); + /* the kernel must be computed for each y because the fine + offset yy-yc is different in each case */ + + for(x=0;xxsize;x++) + { + sum = 0.0; + for(i=0;idim;i++) + { + j = yc - h + i; + + /* symmetry boundary condition */ + while( j < 0 ) j += double_y_size; + while( j >= double_y_size ) j -= double_y_size; + if( j >= (int) in->ysize ) j = double_y_size-1-j; + + sum += aux->data[ x + j * aux->xsize ] * kernel->values[i]; + } + out->data[ x + y * out->xsize ] = sum; + } + } + + /* free memory */ + free_ntuple_list(kernel); + free_image_double(aux); + + return out; +} + + +/*----------------------------------------------------------------------------*/ +/*--------------------------------- Gradient ---------------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** Computes the direction of the level line of 'in' at each point. + + The result is: + - an image_double with the angle at each pixel, or NOTDEF if not defined. + - the image_double 'modgrad' (a pointer is passed as argument) + with the gradient magnitude at each point. + - a list of pixels 'list_p' roughly ordered by decreasing + gradient magnitude. (The order is made by classifying points + into bins by gradient magnitude. The parameters 'n_bins' and + 'max_grad' specify the number of bins and the gradient modulus + at the highest bin. The pixels in the list would be in + decreasing gradient magnitude, up to a precision of the size of + the bins.) + - a pointer 'mem_p' to the memory used by 'list_p' to be able to + free the memory when it is not used anymore. + */ +static image_double ll_angle( image_double in, double threshold, + struct coorlist ** list_p, void ** mem_p, + image_double * modgrad, unsigned int n_bins ) +{ + image_double g; + unsigned int n,p,x,y,adr,i; + double com1,com2,gx,gy,norm,norm2; + /* the rest of the variables are used for pseudo-ordering + the gradient magnitude values */ + int list_count = 0; + struct coorlist * list; + struct coorlist ** range_l_s; /* array of pointers to start of bin list */ + struct coorlist ** range_l_e; /* array of pointers to end of bin list */ + struct coorlist * start; + struct coorlist * end; + double max_grad = 0.0; + + /* check parameters */ + if( in == NULL || in->data == NULL || in->xsize == 0 || in->ysize == 0 ) + error("ll_angle: invalid image."); + if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive."); + if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'."); + if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'."); + if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'."); + if( n_bins == 0 ) error("ll_angle: 'n_bins' must be positive."); + + /* image size shortcuts */ + n = in->ysize; + p = in->xsize; + + /* allocate output image */ + g = new_image_double(in->xsize,in->ysize); + + /* get memory for the image of gradient modulus */ + *modgrad = new_image_double(in->xsize,in->ysize); + + /* get memory for "ordered" list of pixels */ + list = (struct coorlist *) calloc( (size_t) (n*p), sizeof(struct coorlist) ); + *mem_p = (void *) list; + range_l_s = (struct coorlist **) calloc( (size_t) n_bins, + sizeof(struct coorlist *) ); + range_l_e = (struct coorlist **) calloc( (size_t) n_bins, + sizeof(struct coorlist *) ); + if( list == NULL || range_l_s == NULL || range_l_e == NULL ) + error("not enough memory."); + for(i=0;idata[(n-1)*p+x] = NOTDEF; + for(y=0;ydata[p*y+p-1] = NOTDEF; + + /* compute gradient on the remaining pixels */ + for(x=0;xdata[adr+p+1] - in->data[adr]; + com2 = in->data[adr+1] - in->data[adr+p]; + + gx = com1+com2; /* gradient x component */ + gy = com1-com2; /* gradient y component */ + norm2 = gx*gx+gy*gy; + norm = sqrt( norm2 / 4.0 ); /* gradient norm */ + + (*modgrad)->data[adr] = norm; /* store gradient norm */ + + if( norm <= threshold ) /* norm too small, gradient no defined */ + g->data[adr] = NOTDEF; /* gradient angle not defined */ + else + { + /* gradient angle computation */ + g->data[adr] = atan2(gx,-gy); + + /* look for the maximum of the gradient */ + if( norm > max_grad ) max_grad = norm; + } + } + + /* compute histogram of gradient values */ + for(x=0;xdata[y*p+x]; + + /* store the point in the right bin according to its norm */ + i = (unsigned int) (norm * (double) n_bins / max_grad); + if( i >= n_bins ) i = n_bins-1; + if( range_l_e[i] == NULL ) + range_l_s[i] = range_l_e[i] = list+list_count++; + else + { + range_l_e[i]->next = list+list_count; + range_l_e[i] = list+list_count++; + } + range_l_e[i]->x = (int) x; + range_l_e[i]->y = (int) y; + range_l_e[i]->next = NULL; + } + + /* Make the list of pixels (almost) ordered by norm value. + It starts by the larger bin, so the list starts by the + pixels with the highest gradient value. Pixels would be ordered + by norm value, up to a precision given by max_grad/n_bins. + */ + for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--); + start = range_l_s[i]; + end = range_l_e[i]; + if( start != NULL ) + while(i>0) + { + --i; + if( range_l_s[i] != NULL ) + { + end->next = range_l_s[i]; + end = range_l_e[i]; + } + } + *list_p = start; + + /* free memory */ + free( (void *) range_l_s ); + free( (void *) range_l_e ); + + return g; +} + +/*----------------------------------------------------------------------------*/ +/** Is point (x,y) aligned to angle theta, up to precision 'prec'? + */ +static int isaligned( int x, int y, image_double angles, double theta, + double prec ) +{ + double a; + + /* check parameters */ + if( angles == NULL || angles->data == NULL ) + error("isaligned: invalid image 'angles'."); + if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize ) + error("isaligned: (x,y) out of the image."); + if( prec < 0.0 ) error("isaligned: 'prec' must be positive."); + + /* angle at pixel (x,y) */ + a = angles->data[ x + y * angles->xsize ]; + + /* pixels whose level-line angle is not defined + are considered as NON-aligned */ + if( a == NOTDEF ) return FALSE; /* there is no need to call the function + 'double_equal' here because there is + no risk of problems related to the + comparison doubles, we are only + interested in the exact NOTDEF value */ + + /* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */ + theta -= a; + if( theta < 0.0 ) theta = -theta; + if( theta > M_3_2_PI ) + { + theta -= M_2__PI; + if( theta < 0.0 ) theta = -theta; + } + + return theta <= prec; +} + +/*----------------------------------------------------------------------------*/ +/** Absolute value angle difference. + */ +static double angle_diff(double a, double b) +{ + a -= b; + while( a <= -M_PI ) a += M_2__PI; + while( a > M_PI ) a -= M_2__PI; + if( a < 0.0 ) a = -a; + return a; +} + +/*----------------------------------------------------------------------------*/ +/** Signed angle difference. + */ +static double angle_diff_signed(double a, double b) +{ + a -= b; + while( a <= -M_PI ) a += M_2__PI; + while( a > M_PI ) a -= M_2__PI; + return a; +} + + +/*----------------------------------------------------------------------------*/ +/*----------------------------- NFA computation ------------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** Computes the natural logarithm of the absolute value of + the gamma function of x using the Lanczos approximation. + See http://www.rskey.org/gamma.htm + + The formula used is + @f[ + \Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) } + (x+5.5)^{x+0.5} e^{-(x+5.5)} + @f] + so + @f[ + \log\Gamma(x) = \log\left( \sum_{n=0}^{N} q_n x^n \right) + + (x+0.5) \log(x+5.5) - (x+5.5) - \sum_{n=0}^{N} \log(x+n) + @f] + and + q0 = 75122.6331530, + q1 = 80916.6278952, + q2 = 36308.2951477, + q3 = 8687.24529705, + q4 = 1168.92649479, + q5 = 83.8676043424, + q6 = 2.50662827511. + */ +static double log_gamma_lanczos(double x) +{ + static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477, + 8687.24529705, 1168.92649479, 83.8676043424, + 2.50662827511 }; + double a = (x+0.5) * log(x+5.5) - (x+5.5); + double b = 0.0; + int n; + + for(n=0;n<7;n++) + { + a -= log( x + (double) n ); + b += q[n] * pow( x, (double) n ); + } + return a + log(b); +} + +/*----------------------------------------------------------------------------*/ +/** Computes the natural logarithm of the absolute value of + the gamma function of x using Windschitl method. + See http://www.rskey.org/gamma.htm + + The formula used is + @f[ + \Gamma(x) = \sqrt{\frac{2\pi}{x}} \left( \frac{x}{e} + \sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } \right)^x + @f] + so + @f[ + \log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x + + 0.5x\log\left( x\sinh(1/x) + \frac{1}{810x^6} \right). + @f] + This formula is a good approximation when x > 15. + */ +static double log_gamma_windschitl(double x) +{ + return 0.918938533204673 + (x-0.5)*log(x) - x + + 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) ); +} + +/*----------------------------------------------------------------------------*/ +/** Computes the natural logarithm of the absolute value of + the gamma function of x. When x>15 use log_gamma_windschitl(), + otherwise use log_gamma_lanczos(). + */ +#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x)) + +/*----------------------------------------------------------------------------*/ +/** Size of the table to store already computed inverse values. + */ +#define TABSIZE 100000 + +// clang-format on + +static double *inv = NULL; /* table to keep computed inverse values */ + +__attribute__((constructor)) static void invConstructor() +{ + if(inv) return; + inv = (double *)malloc(sizeof(double) * TABSIZE); +} + +__attribute__((destructor)) static void invDestructor() +{ + free(inv); + inv = NULL; +} + +// clang-format off + +/*----------------------------------------------------------------------------*/ +/** Computes -log10(NFA). + + NFA stands for Number of False Alarms: + @f[ + \mathrm{NFA} = NT \cdot B(n,k,p) + @f] + + - NT - number of tests + - B(n,k,p) - tail of binomial distribution with parameters n,k and p: + @f[ + B(n,k,p) = \sum_{j=k}^n + \left(\begin{array}{c}n\\j\end{array}\right) + p^{j} (1-p)^{n-j} + @f] + + The value -log10(NFA) is equivalent but more intuitive than NFA: + - -1 corresponds to 10 mean false alarms + - 0 corresponds to 1 mean false alarm + - 1 corresponds to 0.1 mean false alarms + - 2 corresponds to 0.01 mean false alarms + - ... + + Used this way, the bigger the value, better the detection, + and a logarithmic scale is used. + + @param n,k,p binomial parameters. + @param logNT logarithm of Number of Tests + + The computation is based in the gamma function by the following + relation: + @f[ + \left(\begin{array}{c}n\\k\end{array}\right) + = \frac{ \Gamma(n+1) }{ \Gamma(k+1) \cdot \Gamma(n-k+1) }. + @f] + We use efficient algorithms to compute the logarithm of + the gamma function. + + To make the computation faster, not all the sum is computed, part + of the terms are neglected based on a bound to the error obtained + (an error of 10% in the result is accepted). + */ +static double nfa(int n, int k, double p, double logNT) +{ + double tolerance = 0.1; /* an error of 10% in the result is accepted */ + double log1term,term,bin_term,mult_term,bin_tail,err,p_term; + int i; + + /* check parameters */ + if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 ) + error("nfa: wrong n, k or p values."); + + /* trivial cases */ + if( n==0 || k==0 ) return -logNT; + if( n==k ) return -logNT - (double) n * log10(p); + + /* probability term */ + p_term = p / (1.0-p); + + /* compute the first term of the series */ + /* + binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i} + where bincoef(n,i) are the binomial coefficients. + But + bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ). + We use this to compute the first term. Actually the log of it. + */ + log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 ) + - log_gamma( (double) (n-k) + 1.0 ) + + (double) k * log(p) + (double) (n-k) * log(1.0-p); + term = exp(log1term); + + /* in some cases no more computations are needed */ + if( double_equal(term,0.0) ) /* the first term is almost zero */ + { + if( (double) k > (double) n * p ) /* at begin or end of the tail? */ + return -log1term / M_LN10 - logNT; /* end: use just the first term */ + else + return -logNT; /* begin: the tail is roughly 1 */ + } + + /* compute more terms if needed */ + bin_tail = term; + for(i=k+1;i<=n;i++) + { + /* + As + term_i = bincoef(n,i) * p^i * (1-p)^(n-i) + and + bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i, + then, + term_i / term_i-1 = (n-i+1)/i * p/(1-p) + and + term_i = term_i-1 * (n-i+1)/i * p/(1-p). + 1/i is stored in a table as they are computed, + because divisions are expensive. + p/(1-p) is computed only once and stored in 'p_term'. + */ + bin_term = (double) (n-i+1) * ( ii. + Then, the error on the binomial tail when truncated at + the i term can be bounded by a geometric series of form + term_i * sum mult_term_i^j. */ + err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) / + (1.0-mult_term) - 1.0 ); + + /* One wants an error at most of tolerance*final_result, or: + tolerance * abs(-log10(bin_tail)-logNT). + Now, the error that can be accepted on bin_tail is + given by tolerance*final_result divided by the derivative + of -log10(x) when x=bin_tail. that is: + tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail) + Finally, we truncate the tail if the error is less than: + tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */ + if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break; + } + } + return -log10(bin_tail) - logNT; +} + + +/*----------------------------------------------------------------------------*/ +/*--------------------------- Rectangle structure ----------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** Rectangle structure: line segment with width. + */ +struct rect +{ + double x1,y1,x2,y2; /* first and second point of the line segment */ + double width; /* rectangle width */ + double x,y; /* center of the rectangle */ + double theta; /* angle */ + double dx,dy; /* (dx,dy) is vector oriented as the line segment */ + double prec; /* tolerance angle */ + double p; /* probability of a point with angle within 'prec' */ +}; + +/*----------------------------------------------------------------------------*/ +/** Copy one rectangle structure to another. + */ +static void rect_copy(struct rect * in, struct rect * out) +{ + /* check parameters */ + if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'."); + + /* copy values */ + out->x1 = in->x1; + out->y1 = in->y1; + out->x2 = in->x2; + out->y2 = in->y2; + out->width = in->width; + out->x = in->x; + out->y = in->y; + out->theta = in->theta; + out->dx = in->dx; + out->dy = in->dy; + out->prec = in->prec; + out->p = in->p; +} + +/*----------------------------------------------------------------------------*/ +/** Rectangle points iterator. + + The integer coordinates of pixels inside a rectangle are + iteratively explored. This structure keep track of the process and + functions ri_ini(), ri_inc(), ri_end(), and ri_del() are used in + the process. An example of how to use the iterator is as follows: + \code + + struct rect * rec = XXX; // some rectangle + rect_iter * i; + for( i=ri_ini(rec); !ri_end(i); ri_inc(i) ) + { + // your code, using 'i->x' and 'i->y' as coordinates + } + ri_del(i); // delete iterator + + \endcode + The pixels are explored 'column' by 'column', where we call + 'column' a set of pixels with the same x value that are inside the + rectangle. The following is an schematic representation of a + rectangle, the 'column' being explored is marked by colons, and + the current pixel being explored is 'x,y'. + \verbatim + + vx[1],vy[1] + * * + * * + * * + * ye + * : * + vx[0],vy[0] : * + * : * + * x,y * + * : * + * : vx[2],vy[2] + * : * + y ys * + ^ * * + | * * + | * * + +---> x vx[3],vy[3] + + \endverbatim + The first 'column' to be explored is the one with the smaller x + value. Each 'column' is explored starting from the pixel of the + 'column' (inside the rectangle) with the smallest y value. + + The four corners of the rectangle are stored in order that rotates + around the corners at the arrays 'vx[]' and 'vy[]'. The first + point is always the one with smaller x value. + + 'x' and 'y' are the coordinates of the pixel being explored. 'ys' + and 'ye' are the start and end values of the current column being + explored. So, 'ys' < 'ye'. + */ +typedef struct +{ + double vx[4]; /* rectangle's corner X coordinates in circular order */ + double vy[4]; /* rectangle's corner Y coordinates in circular order */ + double ys,ye; /* start and end Y values of current 'column' */ + int x,y; /* coordinates of currently explored pixel */ +} rect_iter; + +/*----------------------------------------------------------------------------*/ +/** Interpolate y value corresponding to 'x' value given, in + the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the smaller + of 'y1' and 'y2'. + + The following restrictions are required: + - x1 <= x2 + - x1 <= x + - x <= x2 + */ +static double inter_low(double x, double x1, double y1, double x2, double y2) +{ + /* check parameters */ + if( x1 > x2 || x < x1 || x > x2 ) + error("inter_low: unsuitable input, 'x1>x2' or 'xx2'."); + + /* interpolation */ + if( double_equal(x1,x2) && y1y2 ) return y2; + return y1 + (x-x1) * (y2-y1) / (x2-x1); +} + +/*----------------------------------------------------------------------------*/ +/** Interpolate y value corresponding to 'x' value given, in + the line 'x1,y1' to 'x2,y2'; if 'x1=x2' return the larger + of 'y1' and 'y2'. + + The following restrictions are required: + - x1 <= x2 + - x1 <= x + - x <= x2 + */ +static double inter_hi(double x, double x1, double y1, double x2, double y2) +{ + /* check parameters */ + if( x1 > x2 || x < x1 || x > x2 ) + error("inter_hi: unsuitable input, 'x1>x2' or 'xx2'."); + + /* interpolation */ + if( double_equal(x1,x2) && y1y2 ) return y1; + return y1 + (x-x1) * (y2-y1) / (x2-x1); +} + +/*----------------------------------------------------------------------------*/ +/** Free memory used by a rectangle iterator. + */ +static void ri_del(rect_iter * iter) +{ + if( iter == NULL ) error("ri_del: NULL iterator."); + free( (void *) iter ); +} + +/*----------------------------------------------------------------------------*/ +/** Check if the iterator finished the full iteration. + + See details in \ref rect_iter + */ +static int ri_end(rect_iter * i) +{ + /* check input */ + if( i == NULL ) error("ri_end: NULL iterator."); + + /* if the current x value is larger than the largest + x value in the rectangle (vx[2]), we know the full + exploration of the rectangle is finished. */ + return (double)(i->x) > i->vx[2]; +} + +/*----------------------------------------------------------------------------*/ +/** Increment a rectangle iterator. + + See details in \ref rect_iter + */ +static void ri_inc(rect_iter * i) +{ + /* check input */ + if( i == NULL ) error("ri_inc: NULL iterator."); + + /* if not at end of exploration, + increase y value for next pixel in the 'column' */ + if( !ri_end(i) ) i->y++; + + /* if the end of the current 'column' is reached, + and it is not the end of exploration, + advance to the next 'column' */ + while( (double) (i->y) > i->ye && !ri_end(i) ) + { + /* increase x, next 'column' */ + i->x++; + + /* if end of exploration, return */ + if( ri_end(i) ) return; + + /* update lower y limit (start) for the new 'column'. + + We need to interpolate the y value that corresponds to the + lower side of the rectangle. The first thing is to decide if + the corresponding side is + + vx[0],vy[0] to vx[3],vy[3] or + vx[3],vy[3] to vx[2],vy[2] + + Then, the side is interpolated for the x value of the + 'column'. But, if the side is vertical (as it could happen if + the rectangle is vertical and we are dealing with the first + or last 'columns') then we pick the lower value of the side + by using 'inter_low'. + */ + if( (double) i->x < i->vx[3] ) + i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]); + else + i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]); + + /* update upper y limit (end) for the new 'column'. + + We need to interpolate the y value that corresponds to the + upper side of the rectangle. The first thing is to decide if + the corresponding side is + + vx[0],vy[0] to vx[1],vy[1] or + vx[1],vy[1] to vx[2],vy[2] + + Then, the side is interpolated for the x value of the + 'column'. But, if the side is vertical (as it could happen if + the rectangle is vertical and we are dealing with the first + or last 'columns') then we pick the lower value of the side + by using 'inter_low'. + */ + if( (double)i->x < i->vx[1] ) + i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]); + else + i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]); + + /* new y */ + i->y = (int) ceil(i->ys); + } +} + +/*----------------------------------------------------------------------------*/ +/** Create and initialize a rectangle iterator. + + See details in \ref rect_iter + */ +static rect_iter * ri_ini(struct rect * r) +{ + double vx[4],vy[4]; + int n,offset; + rect_iter * i; + + /* check parameters */ + if( r == NULL ) error("ri_ini: invalid rectangle."); + + /* get memory */ + i = (rect_iter *) malloc(sizeof(rect_iter)); + if( i == NULL ) error("ri_ini: Not enough memory."); + + /* build list of rectangle corners ordered + in a circular way around the rectangle */ + vx[0] = r->x1 - r->dy * r->width / 2.0; + vy[0] = r->y1 + r->dx * r->width / 2.0; + vx[1] = r->x2 - r->dy * r->width / 2.0; + vy[1] = r->y2 + r->dx * r->width / 2.0; + vx[2] = r->x2 + r->dy * r->width / 2.0; + vy[2] = r->y2 - r->dx * r->width / 2.0; + vx[3] = r->x1 + r->dy * r->width / 2.0; + vy[3] = r->y1 - r->dx * r->width / 2.0; + + /* compute rotation of index of corners needed so that the first + point has the smaller x. + + if one side is vertical, thus two corners have the same smaller x + value, the one with the largest y value is selected as the first. + */ + if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0; + else if( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1; + else if( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2; + else offset = 3; + + /* apply rotation of index. */ + for(n=0; n<4; n++) + { + i->vx[n] = vx[(offset+n)%4]; + i->vy[n] = vy[(offset+n)%4]; + } + + /* Set an initial condition. + + The values are set to values that will cause 'ri_inc' (that will + be called immediately) to initialize correctly the first 'column' + and compute the limits 'ys' and 'ye'. + + 'y' is set to the integer value of vy[0], the starting corner. + + 'ys' and 'ye' are set to very small values, so 'ri_inc' will + notice that it needs to start a new 'column'. + + The smallest integer coordinate inside of the rectangle is + 'ceil(vx[0])'. The current 'x' value is set to that value minus + one, so 'ri_inc' (that will increase x by one) will advance to + the first 'column'. + */ + i->x = (int) ceil(i->vx[0]) - 1; + i->y = (int) ceil(i->vy[0]); + i->ys = i->ye = -DBL_MAX; + + /* advance to the first pixel */ + ri_inc(i); + + return i; +} + +/*----------------------------------------------------------------------------*/ +/** Compute a rectangle's NFA value. + */ +static double rect_nfa(struct rect * rec, image_double angles, double logNT) +{ + rect_iter * i; + int pts = 0; + int alg = 0; + + /* check parameters */ + if( rec == NULL ) error("rect_nfa: invalid rectangle."); + if( angles == NULL ) error("rect_nfa: invalid 'angles'."); + + /* compute the total number of pixels and of aligned points in 'rec' */ + for(i=ri_ini(rec); !ri_end(i); ri_inc(i)) /* rectangle iterator */ + if( i->x >= 0 && i->y >= 0 && + i->x < (int) angles->xsize && i->y < (int) angles->ysize ) + { + ++pts; /* total number of pixels counter */ + if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) ) + ++alg; /* aligned points counter */ + } + ri_del(i); /* delete iterator */ + + return nfa(pts,alg,rec->p,logNT); /* compute NFA value */ +} + + +/*----------------------------------------------------------------------------*/ +/*---------------------------------- Regions ---------------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** Compute region's angle as the principal inertia axis of the region. + + The following is the region inertia matrix A: + @f[ + + A = \left(\begin{array}{cc} + Ixx & Ixy \\ + Ixy & Iyy \\ + \end{array}\right) + + @f] + where + + Ixx = sum_i G(i).(y_i - cx)^2 + + Iyy = sum_i G(i).(x_i - cy)^2 + + Ixy = - sum_i G(i).(x_i - cx).(y_i - cy) + + and + - G(i) is the gradient norm at pixel i, used as pixel's weight. + - x_i and y_i are the coordinates of pixel i. + - cx and cy are the coordinates of the center of th region. + + lambda1 and lambda2 are the eigenvalues of matrix A, + with lambda1 >= lambda2. They are found by solving the + characteristic polynomial: + + det( lambda I - A) = 0 + + that gives: + + lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2 + + lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)^2 + 4.0*Ixy*Ixy) ) / 2 + + To get the line segment direction we want to get the angle the + eigenvector associated to the smallest eigenvalue. We have + to solve for a,b in: + + a.Ixx + b.Ixy = a.lambda2 + + a.Ixy + b.Iyy = b.lambda2 + + We want the angle theta = atan(b/a). It can be computed with + any of the two equations: + + theta = atan( (lambda2-Ixx) / Ixy ) + + or + + theta = atan( Ixy / (lambda2-Iyy) ) + + When |Ixx| > |Iyy| we use the first, otherwise the second (just to + get better numeric precision). + */ +static double get_theta( struct point * reg, int reg_size, double x, double y, + image_double modgrad, double reg_angle, double prec ) +{ + double lambda,theta,weight; + double Ixx = 0.0; + double Iyy = 0.0; + double Ixy = 0.0; + int i; + + /* check parameters */ + if( reg == NULL ) error("get_theta: invalid region."); + if( reg_size <= 1 ) error("get_theta: region size <= 1."); + if( modgrad == NULL || modgrad->data == NULL ) + error("get_theta: invalid 'modgrad'."); + if( prec < 0.0 ) error("get_theta: 'prec' must be positive."); + + /* compute inertia matrix */ + for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ]; + Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight; + Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight; + Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight; + } + if( double_equal(Ixx,0.0) && double_equal(Iyy,0.0) && double_equal(Ixy,0.0) ) + error("get_theta: null inertia matrix."); + + /* compute smallest eigenvalue */ + lambda = 0.5 * ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) ); + + /* compute angle */ + theta = fabs(Ixx)>fabs(Iyy) ? atan2(lambda-Ixx,Ixy) : atan2(Ixy,lambda-Iyy); + + /* The previous procedure doesn't cares about orientation, + so it could be wrong by 180 degrees. Here is corrected if necessary. */ + if( angle_diff(theta,reg_angle) > prec ) theta += M_PI; + + return theta; +} + +/*----------------------------------------------------------------------------*/ +/** Computes a rectangle that covers a region of points. + */ +static void region2rect( struct point * reg, int reg_size, + image_double modgrad, double reg_angle, + double prec, double p, struct rect * rec ) +{ + double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max; + int i; + + /* check parameters */ + if( reg == NULL ) error("region2rect: invalid region."); + if( reg_size <= 1 ) error("region2rect: region size <= 1."); + if( modgrad == NULL || modgrad->data == NULL ) + error("region2rect: invalid image 'modgrad'."); + if( rec == NULL ) error("region2rect: invalid 'rec'."); + + /* center of the region: + + It is computed as the weighted sum of the coordinates + of all the pixels in the region. The norm of the gradient + is used as the weight of a pixel. The sum is as follows: + cx = \sum_i G(i).x_i + cy = \sum_i G(i).y_i + where G(i) is the norm of the gradient of pixel i + and x_i,y_i are its coordinates. + */ + x = y = sum = 0.0; + for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ]; + x += (double) reg[i].x * weight; + y += (double) reg[i].y * weight; + sum += weight; + } + if( sum <= 0.0 ) error("region2rect: weights sum equal to zero."); + x /= sum; + y /= sum; + + /* theta */ + theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec); + + /* length and width: + + 'l' and 'w' are computed as the distance from the center of the + region to pixel i, projected along the rectangle axis (dx,dy) and + to the orthogonal axis (-dy,dx), respectively. + + The length of the rectangle goes from l_min to l_max, where l_min + and l_max are the minimum and maximum values of l in the region. + Analogously, the width is selected from w_min to w_max, where + w_min and w_max are the minimum and maximum of w for the pixels + in the region. + */ + dx = cos(theta); + dy = sin(theta); + l_min = l_max = w_min = w_max = 0.0; + for(i=0; i l_max ) l_max = l; + if( l < l_min ) l_min = l; + if( w > w_max ) w_max = w; + if( w < w_min ) w_min = w; + } + + /* store values */ + rec->x1 = x + l_min * dx; + rec->y1 = y + l_min * dy; + rec->x2 = x + l_max * dx; + rec->y2 = y + l_max * dy; + rec->width = w_max - w_min; + rec->x = x; + rec->y = y; + rec->theta = theta; + rec->dx = dx; + rec->dy = dy; + rec->prec = prec; + rec->p = p; + + /* we impose a minimal width of one pixel + + A sharp horizontal or vertical step would produce a perfectly + horizontal or vertical region. The width computed would be + zero. But that corresponds to a one pixels width transition in + the image. + */ + if( rec->width < 1.0 ) rec->width = 1.0; +} + +/*----------------------------------------------------------------------------*/ +/** Build a region of pixels that share the same angle, up to a + tolerance 'prec', starting at point (x,y). + */ +static void region_grow( int x, int y, image_double angles, struct point * reg, + int * reg_size, double * reg_angle, image_char used, + double prec ) +{ + double sumdx,sumdy; + int xx,yy,i; + + /* check parameters */ + if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize ) + error("region_grow: (x,y) out of the image."); + if( angles == NULL || angles->data == NULL ) + error("region_grow: invalid image 'angles'."); + if( reg == NULL ) error("region_grow: invalid 'reg'."); + if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'."); + if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'."); + if( used == NULL || used->data == NULL ) + error("region_grow: invalid image 'used'."); + + /* first point of the region */ + *reg_size = 1; + reg[0].x = x; + reg[0].y = y; + *reg_angle = angles->data[x+y*angles->xsize]; /* region's angle */ + sumdx = cos(*reg_angle); + sumdy = sin(*reg_angle); + used->data[x+y*used->xsize] = USED; + + /* try neighbors as new region points */ + for(i=0; i<*reg_size; i++) + for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++) + for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++) + if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize && + used->data[xx+yy*used->xsize] != USED && + isaligned(xx,yy,angles,*reg_angle,prec) ) + { + /* add point */ + used->data[xx+yy*used->xsize] = USED; + reg[*reg_size].x = xx; + reg[*reg_size].y = yy; + ++(*reg_size); + + /* update region's angle */ + sumdx += cos( angles->data[xx+yy*angles->xsize] ); + sumdy += sin( angles->data[xx+yy*angles->xsize] ); + *reg_angle = atan2(sumdy,sumdx); + } +} + +/*----------------------------------------------------------------------------*/ +/** Try some rectangles variations to improve NFA value. Only if the + rectangle is not meaningful (i.e., log_nfa <= log_eps). + */ +static double rect_improve( struct rect * rec, image_double angles, + double logNT, double log_eps ) +{ + struct rect r; + double log_nfa,log_nfa_new; + double delta = 0.5; + double delta_2 = delta / 2.0; + int n; + + log_nfa = rect_nfa(rec,angles,logNT); + + if( log_nfa > log_eps ) return log_nfa; + + /* try finer precisions */ + rect_copy(rec,&r); + for(n=0; n<5; n++) + { + r.p /= 2.0; + r.prec = r.p * M_PI; + log_nfa_new = rect_nfa(&r,angles,logNT); + if( log_nfa_new > log_nfa ) + { + log_nfa = log_nfa_new; + rect_copy(&r,rec); + } + } + + if( log_nfa > log_eps ) return log_nfa; + + /* try to reduce width */ + rect_copy(rec,&r); + for(n=0; n<5; n++) + { + if( (r.width - delta) >= 0.5 ) + { + r.width -= delta; + log_nfa_new = rect_nfa(&r,angles,logNT); + if( log_nfa_new > log_nfa ) + { + rect_copy(&r,rec); + log_nfa = log_nfa_new; + } + } + } + + if( log_nfa > log_eps ) return log_nfa; + + /* try to reduce one side of the rectangle */ + rect_copy(rec,&r); + for(n=0; n<5; n++) + { + if( (r.width - delta) >= 0.5 ) + { + r.x1 += -r.dy * delta_2; + r.y1 += r.dx * delta_2; + r.x2 += -r.dy * delta_2; + r.y2 += r.dx * delta_2; + r.width -= delta; + log_nfa_new = rect_nfa(&r,angles,logNT); + if( log_nfa_new > log_nfa ) + { + rect_copy(&r,rec); + log_nfa = log_nfa_new; + } + } + } + + if( log_nfa > log_eps ) return log_nfa; + + /* try to reduce the other side of the rectangle */ + rect_copy(rec,&r); + for(n=0; n<5; n++) + { + if( (r.width - delta) >= 0.5 ) + { + r.x1 -= -r.dy * delta_2; + r.y1 -= r.dx * delta_2; + r.x2 -= -r.dy * delta_2; + r.y2 -= r.dx * delta_2; + r.width -= delta; + log_nfa_new = rect_nfa(&r,angles,logNT); + if( log_nfa_new > log_nfa ) + { + rect_copy(&r,rec); + log_nfa = log_nfa_new; + } + } + } + + if( log_nfa > log_eps ) return log_nfa; + + /* try even finer precisions */ + rect_copy(rec,&r); + for(n=0; n<5; n++) + { + r.p /= 2.0; + r.prec = r.p * M_PI; + log_nfa_new = rect_nfa(&r,angles,logNT); + if( log_nfa_new > log_nfa ) + { + log_nfa = log_nfa_new; + rect_copy(&r,rec); + } + } + + return log_nfa; +} + +/*----------------------------------------------------------------------------*/ +/** Reduce the region size, by elimination the points far from the + starting point, until that leads to rectangle with the right + density of region points or to discard the region if too small. + */ +static int reduce_region_radius( struct point * reg, int * reg_size, + image_double modgrad, double reg_angle, + double prec, double p, struct rect * rec, + image_char used, image_double angles, + double density_th ) +{ + double density,radius1,radius2,rad,xc,yc; + int i; + + /* check parameters */ + if( reg == NULL ) error("reduce_region_radius: invalid pointer 'reg'."); + if( reg_size == NULL ) + error("reduce_region_radius: invalid pointer 'reg_size'."); + if( prec < 0.0 ) error("reduce_region_radius: 'prec' must be positive."); + if( rec == NULL ) error("reduce_region_radius: invalid pointer 'rec'."); + if( used == NULL || used->data == NULL ) + error("reduce_region_radius: invalid image 'used'."); + if( angles == NULL || angles->data == NULL ) + error("reduce_region_radius: invalid image 'angles'."); + + /* compute region points density */ + density = (double) *reg_size / + ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); + + /* if the density criterion is satisfied there is nothing to do */ + if( density >= density_th ) return TRUE; + + /* compute region's radius */ + xc = (double) reg[0].x; + yc = (double) reg[0].y; + radius1 = dist( xc, yc, rec->x1, rec->y1 ); + radius2 = dist( xc, yc, rec->x2, rec->y2 ); + rad = radius1 > radius2 ? radius1 : radius2; + + /* while the density criterion is not satisfied, remove farther pixels */ + while( density < density_th ) + { + rad *= 0.75; /* reduce region's radius to 75% of its value */ + + /* remove points from the region and update 'used' map */ + for(i=0; i<*reg_size; i++) + if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad ) + { + /* point not kept, mark it as NOTUSED */ + used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED; + /* remove point from the region */ + reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */ + reg[i].y = reg[*reg_size-1].y; + --(*reg_size); + --i; /* to avoid skipping one point */ + } + + /* reject if the region is too small. + 2 is the minimal region size for 'region2rect' to work. */ + if( *reg_size < 2 ) return FALSE; + + /* re-compute rectangle */ + region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec); + + /* re-compute region points density */ + density = (double) *reg_size / + ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); + } + + /* if this point is reached, the density criterion is satisfied */ + return TRUE; +} + +/*----------------------------------------------------------------------------*/ +/** Refine a rectangle. + + For that, an estimation of the angle tolerance is performed by the + standard deviation of the angle at points near the region's + starting point. Then, a new region is grown starting from the same + point, but using the estimated angle tolerance. If this fails to + produce a rectangle with the right density of region points, + 'reduce_region_radius' is called to try to satisfy this condition. + */ +static int refine( struct point * reg, int * reg_size, image_double modgrad, + double reg_angle, double prec, double p, struct rect * rec, + image_char used, image_double angles, double density_th ) +{ + double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum; + int i,n; + + /* check parameters */ + if( reg == NULL ) error("refine: invalid pointer 'reg'."); + if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'."); + if( prec < 0.0 ) error("refine: 'prec' must be positive."); + if( rec == NULL ) error("refine: invalid pointer 'rec'."); + if( used == NULL || used->data == NULL ) + error("refine: invalid image 'used'."); + if( angles == NULL || angles->data == NULL ) + error("refine: invalid image 'angles'."); + + /* compute region points density */ + density = (double) *reg_size / + ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); + + /* if the density criterion is satisfied there is nothing to do */ + if( density >= density_th ) return TRUE; + + /*------ First try: reduce angle tolerance ------*/ + + /* compute the new mean angle and tolerance */ + xc = (double) reg[0].x; + yc = (double) reg[0].y; + ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ]; + sum = s_sum = 0.0; + n = 0; + for(i=0; i<*reg_size; i++) + { + used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED; + if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width ) + { + angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ]; + ang_d = angle_diff_signed(angle,ang_c); + sum += ang_d; + s_sum += ang_d * ang_d; + ++n; + } + } + + /* should not happen */ + if(n == 0) return FALSE; + + mean_angle = sum / (double) n; + tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n + + mean_angle*mean_angle ); /* 2 * standard deviation */ + + /* find a new region from the same starting point and new angle tolerance */ + region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau); + + /* if the region is too small, reject */ + if( *reg_size < 2 ) return FALSE; + + /* re-compute rectangle */ + region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec); + + /* re-compute region points density */ + density = (double) *reg_size / + ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); + + /*------ Second try: reduce region radius ------*/ + if( density < density_th ) + return reduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p, + rec, used, angles, density_th ); + + /* if this point is reached, the density criterion is satisfied */ + return TRUE; +} + + +/*----------------------------------------------------------------------------*/ +/*-------------------------- Line Segment Detector ---------------------------*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/** LSD full interface. + */ +static +double * LineSegmentDetection( int * n_out, + double * img, int X, int Y, + double scale, double sigma_scale, double quant, + double ang_th, double log_eps, double density_th, + int n_bins, + int ** reg_img, int * reg_x, int * reg_y ) +{ + image_double image; + ntuple_list out = new_ntuple_list(7); + double * return_value; + image_double scaled_image,angles,modgrad; + image_char used; + image_int region = NULL; + struct coorlist * list_p; + void * mem_p; + struct rect rec; + struct point * reg; + int reg_size,min_reg_size,i; + unsigned int xsize,ysize; + double rho,reg_angle,prec,p,log_nfa,logNT; + int ls_count = 0; /* line segments are numbered 1,2,3,... */ + + + /* check parameters */ + if( img == NULL || X <= 0 || Y <= 0 ) error("invalid image input."); + if( scale <= 0.0 ) error("'scale' value must be positive."); + if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive."); + if( quant < 0.0 ) error("'quant' value must be positive."); + if( ang_th <= 0.0 || ang_th >= 180.0 ) + error("'ang_th' value must be in the range (0,180)."); + if( density_th < 0.0 || density_th > 1.0 ) + error("'density_th' value must be in the range [0,1]."); + if( n_bins <= 0 ) error("'n_bins' value must be positive."); + + + /* angle tolerance */ + prec = M_PI * ang_th / 180.0; + p = ang_th / 180.0; + rho = quant / sin(prec); /* gradient magnitude threshold */ + + + /* load and scale image (if necessary) and compute angle at each pixel */ + image = new_image_double_ptr( (unsigned int) X, (unsigned int) Y, img ); + if( scale != 1.0 ) + { + scaled_image = gaussian_sampler( image, scale, sigma_scale ); + angles = ll_angle( scaled_image, rho, &list_p, &mem_p, + &modgrad, (unsigned int) n_bins ); + free_image_double(scaled_image); + } + else + angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad, + (unsigned int) n_bins ); + xsize = angles->xsize; + ysize = angles->ysize; + + /* Number of Tests - NT + + The theoretical number of tests is Np.(XY)^(5/2) + where X and Y are number of columns and rows of the image. + Np corresponds to the number of angle precisions considered. + As the procedure 'rect_improve' tests 5 times to halve the + angle precision, and 5 more times after improving other factors, + 11 different precision values are potentially tested. Thus, + the number of tests is + 11 * (X*Y)^(5/2) + whose logarithm value is + log10(11) + 5/2 * (log10(X) + log10(Y)). + */ + logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0 + + log10(11.0); + min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region + that can give a meaningful event */ + + + /* initialize some structures */ + if( reg_img != NULL && reg_x != NULL && reg_y != NULL ) /* save region data */ + region = new_image_int_ini(angles->xsize,angles->ysize,0); + used = new_image_char_ini(xsize,ysize,NOTUSED); + reg = (struct point *) calloc( (size_t) (xsize*ysize), sizeof(struct point) ); + if( reg == NULL ) error("not enough memory!"); + + + /* search for line segments */ + for(; list_p != NULL; list_p = list_p->next ) + if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED && + angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF ) + /* there is no risk of double comparison problems here + because we are only interested in the exact NOTDEF value */ + { + /* find the region of connected point and ~equal angle */ + region_grow( list_p->x, list_p->y, angles, reg, ®_size, + ®_angle, used, prec ); + + /* reject small regions */ + if( reg_size < min_reg_size ) continue; + + /* construct rectangular approximation for the region */ + region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec); + + /* Check if the rectangle exceeds the minimal density of + region points. If not, try to improve the region. + The rectangle will be rejected if the final one does + not fulfill the minimal density condition. + This is an addition to the original LSD algorithm published in + "LSD: A Fast Line Segment Detector with a False Detection Control" + by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall. + The original algorithm is obtained with density_th = 0.0. + */ + if( !refine( reg, ®_size, modgrad, reg_angle, + prec, p, &rec, used, angles, density_th ) ) continue; + + /* compute NFA value */ + log_nfa = rect_improve(&rec,angles,logNT,log_eps); + if( log_nfa <= log_eps ) continue; + + /* A New Line Segment was found! */ + ++ls_count; /* increase line segment counter */ + + /* + The gradient was computed with a 2x2 mask, its value corresponds to + points with an offset of (0.5,0.5), that should be added to output. + The coordinates origin is at the center of pixel (0,0). + */ + rec.x1 += 0.5; rec.y1 += 0.5; + rec.x2 += 0.5; rec.y2 += 0.5; + + /* scale the result values if a subsampling was performed */ + if( scale != 1.0 ) + { + rec.x1 /= scale; rec.y1 /= scale; + rec.x2 /= scale; rec.y2 /= scale; + rec.width /= scale; + } + + /* add line segment found to output */ + add_7tuple( out, rec.x1, rec.y1, rec.x2, rec.y2, + rec.width, rec.p, log_nfa ); + + /* add region number to 'region' image if needed */ + if( region != NULL ) + for(i=0; idata[ reg[i].x + reg[i].y * region->xsize ] = ls_count; + } + + + /* free memory */ + free( (void *) image ); /* only the double_image structure should be freed, + the data pointer was provided to this functions + and should not be destroyed. */ + free_image_double(angles); + free_image_double(modgrad); + free_image_char(used); + free( (void *) reg ); + free( (void *) mem_p ); + + /* return the result */ + if( reg_img != NULL && reg_x != NULL && reg_y != NULL ) + { + if( region == NULL ) error("'region' should be a valid image."); + *reg_img = region->data; + if( region->xsize > (unsigned int) INT_MAX || + region->ysize > (unsigned int) INT_MAX ) + error("region image to big to fit in INT sizes."); + *reg_x = (int) (region->xsize); + *reg_y = (int) (region->ysize); + + /* free the 'region' structure. + we cannot use the function 'free_image_int' because we need to keep + the memory with the image data to be returned by this function. */ + free( (void *) region ); + } + if( out->size > (unsigned int) INT_MAX ) + error("too many detections to fit in an INT."); + *n_out = (int) (out->size); + + return_value = out->values; + free( (void *) out ); /* only the 'ntuple_list' structure must be freed, + but the 'values' pointer must be keep to return + as a result. */ + + return return_value; +} +#if 0 +/*----------------------------------------------------------------------------*/ +/** LSD Simple Interface with Scale and Region output. + */ +static +double * lsd_scale_region( int * n_out, + double * img, int X, int Y, double scale, + int ** reg_img, int * reg_x, int * reg_y ) +{ + /* LSD parameters */ + double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as + sigma = sigma_scale/scale. */ + double quant = 2.0; /* Bound to the quantization error on the + gradient norm. */ + double ang_th = 22.5; /* Gradient angle tolerance in degrees. */ + double log_eps = 0.0; /* Detection threshold: -log10(NFA) > log_eps */ + double density_th = 0.7; /* Minimal density of region points in rectangle. */ + int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient + modulus. */ + + return LineSegmentDetection( n_out, img, X, Y, scale, sigma_scale, quant, + ang_th, log_eps, density_th, n_bins, + reg_img, reg_x, reg_y ); +} + +/*----------------------------------------------------------------------------*/ +/** LSD Simple Interface with Scale. + */ +static +double * lsd_scale(int * n_out, double * img, int X, int Y, double scale) +{ + return lsd_scale_region(n_out,img,X,Y,scale,NULL,NULL,NULL); +} + +/*----------------------------------------------------------------------------*/ +/** LSD Simple Interface. + */ +static +double * lsd(int * n_out, double * img, int X, int Y) +{ + /* LSD parameters */ + double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */ + + return lsd_scale(n_out,img,X,Y,scale); +} +/*----------------------------------------------------------------------------*/ +#endif +/*================================================================================== + * end of LSD code + *==================================================================================*/ + +// clang-format on + +#undef NOTDEF +#undef NOTUSED +#undef USED +#undef RELATIVE_ERROR_FACTOR +#undef TABSIZE + +// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh +// vim: shiftwidth=2 expandtab tabstop=2 cindent +// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified; diff --git a/rtengine/ashift_nmsimplex.c b/rtengine/ashift_nmsimplex.c new file mode 100644 index 000000000..512bac878 --- /dev/null +++ b/rtengine/ashift_nmsimplex.c @@ -0,0 +1,425 @@ +/* + This file is part of darktable, + copyright (c) 2016 Ulrich Pegelow. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ + +/* For parameter optimization we are using the Nelder-Mead simplex method + * implemented by Michael F. Hutt. + * Changes versus the original code: + * do not include "nmsimplex.h" (not needed) + * renamed configuration variables to NMS_* + * add additional argument to objfun for arbitrary parameters + * simplex() returns number of used iterations instead of min value + * maximum number of iterations as function parameter + * make interface function simplex() static + * initialize i and j to avoid compiler warnings + * comment out printing of status inormation + * reformat according to darktable's clang standards + */ + +/*================================================================================== + * begin nmsimplex code downloaded from http://www.mikehutt.com/neldermead.html + * on February 6, 2016 + *==================================================================================*/ +/* + * Program: nmsimplex.c + * Author : Michael F. Hutt + * http://www.mikehutt.com + * 11/3/97 + * + * An implementation of the Nelder-Mead simplex method. + * + * Copyright (c) 1997-2011 + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * Jan. 6, 1999 + * Modified to conform to the algorithm presented + * in Margaret H. Wright's paper on Direct Search Methods. + * + * Jul. 23, 2007 + * Fixed memory leak. + * + * Mar. 1, 2011 + * Added constraints. + */ + +//#include "nmsimplex.h" + +static int simplex(double (*objfunc)(double[], void *params), double start[], int n, double EPSILON, double scale, + int maxiter, void (*constrain)(double[], int n), void *params) +{ + + int vs; /* vertex with smallest value */ + int vh; /* vertex with next smallest value */ + int vg; /* vertex with largest value */ + + int i = 0, j = 0, m, row; + int k; /* track the number of function evaluations */ + int itr; /* track the number of iterations */ + + double **v; /* holds vertices of simplex */ + double pn, qn; /* values used to create initial simplex */ + double *f; /* value of function at each vertex */ + double fr; /* value of function at reflection point */ + double fe; /* value of function at expansion point */ + double fc; /* value of function at contraction point */ + double *vr; /* reflection - coordinates */ + double *ve; /* expansion - coordinates */ + double *vc; /* contraction - coordinates */ + double *vm; /* centroid - coordinates */ + //double min; + + double fsum, favg, s, cent; + + /* dynamically allocate arrays */ + + /* allocate the rows of the arrays */ + v = (double **)malloc((n + 1) * sizeof(double *)); + f = (double *)malloc((n + 1) * sizeof(double)); + vr = (double *)malloc(n * sizeof(double)); + ve = (double *)malloc(n * sizeof(double)); + vc = (double *)malloc(n * sizeof(double)); + vm = (double *)malloc(n * sizeof(double)); + + /* allocate the columns of the arrays */ + for(i = 0; i <= n; i++) + { + v[i] = (double *)malloc(n * sizeof(double)); + } + + /* create the initial simplex */ + /* assume one of the vertices is 0,0 */ + + pn = scale * (sqrt(n + 1) - 1 + n) / (n * sqrt(2)); + qn = scale * (sqrt(n + 1) - 1) / (n * sqrt(2)); + + for(i = 0; i < n; i++) + { + v[0][i] = start[i]; + } + + for(i = 1; i <= n; i++) + { + for(j = 0; j < n; j++) + { + if(i - 1 == j) + { + v[i][j] = pn + start[j]; + } + else + { + v[i][j] = qn + start[j]; + } + } + } + + if(constrain != NULL) + { + constrain(v[j], n); + } + /* find the initial function values */ + for(j = 0; j <= n; j++) + { + f[j] = objfunc(v[j], params); + } + + k = n + 1; +#if 0 + /* print out the initial values */ + printf("Initial Values\n"); + for(j = 0; j <= n; j++) + { + for(i = 0; i < n; i++) + { + printf("%f %f\n", v[j][i], f[j]); + } + } +#endif + + /* begin the main loop of the minimization */ + for(itr = 1; itr <= maxiter; itr++) + { + /* find the index of the largest value */ + vg = 0; + for(j = 0; j <= n; j++) + { + if(f[j] > f[vg]) + { + vg = j; + } + } + + /* find the index of the smallest value */ + vs = 0; + for(j = 0; j <= n; j++) + { + if(f[j] < f[vs]) + { + vs = j; + } + } + + /* find the index of the second largest value */ + vh = vs; + for(j = 0; j <= n; j++) + { + if(f[j] > f[vh] && f[j] < f[vg]) + { + vh = j; + } + } + + /* calculate the centroid */ + for(j = 0; j <= n - 1; j++) + { + cent = 0.0; + for(m = 0; m <= n; m++) + { + if(m != vg) + { + cent += v[m][j]; + } + } + vm[j] = cent / n; + } + + /* reflect vg to new vertex vr */ + for(j = 0; j <= n - 1; j++) + { + /*vr[j] = (1+NMS_ALPHA)*vm[j] - NMS_ALPHA*v[vg][j];*/ + vr[j] = vm[j] + NMS_ALPHA * (vm[j] - v[vg][j]); + } + if(constrain != NULL) + { + constrain(vr, n); + } + fr = objfunc(vr, params); + k++; + + if(fr < f[vh] && fr >= f[vs]) + { + for(j = 0; j <= n - 1; j++) + { + v[vg][j] = vr[j]; + } + f[vg] = fr; + } + + /* investigate a step further in this direction */ + if(fr < f[vs]) + { + for(j = 0; j <= n - 1; j++) + { + /*ve[j] = NMS_GAMMA*vr[j] + (1-NMS_GAMMA)*vm[j];*/ + ve[j] = vm[j] + NMS_GAMMA * (vr[j] - vm[j]); + } + if(constrain != NULL) + { + constrain(ve, n); + } + fe = objfunc(ve, params); + k++; + + /* by making fe < fr as opposed to fe < f[vs], + Rosenbrocks function takes 63 iterations as opposed + to 64 when using double variables. */ + + if(fe < fr) + { + for(j = 0; j <= n - 1; j++) + { + v[vg][j] = ve[j]; + } + f[vg] = fe; + } + else + { + for(j = 0; j <= n - 1; j++) + { + v[vg][j] = vr[j]; + } + f[vg] = fr; + } + } + + /* check to see if a contraction is necessary */ + if(fr >= f[vh]) + { + if(fr < f[vg] && fr >= f[vh]) + { + /* perform outside contraction */ + for(j = 0; j <= n - 1; j++) + { + /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/ + vc[j] = vm[j] + NMS_BETA * (vr[j] - vm[j]); + } + if(constrain != NULL) + { + constrain(vc, n); + } + fc = objfunc(vc, params); + k++; + } + else + { + /* perform inside contraction */ + for(j = 0; j <= n - 1; j++) + { + /*vc[j] = NMS_BETA*v[vg][j] + (1-NMS_BETA)*vm[j];*/ + vc[j] = vm[j] - NMS_BETA * (vm[j] - v[vg][j]); + } + if(constrain != NULL) + { + constrain(vc, n); + } + fc = objfunc(vc, params); + k++; + } + + + if(fc < f[vg]) + { + for(j = 0; j <= n - 1; j++) + { + v[vg][j] = vc[j]; + } + f[vg] = fc; + } + /* at this point the contraction is not successful, + we must halve the distance from vs to all the + vertices of the simplex and then continue. + 10/31/97 - modified to account for ALL vertices. + */ + else + { + for(row = 0; row <= n; row++) + { + if(row != vs) + { + for(j = 0; j <= n - 1; j++) + { + v[row][j] = v[vs][j] + (v[row][j] - v[vs][j]) / 2.0; + } + } + } + if(constrain != NULL) + { + constrain(v[vg], n); + } + f[vg] = objfunc(v[vg], params); + k++; + if(constrain != NULL) + { + constrain(v[vh], n); + } + f[vh] = objfunc(v[vh], params); + k++; + } + } +#if 0 + /* print out the value at each iteration */ + printf("Iteration %d\n", itr); + for(j = 0; j <= n; j++) + { + for(i = 0; i < n; i++) + { + printf("%f %f\n", v[j][i], f[j]); + } + } +#endif + /* test for convergence */ + fsum = 0.0; + for(j = 0; j <= n; j++) + { + fsum += f[j]; + } + favg = fsum / (n + 1); + s = 0.0; + for(j = 0; j <= n; j++) + { + s += pow((f[j] - favg), 2.0) / (n); + } + s = sqrt(s); + if(s < EPSILON) break; + } + /* end main loop of the minimization */ + + /* find the index of the smallest value */ + vs = 0; + for(j = 0; j <= n; j++) + { + if(f[j] < f[vs]) + { + vs = j; + } + } +#if 0 + printf("The minimum was found at\n"); + for(j = 0; j < n; j++) + { + printf("%e\n", v[vs][j]); + start[j] = v[vs][j]; + } + double min = objfunc(v[vs], params); + k++; + printf("The minimum value is %f\n", min); + printf("%d Function Evaluations\n", k); + printf("%d Iterations through program\n", itr); +#else + for(j = 0; j < n; j++) + { + start[j] = v[vs][j]; + } +#endif + free(f); + free(vr); + free(ve); + free(vc); + free(vm); + for(i = 0; i <= n; i++) + { + free(v[i]); + } + free(v); + return itr; +} + +/*================================================================================== + * end of nmsimplex code + *==================================================================================*/ + +// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh +// vim: shiftwidth=2 expandtab tabstop=2 cindent +// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified; diff --git a/rtengine/camconst.json b/rtengine/camconst.json index 5fc7d4062..6e686c2f1 100644 --- a/rtengine/camconst.json +++ b/rtengine/camconst.json @@ -430,6 +430,7 @@ Camera constants: { // Quality B, some intermediate ISO samples missing, LENR samples missing so White Levels not properly indicated, some aperture scaling missing "make_model": "Canon EOS 5D Mark IV", + "global_green_equilibration" : true, "dcraw_matrix": [ 6446,-366,-864,-4436,12204,2513,-952,2496,6348 ], // DNG_V9.7 D65 "raw_crop": [ 136, 42, 6740, 4500 ], // full size 6880x4544, official crop 148,54,6867,4533 "masked_areas": [ 54, 4, 4534, 132 ], @@ -1295,12 +1296,6 @@ Camera constants: "ranges": { "white": 16100 } }, - { // Quality C - "make_model": [ "FUJIFILM X100V", "FUJIFILM X-T4" ], - "dcraw_matrix": [ 13426,-6334,-1177,-4244,12136,2371,-580,1303,5980 ], // DNG_v12.2 D65 - "raw_crop": [ 0, 5, 6252, 4140 ] - }, - { // Quality C "make_model": "Fujifilm X10", "ranges": { "white": 3788 } @@ -1384,7 +1379,7 @@ Camera constants: }, { // Quality C, only raw crop - "make_model": [ "FUJIFILM X-T3", "FUJIFILM X-T30", "FUJIFILM X-PRO3" ], + "make_model": [ "FUJIFILM X-T3", "FUJIFILM X-T30", "FUJIFILM X-PRO3", "FUJIFILM X100V", "FUJIFILM X-T4" ], "dcraw_matrix": [ 13426,-6334,-1177,-4244,12136,2371,-580,1303,5980 ], // DNG_v11, standard_v2 d65 "raw_crop": [ 0, 5, 6252, 4176] }, diff --git a/rtengine/ciecam02.cc b/rtengine/ciecam02.cc index 208ed4366..c591bcb2c 100644 --- a/rtengine/ciecam02.cc +++ b/rtengine/ciecam02.cc @@ -22,11 +22,6 @@ #include #include "sleef.h" -#ifdef _DEBUG -#include "settings.h" -#include -#endif - #undef CLIPD #define CLIPD(a) ((a)>0.f?((a)<1.f?(a):1.f):0.f) #define MAXR(a,b) ((a) > (b) ? (a) : (b)) @@ -420,13 +415,6 @@ void Ciecam02::initcam1float (float yb, float pilotd, float f, float la, float x aw = achromatic_response_to_whitefloat ( xw, yw, zw, d, fl, nbb); wh = ( 4.0f / c ) * ( aw + 4.0f ) * pow_F ( fl, 0.25f ); pfl = pow_F ( fl, 0.25f ); -#ifdef _DEBUG - - if (settings->verbose) { - printf ("Source float d=%f aw=%f fl=%f wh=%f c=%f awc=%f\n", d, aw, fl, wh, c, (4.f / c) * (aw + 4.f)); - } - -#endif } void Ciecam02::initcam2float (float yb, float pilotd, float f, float la, float xw, float yw, float zw, float &n, float &d, float &nbb, float &ncb, @@ -445,13 +433,6 @@ void Ciecam02::initcam2float (float yb, float pilotd, float f, float la, float x nbb = ncb = 0.725f * pow_F ( 1.0f / n, 0.2f ); cz = 1.48f + sqrt ( n ); aw = achromatic_response_to_whitefloat ( xw, yw, zw, d, fl, nbb); -#ifdef _DEBUG - - if (settings->verbose) { - printf ("Viewing float d=%f aw=%f fl=%f n=%f\n", d, aw, fl, n); - } - -#endif } void Ciecam02::xyz2jchqms_ciecam02float ( float &J, float &C, float &h, float &Q, float &M, float &s, float aw, float fl, float wh, diff --git a/rtengine/color.cc b/rtengine/color.cc index 89be69e9d..11668451e 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -25,10 +25,6 @@ #include "opthelper.h" #include "iccstore.h" -#ifdef _DEBUG -#include "mytime.h" -#endif - using namespace std; namespace rtengine @@ -45,6 +41,9 @@ LUTf Color::igammatab_srgb; LUTf Color::igammatab_srgb1; LUTf Color::gammatab_srgb; LUTf Color::gammatab_srgb1; +LUTf Color::gammatab_srgb327; +LUTf Color::gammatab_bt709; +LUTf Color::igammatab_bt709; LUTf Color::denoiseGammaTab; LUTf Color::denoiseIGammaTab; @@ -103,20 +102,6 @@ LUTf Color::_10GY30, Color::_10GY40, Color::_10GY50, Color::_10GY60, Color::_10G LUTf Color::_75GY30, Color::_75GY40, Color::_75GY50, Color::_75GY60, Color::_75GY70, Color::_75GY80; LUTf Color::_5GY30, Color::_5GY40, Color::_5GY50, Color::_5GY60, Color::_5GY70, Color::_5GY80; -#ifdef _DEBUG -MunsellDebugInfo::MunsellDebugInfo() -{ - reinitValues(); -} -void MunsellDebugInfo::reinitValues() -{ - maxdhue[0] = maxdhue[1] = maxdhue[2] = maxdhue[3] = 0.0f; - maxdhuelum[0] = maxdhuelum[1] = maxdhuelum[2] = maxdhuelum[3] = 0.0f; - depass = depassLum = 0; -} -#endif - - void Color::init () { @@ -130,9 +115,12 @@ void Color::init () gammatabThumb(maxindex, 0); igammatab_srgb(maxindex, 0); + igammatab_bt709(maxindex, 0); igammatab_srgb1(maxindex, 0); gammatab_srgb(maxindex, 0); + gammatab_bt709(maxindex, 0); gammatab_srgb1(maxindex, 0); + gammatab_srgb327(32768, 0); denoiseGammaTab(maxindex, 0); denoiseIGammaTab(maxindex, 0); @@ -197,6 +185,18 @@ void Color::init () } #ifdef _OPENMP #pragma omp section +#endif + { + for (int i = 0; i < 32768; i++) + { + gammatab_srgb327[i] = gamma2(i / 32767.0); + } + + gammatab_srgb327 *= 32767.f; + // gamma2curve.share(gammatab_srgb, LUT_CLIP_BELOW | LUT_CLIP_ABOVE); // shares the buffer with gammatab_srgb but has different clip flags + } +#ifdef _OPENMP + #pragma omp section #endif { for (int i = 0; i < maxindex; i++) @@ -206,6 +206,7 @@ void Color::init () igammatab_srgb *= 65535.f; } + #ifdef _OPENMP #pragma omp section #endif @@ -282,6 +283,22 @@ void Color::init () break; } +#ifdef _OPENMP + #pragma omp section +#endif + + for (int i = 0; i < maxindex; i++) { + gammatab_bt709[i] = 65535.0 * gamma709(i / 65535.0); + } + +#ifdef _OPENMP + #pragma omp section +#endif + + for (int i = 0; i < maxindex; i++) { + igammatab_bt709[i] = 65535.0 * igamma709(i / 65535.0); + } + #ifdef _OPENMP #pragma omp section #endif @@ -1421,15 +1438,7 @@ void Color::interpolateRGBColor (const float balance, const float r1, const floa Color::Lab2Lch(a_1, b_1, c1, h1); Lr = L1 / 327.68f; //for gamutlch //gamut control on r1 g1 b1 -#ifndef NDEBUG - bool neg = false; - bool more_rgb = false; - - //gamut control : Lab values are in gamut - Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb); -#else Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f); -#endif L1 = Lr * 327.68f; @@ -1440,14 +1449,7 @@ void Color::interpolateRGBColor (const float balance, const float r1, const floa Lr = L2 / 327.68f; //for gamutlch //gamut control on r2 g2 b2 -#ifndef NDEBUG - neg = false; - more_rgb = false; - //gamut control : Lab values are in gamut - Color::gamutLchonly(h2, Lr, c2, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb); -#else Color::gamutLchonly(h2, Lr, c2, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f); -#endif L2 = Lr * 327.68f; // interpolating Lch values @@ -1477,15 +1479,8 @@ void Color::interpolateRGBColor (const float balance, const float r1, const floa // here I have put gamut control with gamutlchonly on final process Lr = L1 / 327.68f; //for gamutlch -#ifndef NDEBUG - neg = false; - more_rgb = false; - //gamut control : Lab values are in gamut - Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb); -#else //gamut control : Lab values are in gamut Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f); -#endif //convert CH ==> ab L1 = Lr * 327.68f; @@ -2264,11 +2259,7 @@ void Color::transitred (const float HH, float const Chprov1, const float dred, c * float correctlum : correction Hue for luminance (brigtness, contrast,...) * MunsellDebugInfo* munsDbgInfo: (Debug target only) object to collect information. */ -#ifdef _DEBUG -void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHuechroma, float &correctlum, MunsellDebugInfo* munsDbgInfo) -#else void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHuechroma, float &correctlum) -#endif { bool contin1, contin2; @@ -2292,22 +2283,6 @@ void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, fl contin1 = contin2 = false; correctL = false; MunsellLch (Lprov1, HH, Chprov1, CC, correctionHue, zo, correctionHueLum, correctL); //munsell chroma correction -#ifdef _DEBUG - float absCorrectionHue = fabs(correctionHue); - - if(correctionHue != 0.0) { - int idx = zo - 1; - #pragma omp critical (maxdhue) - { - munsDbgInfo->maxdhue[idx] = MAX(munsDbgInfo->maxdhue[idx], absCorrectionHue); - } - } - - if(absCorrectionHue > 0.45) - #pragma omp atomic - munsDbgInfo->depass++; //verify if no bug in calculation - -#endif correctionHuechroma = correctionHue; //preserve if(lumaMuns) { @@ -2339,46 +2314,11 @@ void Color::AllMunsellLch(bool lumaMuns, float Lprov1, float Loldd, float HH, fl if(contin1 && contin2) { correctlum = correctlumprov2 - correctlumprov; } - -#ifdef _DEBUG - float absCorrectLum = fabs(correctlum); - - if(correctlum != 0.0) { - int idx = zo - 1; - #pragma omp critical (maxdhuelum) - { - munsDbgInfo->maxdhuelum[idx] = MAX(munsDbgInfo->maxdhuelum[idx], absCorrectLum); - } - } - - if(absCorrectLum > 0.35) - #pragma omp atomic - munsDbgInfo->depassLum++; //verify if no bug in calculation - -#endif } } } } - } - -#ifdef _DEBUG - - if (correctlum < -0.35f) { - correctlum = -0.35f; - } else if(correctlum > 0.35f) { - correctlum = 0.35f; - } - - if (correctionHuechroma < -0.45f) { - correctionHuechroma = -0.45f; - } else if(correctionHuechroma > 0.45f) { - correctionHuechroma = 0.45f; - } - -#endif - } /* @@ -2437,17 +2377,10 @@ void Color::AllMunsellLch(float Lprov1, float HH, float Chprov1, float CC, float * float coef : a float number between [0.95 ; 1.0[... the nearest it is from 1.0, the more precise it will be... and the longer too as more iteration will be necessary) * bool neg and moreRGB : only in DEBUG mode to calculate iterations for negatives values and > 65535 */ -#ifdef _DEBUG -void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef, bool &neg, bool &more_rgb) -#else void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef) -#endif { const float ClipLevel = 65535.0f; bool inGamut; -#ifdef _DEBUG - neg = false, more_rgb = false; -#endif float2 sincosval = xsincosf(HH); do { @@ -2471,10 +2404,6 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo // gamut control before saturation to put Lab values in future gamut, but not RGB if (R < 0.0f || G < 0.0f || B < 0.0f) { -#ifdef _DEBUG - neg = true; -#endif - if (Lprov1 < 0.1f) { Lprov1 = 0.1f; } @@ -2520,10 +2449,6 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo } else if (!isHLEnabled && rtengine::max(R, G, B) > ClipLevel && rtengine::min(R, G, B) <= ClipLevel) { // if "highlight reconstruction" is enabled or the point is completely white (clipped, no color), don't control Gamut -#ifdef _DEBUG - more_rgb = true; -#endif - if (Lprov1 > 99.999f) { Lprov1 = 99.98f; } @@ -2558,17 +2483,10 @@ void Color::gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, flo * float coef : a float number between [0.95 ; 1.0[... the nearest it is from 1.0, the more precise it will be... and the longer too as more iteration will be necessary) * bool neg and moreRGB : only in DEBUG mode to calculate iterations for negatives values and > 65535 */ -#ifdef _DEBUG -void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef, bool &neg, bool &more_rgb) -#else void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef) -#endif { constexpr float ClipLevel = 65535.0f; bool inGamut; -#ifdef _DEBUG - neg = false, more_rgb = false; -#endif float ChprovSave = Chprov1; do { @@ -2590,9 +2508,6 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr // gamut control before saturation to put Lab values in future gamut, but not RGB if (R < 0.0f || G < 0.0f || B < 0.0f) { -#ifdef _DEBUG - neg = true; -#endif if (isnan(HH)) { float atemp = ChprovSave * sincosval.y * 327.68; @@ -2645,10 +2560,6 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr } else if (!isHLEnabled && rtengine::max(R, G, B) > ClipLevel && rtengine::min(R, G, B) <= ClipLevel) { // if "highlight reconstruction" is enabled or the point is completely white (clipped, no color), don't control Gamut -#ifdef _DEBUG - more_rgb = true; -#endif - if (Lprov1 > 99.999f) { Lprov1 = 99.98f; } @@ -2770,17 +2681,10 @@ void Color::gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chpr } -#ifdef _DEBUG -void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef, bool &neg, bool &more_rgb) -#else void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], const bool isHLEnabled, const float lowerCoef, const float higherCoef) -#endif { const float ClipLevel = 65535.0f; bool inGamut; -#ifdef _DEBUG - neg = false, more_rgb = false; -#endif do { inGamut = true; @@ -2804,10 +2708,6 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const // gamut control before saturation to put Lab values in future gamut, but not RGB if (R < 0.0f || G < 0.0f || B < 0.0f) { -#ifdef _DEBUG - neg = true; -#endif - if (Lprov1 < 0.01f) { Lprov1 = 0.01f; } @@ -2822,10 +2722,6 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const } else if (!isHLEnabled && rtengine::max(R, G, B) > ClipLevel && rtengine::min(R, G, B) <= ClipLevel) { // if "highlight reconstruction" is enabled or the point is completely white (clipped, no color), don't control Gamut -#ifdef _DEBUG - more_rgb = true; -#endif - if (Lprov1 > 99.999f) { Lprov1 = 99.98f; } @@ -2865,17 +2761,6 @@ void Color::gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const */ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N, bool corMunsell, bool lumaMuns, bool isHLEnabled, bool gamut, const double wip[3][3]) { -#ifdef _DEBUG - MyTime t1e, t2e; - t1e.set(); - int negat = 0, moreRGB = 0; - MunsellDebugInfo* MunsDebugInfo = nullptr; - - if (corMunsell) { - MunsDebugInfo = new MunsellDebugInfo(); - } - -#endif float correctlum = 0.f; float correctionHuechroma = 0.f; #ifdef __SSE2__ @@ -2914,9 +2799,6 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N, float2 sincosval; if(gamut) { -#ifdef _DEBUG - bool neg, more_rgb; -#endif // According to mathematical laws we can get the sin and cos of HH by simple operations float R, G, B; @@ -2929,23 +2811,7 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N, } //gamut control : Lab values are in gamut -#ifdef _DEBUG - gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, isHLEnabled, 0.15f, 0.96f, neg, more_rgb); -#else gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, isHLEnabled, 0.15f, 0.96f); -#endif - -#ifdef _DEBUG - - if(neg) { - negat++; - } - - if(more_rgb) { - moreRGB++; - } - -#endif } labL[j] = Lprov1 * 327.68f; @@ -2953,12 +2819,7 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N, correctlum = 0.f; if(corMunsell) -#ifdef _DEBUG - AllMunsellLch(lumaMuns, Lprov1, Loldd, HH, Chprov1, Coldd, correctionHuechroma, correctlum, MunsDebugInfo); - -#else AllMunsellLch(lumaMuns, Lprov1, Loldd, HH, Chprov1, Coldd, correctionHuechroma, correctlum); -#endif if(correctlum == 0.f && correctionHuechroma == 0.f) { if(!gamut) { @@ -2979,28 +2840,6 @@ void Color::LabGamutMunsell(float *labL, float *laba, float *labb, const int N, laba[j] = Chprov1 * sincosval.y * 327.68f; labb[j] = Chprov1 * sincosval.x * 327.68f; } - -#ifdef _DEBUG - t2e.set(); - - if (settings->verbose) { - printf("Color::LabGamutMunsell (correction performed in %d usec):\n", t2e.etime(t1e)); - printf(" Gamut : G1negat=%iiter G165535=%iiter \n", negat, moreRGB); - - if (MunsDebugInfo) { - printf(" Munsell chrominance: MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad depass=%u\n", MunsDebugInfo->maxdhue[0], MunsDebugInfo->maxdhue[1], MunsDebugInfo->maxdhue[2], MunsDebugInfo->maxdhue[3], MunsDebugInfo->depass); - printf(" Munsell luminance : MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad depass=%u\n", MunsDebugInfo->maxdhuelum[0] , MunsDebugInfo->maxdhuelum[1], MunsDebugInfo->maxdhuelum[2], MunsDebugInfo->maxdhuelum[3], MunsDebugInfo->depassLum); - } else { - printf(" Munsell correction wasn't requested\n"); - } - } - - if (MunsDebugInfo) { - delete MunsDebugInfo; - } - -#endif - } /* @@ -3105,11 +2944,6 @@ void Color::SkinSat (float lum, float hue, float chrom, float &satreduc) */ void Color::initMunsell () { -#ifdef _DEBUG - MyTime t1e, t2e; - t1e.set(); -#endif - const int maxInd = 140; const int maxInd2 = 90; const int maxInd3 = 50; @@ -5710,14 +5544,6 @@ void Color::initMunsell () //printf("5GY %1.2f %1.2f %1.2f\n",_5GY80[44],_5GY80[84],_5GY80[125] ); -#ifdef _DEBUG - t2e.set(); - - if (settings->verbose) { - printf("Lutf Munsell %d usec\n", t2e.etime(t1e)); - } - -#endif } } diff --git a/rtengine/color.h b/rtengine/color.h index 045e062ad..55ef52e36 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -36,23 +36,6 @@ namespace rtengine typedef std::array GammaValues; -#ifdef _DEBUG - -class MunsellDebugInfo -{ -public: - float maxdhuelum[4]; - float maxdhue[4]; - unsigned int depass; - unsigned int depassLum; - - MunsellDebugInfo(); - void reinitValues(); -}; - -#endif - - class Color { @@ -162,12 +145,15 @@ public: static LUTf igammatab_srgb; static LUTf igammatab_srgb1; static LUTf gammatab_srgb; + static LUTf gammatab_srgb327; static LUTf gammatab_srgb1; + static LUTf gammatab_bt709; static LUTf denoiseGammaTab; static LUTf denoiseIGammaTab; static LUTf igammatab_24_17; + static LUTf igammatab_bt709; static LUTf gammatab_24_17a; static LUTf gammatab_13_2; static LUTf igammatab_13_2; @@ -1150,23 +1136,25 @@ public: } - /* +/* * @brief Get the gamma value for Gamma=2.2 Slope=4.5 * @param x red, green or blue channel's value [0 ; 1] * @return the gamma modified's value [0 ; 1] * +*/ static inline double gamma709 (double x) { return x <= 0.0176 ? x*4.5 : 1.0954*exp(log(x)/2.2)-0.0954; } - +/* * @brief Get the inverse gamma value for Gamma=2.2 Slope=4.5 * @param x red, green or blue channel's value [0 ; 1] * @return the inverse gamma modified's value [0 ; 1] * +*/ static inline double igamma709 (double x) { return x <= 0.0795 ? x/4.5 : exp(log((x+0.0954)/1.0954)*2.2); } - */ + @@ -1390,11 +1378,7 @@ public: * @param munsDbgInfo (Debug target only) object to collect information */ -#ifdef _DEBUG - static void AllMunsellLch (bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHueChroma, float &correctlum, MunsellDebugInfo* munsDbgInfo); -#else static void AllMunsellLch (bool lumaMuns, float Lprov1, float Loldd, float HH, float Chprov1, float CC, float &correctionHueChroma, float &correctlum); -#endif static void AllMunsellLch (float Lprov1, float HH, float Chprov1, float CC, float &correctionHueChroma); @@ -1419,15 +1403,9 @@ public: * @param neg (Debug target only) to calculate iterations for negatives values * @param moreRGB (Debug target only) to calculate iterations for values >65535 */ -#ifdef _DEBUG - static void gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef, bool &neg, bool &more_rgb); - static void gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef, bool &neg, bool &more_rgb); - static void gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef, bool &neg, bool &more_rgb); -#else static void gamutLchonly (float HH, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef); static void gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &R, float &G, float &B, const double wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef); static void gamutLchonly (float2 sincosval, float &Lprov1, float &Chprov1, const float wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef); -#endif static void gamutLchonly (float HH, float2 sincosval, float &Lprov1, float &Chprov1, float &saturation, const float wip[3][3], bool isHLEnabled, float lowerCoef, float higherCoef); @@ -1494,6 +1472,57 @@ public: static void skinredfloat ( float J, float h, float sres, float Sp, float dred, float protect_red, int sk, float rstprotection, float ko, float &s); // static void scaleredcdbl ( float skinprot, float param, float limit, float HH, float deltaHH, float &scale,float &scaleext); + static inline void pregamutlab(float lum, float hue, float &chr) //big approximation to limit gamut (Prophoto) before good gamut procedure for locallab chroma, to avoid crash + { + if (lum >= 95.0f) { + if (hue > 1.5f && hue < 2.f) { + chr = 120.f; + } else if (hue > 0.7f && hue <= 1.5f) { + chr = 60.f; + } else { + chr = 40.f; + } + } else if (lum > 75.f) { + if (hue > 1.f && hue < 3.14f) { + chr = 130.f; + } else if (hue > -0.4f && hue <= 1.f) { + chr = 80.f; + } else if (hue > -3.15f && hue < -2.f) { + chr = 80.f; + } else { + chr = 60.f; + } + + } else if (lum > 35.f) { + chr = 100.f; + } else if (lum > 20.f) { + if (hue < -1.f && hue > -2.f) { + chr = 120.f; + } else { + chr = 80.f; + } + } else if (lum > 7.f) { + if (hue < -1.f && hue > -1.8f) { + chr = 120.f; + } else { + chr = 60.f; + } + + } else { + if (hue < -1.f && hue > -1.6f) { + chr = 80.f; + } else { + chr = 40.f; + } + + } + + // if(lum < 4.f) { + // chr = 0.1f; + // } + } + + static inline void SkinSatCbdl (float lum, float hue, float chrom, float skinprot, float &scale, bool neg, float b_l, float t_l, float t_r) { diff --git a/rtengine/colortemp.cc b/rtengine/colortemp.cc index 2780fcc84..417476876 100644 --- a/rtengine/colortemp.cc +++ b/rtengine/colortemp.cc @@ -2898,15 +2898,15 @@ void ColorTemp::icieCAT02float(float Xw, float Yw, float Zw, float &iCAM02BB00, float D = adap / 2.; //white destination Wd : RT use always D50 - cam_dest[0] = INVCAT02[0][0] * whiteD50p[0] + INVCAT02[0][1] * whiteD50p[1] + INVCAT02[0][2] * whiteD50p[2]; //Cone reponse RoD + cam_dest[0] = INVCAT02[0][0] * whiteD50p[0] + INVCAT02[0][1] * whiteD50p[1] + INVCAT02[0][2] * whiteD50p[2]; //Cone response RoD cam_dest[1] = INVCAT02[1][0] * whiteD50p[0] + INVCAT02[1][1] * whiteD50p[1] + INVCAT02[1][2] * whiteD50p[2]; //GaD cam_dest[2] = INVCAT02[2][0] * whiteD50p[0] + INVCAT02[2][1] * whiteD50p[1] + INVCAT02[2][2] * whiteD50p[2]; //BeD //origin white Ws : A, D65, custom, etc. - cam_orig[0] = INVCAT02[0][0] * Xw + INVCAT02[0][1] * Yw + INVCAT02[0][2] * Zw; //Cone reponse RoS + cam_orig[0] = INVCAT02[0][0] * Xw + INVCAT02[0][1] * Yw + INVCAT02[0][2] * Zw; //Cone response RoS cam_orig[1] = INVCAT02[1][0] * Xw + INVCAT02[1][1] * Yw + INVCAT02[1][2] * Zw; cam_orig[2] = INVCAT02[2][0] * Xw + INVCAT02[2][1] * Yw + INVCAT02[2][2] * Zw; -// cam_orig[0] = CAT02[0][0] * Xw + CAT02[0][1] * Yw + CAT02[0][2] * Zw; //Cone reponse RoS +// cam_orig[0] = CAT02[0][0] * Xw + CAT02[0][1] * Yw + CAT02[0][2] * Zw; //Cone response RoS // cam_orig[1] = CAT02[1][0] * Xw + CAT02[1][1] * Yw + CAT02[1][2] * Zw; // cam_orig[2] = CAT02[2][0] * Xw + CAT02[2][1] * Yw + CAT02[2][2] * Zw; @@ -3624,7 +3624,7 @@ void ColorTemp::tempxy(bool separated, int repref, float **Tx, float **Ty, float double XX; double ZZ; } WbTxyz; - //probbaly can be "passed" with rawimagesource.cc but I don't know how to do. + //probably can be "passed" with rawimagesource.cc but I don't know how to do this. constexpr WbTxyz Txyz[118] = {//temperature Xwb Zwb 118 values - same table as in Rawimagesource.cc x wb and y wb are calculated after {2001., 1.273842, 0.145295}, {2101., 1.244008, 0.167533}, diff --git a/rtengine/cplx_wavelet_dec.h b/rtengine/cplx_wavelet_dec.h index c127a7adf..592bd2f37 100644 --- a/rtengine/cplx_wavelet_dec.h +++ b/rtengine/cplx_wavelet_dec.h @@ -33,37 +33,38 @@ class wavelet_decomposition : public NonCopyable { public: - - typedef float internal_type; - float *coeff0; - bool memoryAllocationFailed; - -private: - - static const int maxlevels = 10;//should be greater than any conceivable order of decimation - - int lvltot, subsamp; - int m_w, m_h;//dimensions - - int wavfilt_len, wavfilt_offset; - float *wavfilt_anal; - float *wavfilt_synth; - - - wavelet_level * wavelet_decomp[maxlevels]; - -public: + using internal_type = float; template wavelet_decomposition(E * src, int width, int height, int maxlvl, int subsampling, int skipcrop = 1, int numThreads = 1, int Daub4Len = 6); ~wavelet_decomposition(); - internal_type ** level_coeffs(int level) const + bool memory_allocation_failed() const + { + return memoryAllocationFailed; + } + + const internal_type* const* level_coeffs(int level) const { return wavelet_decomp[level]->subbands(); } + internal_type* const* level_coeffs(int level) + { + return wavelet_decomp[level]->subbands(); + } + + const internal_type* get_coeff0() const + { + return coeff0; + } + + internal_type* get_coeff0() + { + return coeff0; + } + int level_W(int level) const { return wavelet_decomp[level]->width(); @@ -88,13 +89,47 @@ public: { return subsamp; } + template void reconstruct(E * dst, const float blend = 1.f); + +private: + static const int maxlevels = 10; // should be greater than any conceivable order of decimation + + int lvltot; + int subsamp; + // Dimensions + int m_w; + int m_h; + + int wavfilt_len; + int wavfilt_offset; + internal_type* wavfilt_anal; + internal_type* wavfilt_synth; + + internal_type* coeff0; + bool memoryAllocationFailed; + + wavelet_level* wavelet_decomp[maxlevels]; }; template -wavelet_decomposition::wavelet_decomposition(E * src, int width, int height, int maxlvl, int subsampling, int skipcrop, int numThreads, int Daub4Len) - : coeff0(nullptr), memoryAllocationFailed(false), lvltot(0), subsamp(subsampling), m_w(width), m_h(height) +wavelet_decomposition::wavelet_decomposition( + E * src, + int width, + int height, + int maxlvl, + int subsampling, + int skipcrop, + int numThreads, + int Daub4Len +) : + lvltot(0), + subsamp(subsampling), + m_w(width), + m_h(height), + coeff0(nullptr), + memoryAllocationFailed(false) { //initialize wavelet filters @@ -135,6 +170,14 @@ wavelet_decomposition::wavelet_decomposition(E * src, int width, int height, int //n=0 lopass, n=1 hipass } } +/* } else if(wavfilt_len == 22) { + for (int n = 0; n < 2; n++) { + for (int i = 0; i < wavfilt_len; i++) { + wavfilt_anal[wavfilt_len * (n) + i] = Daub4_anal22[n][i]; + wavfilt_synth[wavfilt_len * (n) + i] = Daub4_anal22[n][wavfilt_len - 1 - i]; + //n=0 lopass, n=1 hipass + } + } */ } else if(wavfilt_len == 4) { for (int n = 0; n < 2; n++) { for (int i = 0; i < wavfilt_len; i++) { @@ -144,7 +187,7 @@ wavelet_decomposition::wavelet_decomposition(E * src, int width, int height, int } } } - +//printf("OK cplx\n"); // after coefficient rotation, data structure is: // wavelet_decomp[scale][channel={lo,hi1,hi2,hi3}][pixel_array] diff --git a/rtengine/cplx_wavelet_filter_coeffs.h b/rtengine/cplx_wavelet_filter_coeffs.h index 5d191e50c..3386be8d0 100644 --- a/rtengine/cplx_wavelet_filter_coeffs.h +++ b/rtengine/cplx_wavelet_filter_coeffs.h @@ -48,11 +48,14 @@ const float Daub4_anal16[2][16] ALIGNED16 = {//Daub 14 {0.f, 0.f, 0.055049715f, 0.28039564f, 0.515574245f, 0.33218624f, -0.10175691f, -0.158417505f, 0.05042335f, 0.057001725f, -0.026891225f, -0.01171997f, 0.008874895f, 0.0003037575f, -0.0012739524f, 0.0002501134f}, { -0.0002501134f, -0.0012739524f, -0.0003037575f, 0.008874895f, 0.01171997f , -0.026891225f, -0.057001725f, 0.05042335f, 0.158417505f, -0.10175691f, -0.33218624f, 0.515574245f, -0.28039564f, 0.055049715f, 0.f, 0.f} }; -/* -const double Daub4_anal22[2][22] ALIGNED16 = {//Daub 20 - {0., 0., 0.01885858, 0.13306109, 0.37278754, 0.48681406, 0.19881887, -0.1766681, -0.13855494, 0.09006372, 0.0658015, -0.05048328, -0.02082962, 0.0234849, 0.0025502185, -0.0075895, 0.0009866625, 0.0014088433, -0.00048497392, -0.0000823545, 0.00006617718, -0.000009379205}, - {0.000009379205, -0.00006617718, 0.0000823545, 0.00048497392, -0.0014088433, -0.0009866627, 0.0075895, -0.0025502185, -0.0234849, 0.02082962, 0.05048328, -0.0658015, -0.09006372, 0.13855494, 0.1766681, -0.19881887, -0.48681406, -0.37278754, -0.13306109, -0.01885858, 0., 0.} + +const float Daub4_anal22[2][22] ALIGNED16 = {//Daub 20 + {0.f, 0.f, 0.01885858f, 0.13306109f, 0.37278535f, 0.48681406f, 0.19881887f, -0.1766681f, -0.13855494f, 0.09006372f, 0.0658015f, -0.05048328f, -0.02082962f, + 0.0234849f, 0.002550218f, -0.0075895f, 0.0009866627f, 0.001408843f, -0.000484973f, -0.0000823545f, 0.0000661271f, -0.00000939f}, + {0.00000939f, -0.0000661271f, 0.0000823545f, 0.000484973f, -0.001408843f, -0.0009866627f, 0.0075895f, -0.002550218f, -0.0234849f, + 0.02082962f, 0.05048328f, -0.0658015f, -0.09006372f, 0.13855494f, 0.1766681f, -0.19881887f, -0.48681406f, -0.37278535f, -0.13306109f, -0.01885858f, 0.f, 0.f} }; -*/ + +// if necessary ?? we can add D20 !! } diff --git a/rtengine/curves.cc b/rtengine/curves.cc index ebb23e754..7f74467be 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -39,7 +39,6 @@ using namespace std; namespace rtengine { - bool sanitizeCurve(std::vector& curve) { // A curve is valid under one of the following conditions: @@ -47,19 +46,19 @@ bool sanitizeCurve(std::vector& curve) // 2) Number of curve entries is > 3 and odd // 3) curve[0] == DCT_Parametric and curve size is >= 8 and curve[1] .. curve[3] are ordered ascending and are distinct if (curve.empty()) { - curve.push_back (DCT_Linear); + curve.push_back(DCT_Linear); return true; - } else if(curve.size() == 1 && curve[0] != DCT_Linear) { + } else if (curve.size() == 1 && curve[0] != DCT_Linear) { curve[0] = DCT_Linear; return true; - } else if((curve.size() % 2 == 0 || curve.size() < 5) && curve[0] != DCT_Parametric) { + } else if ((curve.size() % 2 == 0 || curve.size() < 5) && curve[0] != DCT_Parametric) { curve.clear(); - curve.push_back (DCT_Linear); + curve.push_back(DCT_Linear); return true; - } else if(curve[0] == DCT_Parametric) { + } else if (curve[0] == DCT_Parametric) { if (curve.size() < 8) { curve.clear(); - curve.push_back (DCT_Linear); + curve.push_back(DCT_Linear); return true; } else { // curve[1] to curve[3] must be ordered ascending and distinct @@ -73,12 +72,13 @@ bool sanitizeCurve(std::vector& curve) } } } + return false; } -Curve::Curve () : N(0), ppn(0), x(nullptr), y(nullptr), mc(0.0), mfc(0.0), msc(0.0), mhc(0.0), hashSize(1000 /* has to be initialized to the maximum value */), ypp(nullptr), x1(0.0), y1(0.0), x2(0.0), y2(0.0), x3(0.0), y3(0.0), firstPointIncluded(false), increment(0.0), nbr_points(0) {} +Curve::Curve() : N(0), ppn(0), x(nullptr), y(nullptr), mc(0.0), mfc(0.0), msc(0.0), mhc(0.0), hashSize(1000 /* has to be initialized to the maximum value */), ypp(nullptr), x1(0.0), y1(0.0), x2(0.0), y2(0.0), x3(0.0), y3(0.0), firstPointIncluded(false), increment(0.0), nbr_points(0) {} -void Curve::AddPolygons () +void Curve::AddPolygons() { if (firstPointIncluded) { poly_x.push_back(x1); @@ -93,8 +93,8 @@ void Curve::AddPolygons () double tr2t = tr * 2 * t; // adding a point to the polyline - poly_x.push_back( tr2 * x1 + tr2t * x2 + t2 * x3); - poly_y.push_back( tr2 * y1 + tr2t * y2 + t2 * y3); + poly_x.push_back(tr2 * x1 + tr2t * x2 + t2 * x3); + poly_y.push_back(tr2 * y1 + tr2t * y2 + t2 * y3); } // adding the last point of the sub-curve @@ -102,11 +102,11 @@ void Curve::AddPolygons () poly_y.push_back(y3); } -void Curve::fillDyByDx () +void Curve::fillDyByDx() { dyByDx.resize(poly_x.size() - 1); - for(unsigned int i = 0; i < poly_x.size() - 1; i++) { + for (unsigned int i = 0; i < poly_x.size() - 1; i++) { double dx = poly_x[i + 1] - poly_x[i]; double dy = poly_y[i + 1] - poly_y[i]; dyByDx[i] = dy / dx; @@ -123,7 +123,7 @@ void Curve::fillHash() double milestone = 0.; for (unsigned short i = 0; i < (hashSize + 1);) { - while(poly_x[polyIter] <= milestone) { + while (poly_x[polyIter] <= milestone) { ++polyIter; } @@ -136,7 +136,7 @@ void Curve::fillHash() polyIter = 0; for (unsigned int i = 0; i < hashSize + 1u;) { - while(poly_x[polyIter] < (milestone + increment)) { + while (poly_x[polyIter] < (milestone + increment)) { ++polyIter; } @@ -173,7 +173,7 @@ void Curve::fillHash() * This method return the number of control points of a curve. Not suitable for parametric curves. * @return number of control points of the curve. 0 will be sent back for Parametric curves */ -int Curve::getSize () const +int Curve::getSize() const { return N; } @@ -204,11 +204,11 @@ void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool nee { if (needed) { - for (int i = 0; i <= 0xffff; i += i < 0xffff - skip ? skip : 1 ) { + for (int i = 0; i <= 0xffff; i += i < 0xffff - skip ? skip : 1) { // change to [0,1] range float val = (float)i / 65535.f; // apply custom/parametric/NURBS curve, if any - val = diagCurve->getVal (val); + val = diagCurve->getVal(val); // store result in a temporary array outCurve[i] = val; } @@ -218,8 +218,8 @@ void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool nee float skipmul = 1.f / (float) skip; for (int i = 0; i <= 0x10000 - skip; i += skip) { - for(int j = 1; j < skip; j++) { - outCurve[i + j] = ( outCurve[i] * (skip - j) + outCurve[i + skip] * j ) * skipmul; + for (int j = 1; j < skip; j++) { + outCurve[i + j] = (outCurve[i] * (skip - j) + outCurve[i + skip] * j) * skipmul; } } } @@ -230,7 +230,7 @@ void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool nee } } -void CurveFactory::curveLightBrightColor (const std::vector& curvePoints1, const std::vector& curvePoints2, const std::vector& curvePoints3, +void CurveFactory::curveLightBrightColor(const std::vector& curvePoints1, const std::vector& curvePoints2, const std::vector& curvePoints3, const LUTu & histogram, LUTu & outBeforeCCurveHistogram,//for Luminance const LUTu & histogramC, LUTu & outBeforeCCurveHistogramC,//for chroma ColorAppearance & customColCurve1, ColorAppearance & customColCurve2, ColorAppearance & customColCurve3, int skip) @@ -289,9 +289,9 @@ void CurveFactory::curveLightBrightColor (const std::vector& curvePoints } } -void CurveFactory::curveBW ( const std::vector& curvePointsbw, const std::vector& curvePointsbw2, - const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,//for Luminance - ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip) +void CurveFactory::curveBW(const std::vector& curvePointsbw, const std::vector& curvePointsbw2, + const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,//for Luminance + ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip) { const float gamma_ = Color::sRGBGammaCurve; @@ -319,7 +319,7 @@ void CurveFactory::curveBW ( const std::vector& curvePointsbw, const std if (!curvePointsbw.empty() && curvePointsbw[0] > DCT_Linear && curvePointsbw[0] < DCT_Unchanged) { DiagonalCurve tcurve(curvePointsbw, CURVES_MIN_POLY_POINTS / skip); - if (outBeforeCCurveHistogrambw ) { + if (outBeforeCCurveHistogrambw) { histNeeded = true; } @@ -336,13 +336,13 @@ void CurveFactory::curveBW ( const std::vector& curvePointsbw, const std } // add curve Lab : C=f(L) -void CurveFactory::curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip) +void CurveFactory::curveCL(bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip) { clcutili = false; std::unique_ptr dCurve; if (!clcurvePoints.empty() && clcurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(clcurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(clcurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { clcutili = true; @@ -352,7 +352,7 @@ void CurveFactory::curveCL ( bool & clcutili, const std::vector& clcurve fillCurveArray(dCurve.get(), clCurve, skip, clcutili); } -void CurveFactory::mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram) +void CurveFactory::mapcurve(bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram) { bool needed = false; std::unique_ptr dCurve; @@ -360,7 +360,7 @@ void CurveFactory::mapcurve ( bool & mapcontlutili, const std::vector& m bool histNeeded = false; if (!mapcurvePoints.empty() && mapcurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(mapcurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(mapcurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (outBeforeCurveHistogram) { histNeeded = true; @@ -379,7 +379,7 @@ void CurveFactory::mapcurve ( bool & mapcontlutili, const std::vector& m fillCurveArray(dCurve.get(), mapcurve, skip, needed); } -void CurveFactory::curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram) +void CurveFactory::curveDehaContL(bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram) { bool needed = false; std::unique_ptr dCurve; @@ -387,7 +387,7 @@ void CurveFactory::curveDehaContL ( bool & dehacontlutili, const std::vector(new DiagonalCurve(dehaclcurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(dehaclcurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (outBeforeCurveHistogram) { histNeeded = true; @@ -407,13 +407,13 @@ void CurveFactory::curveDehaContL ( bool & dehacontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve, /*LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip) +void CurveFactory::curveWavContL(bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve, /*LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip) { bool needed = false; std::unique_ptr dCurve; if (!wavclcurvePoints.empty() && wavclcurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(wavclcurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(wavclcurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { needed = true; @@ -425,13 +425,13 @@ void CurveFactory::curveWavContL ( bool & wavcontlutili, const std::vector& curvePoints, LUTf & ToningCurve, int skip) +void CurveFactory::curveToning(const std::vector& curvePoints, LUTf & ToningCurve, int skip) { bool needed = false; std::unique_ptr dCurve; if (!curvePoints.empty() && curvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { needed = true; @@ -441,12 +441,294 @@ void CurveFactory::curveToning ( const std::vector& curvePoints, LUTf & fillCurveArray(dCurve.get(), ToningCurve, skip, needed); } +void CurveFactory::curveLocal(bool & locallutili, const std::vector& curvePoints, LUTf & LocalLCurve, int skip) +{ + bool needed = false; + std::unique_ptr dCurve; + + if (!curvePoints.empty() && curvePoints[0] != 0) { + dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + + if (dCurve && !dCurve->isIdentity()) { + needed = true; + locallutili = true; + } + } + + fillCurveArray(dCurve.get(), LocalLCurve, skip, needed); + //LocalLCurve.dump("wav"); + +} + +void CurveFactory::curveCCLocal(bool & localcutili, const std::vector& curvePoints, LUTf & LocalCCurve, int skip) +{ + bool needed = false; + std::unique_ptr dCurve; + + if (!curvePoints.empty() && curvePoints[0] != 0) { + dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + + if (dCurve && !dCurve->isIdentity()) { + needed = true; + localcutili = true; + } + } + + fillCurveArray(dCurve.get(), LocalCCurve, skip, needed); + //LocalLCurve.dump("wav"); + +} + +void CurveFactory::curveskLocal(bool & localskutili, const std::vector& curvePoints, LUTf & LocalskCurve, int skip) +{ + bool needed = false; + std::unique_ptr dCurve; + +// if (localskutili && !curvePoints.empty() && curvePoints[0] != 0) { + if (!curvePoints.empty() && curvePoints[0] != 0) { + dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + + if (dCurve && !dCurve->isIdentity()) { + needed = true; + localskutili = true; + } + } + + fillCurveArray(dCurve.get(), LocalskCurve, skip, needed); + +} + +void CurveFactory::curveexLocal(bool & localexutili, const std::vector& curvePoints, LUTf & LocalexCurve, int skip) +{ + bool needed = false; + std::unique_ptr dCurve; + +// if (localexutili && !curvePoints.empty() && curvePoints[0] != 0) { + if (!curvePoints.empty() && curvePoints[0] != 0) { + dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + + if (dCurve && !dCurve->isIdentity()) { + needed = true; + localexutili = true; + } + } + + fillCurveArray(dCurve.get(), LocalexCurve, skip, needed); + +} + +void CurveFactory::curvemaskLocal(bool & localmaskutili, const std::vector& curvePoints, LUTf & LocalmaskCurve, int skip) +{ + bool needed = false; + std::unique_ptr dCurve; + +// if (localexutili && !curvePoints.empty() && curvePoints[0] != 0) { + if (!curvePoints.empty() && curvePoints[0] != 0) { + dCurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + + if (dCurve && !dCurve->isIdentity()) { + needed = true; + localmaskutili = true; + } + } + + fillCurveArray(dCurve.get(), LocalmaskCurve, skip, needed); + +} + + + +void CurveFactory::localLCurve(double br, double contr, /*const std::vector& curvePoints,*/ + LUTu & histogram, LUTf & outCurve, + int skip, bool & utili) +{ + + // curve without contrast + LUTf dcurve(65536, 0); + + // clear array that stores histogram valid before applying the custom curve + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // check if brightness curve is needed + if (br > 0.00001 || br < -0.00001) { + utili = true; + + std::vector brightcurvePoints; + brightcurvePoints.resize(9); + brightcurvePoints.at(0) = double (DCT_NURBS); + + brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range + + if (br > 0) { + brightcurvePoints.at(3) = 0.1; // toe point + brightcurvePoints.at(4) = 0.1 + br / 150.0; //value at toe point + + brightcurvePoints.at(5) = 0.7; // shoulder point + brightcurvePoints.at(6) = min(1.0, 0.7 + br / 300.0); //value at shoulder point + } else { + brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point + brightcurvePoints.at(4) = 0.1; // value at toe point + + brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point + brightcurvePoints.at(6) = 0.7; // value at shoulder point + } + + brightcurvePoints.at(7) = 1.; // white point + brightcurvePoints.at(8) = 1.; // value at white point + + DiagonalCurve* brightcurve = new DiagonalCurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip); + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // Applying brightness curve + for (int i = 0; i < 32768; i++) { // L values range up to 32767, higher values are for highlight overflow + + // change to [0,1] range + float val = (float)i / 32767.0; + + // apply brightness curve + val = brightcurve->getVal(val); + + // store result in a temporary array + dcurve[i] = val; + } + + delete brightcurve; + } else { + for (int i = 0; i < 32768; i++) { // L values range up to 32767, higher values are for highlight overflow + // set the identity curve in the temporary array + dcurve[i] = (float)i / 32767.0; + } + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // check if contrast curve is needed + if (contr > 0.00001 || contr < -0.00001) { + utili = true; + + DiagonalCurve* contrastcurve = NULL; + + // compute mean luminance of the image with the curve applied + int sum = 0; + float avg = 0; + + //float sqavg = 0; + for (int i = 0; i < 32768; i++) { + avg += dcurve[i] * histogram[i]; + //sqavg += dcurve[i]*dcurve[i] * histogram[i]; + sum += histogram[i]; + } + + if (sum) { + avg /= sum; + //sqavg /= sum; + //float stddev = sqrt(sqavg-avg*avg); + // printf("avg=%f\n",avg); + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + std::vector contrastcurvePoints; + contrastcurvePoints.resize(9); + contrastcurvePoints.at(0) = double (DCT_NURBS); + + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range + + contrastcurvePoints.at(3) = avg - avg * (0.6 - contr / 250.0); // toe point + contrastcurvePoints.at(4) = avg - avg * (0.6 + contr / 250.0); // value at toe point + + contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - contr / 250.0); // shoulder point + contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + contr / 250.0); // value at shoulder point + + contrastcurvePoints.at(7) = 1.; // white point + contrastcurvePoints.at(8) = 1.; // value at white point + + contrastcurve = new DiagonalCurve(contrastcurvePoints, CURVES_MIN_POLY_POINTS / skip); + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + } else { + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // sum has an invalid value (next to 0, producing a division by zero, so we create a fake contrast curve, producing a white image + std::vector contrastcurvePoints; + contrastcurvePoints.resize(5); + contrastcurvePoints.at(0) = double (DCT_NURBS); + + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range + + contrastcurvePoints.at(3) = 1.; // white point + contrastcurvePoints.at(4) = 1.; // value at white point + + contrastcurve = new DiagonalCurve(contrastcurvePoints, CURVES_MIN_POLY_POINTS / skip); + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + } + + // apply contrast enhancement + for (int i = 0; i < 32768; i++) { + dcurve[i] = contrastcurve->getVal(dcurve[i]); + } + + delete contrastcurve; + } + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // create a curve if needed + /* DiagonalCurve* tcurve = NULL; + bool histNeeded = false; + if (!curvePoints.empty() && curvePoints[0]!=0) { + tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip); + } + if (tcurve && tcurve->isIdentity()) { + delete tcurve; + tcurve = NULL; + } + + if (tcurve) { + utili=true;//if active + + // L values go up to 32767, last stop is for highlight overflow + for (int i=0; i<32768; i++) { + float val; + // apply custom/parametric/NURBS curve, if any + val = tcurve->getVal (dcurve[i]); + + outCurve[i] = (32767.0 * val); + } + } + else + */ + { + // Skip the slow getval method if no curve is used (or an identity curve) + // L values go up to 32767, last stop is for highlight overflow + for (int i = 0; i < 32768; i++) { + outCurve[i] = 32767.0 * dcurve[i]; + } + } + + for (int i = 32768; i < 65536; i++) { + outCurve[i] = (float)i; + } + + // if (tcurve) + // delete tcurve; + +} + + + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutili, bool & cclutili, - 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) +void CurveFactory::complexsgnCurve(bool & autili, bool & butili, bool & ccutili, bool & cclutili, + 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) { autili = butili = ccutili = cclutili = false; @@ -454,7 +736,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil // create a curve if needed if (!acurvePoints.empty() && acurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(acurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(acurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { autili = true; @@ -468,7 +750,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil //----------------------------------------------------- if (!bcurvePoints.empty() && bcurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(bcurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(bcurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { butili = true; @@ -482,7 +764,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil //----------------------------------------------- if (!cccurvePoints.empty() && cccurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(cccurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(cccurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { ccutili = true; @@ -496,7 +778,7 @@ void CurveFactory::complexsgnCurve (bool & autili, bool & butili, bool & ccutil //---------------------------- if (!lccurvePoints.empty() && lccurvePoints[0] != 0) { - dCurve = std::unique_ptr(new DiagonalCurve(lccurvePoints, CURVES_MIN_POLY_POINTS / skip)); + dCurve = std::unique_ptr (new DiagonalCurve(lccurvePoints, CURVES_MIN_POLY_POINTS / skip)); if (dCurve && !dCurve->isIdentity()) { cclutili = true; @@ -522,13 +804,14 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou // the curve shapes are defined in sRGB gamma, but the output curves will operate on linear floating point data, // hence we do both forward and inverse gamma conversions here. const float gamma_ = Color::sRGBGammaCurve; - const float start = expf(gamma_ * logf( -0.055 / ((1.0 / gamma_ - 1.0) * 1.055 ))); - const float slope = 1.055 * powf (start, 1.0 / gamma_ - 1) - 0.055 / start; + const float start = expf(gamma_ * logf(-0.055 / ((1.0 / gamma_ - 1.0) * 1.055))); + const float slope = 1.055 * powf(start, 1.0 / gamma_ - 1) - 0.055 / start; const float mul = 1.055; const float add = 0.055; + // a: slope of the curve, black: starting point at the x axis - const float a = powf (2.0, ecomp); + const float a = powf(2.0, ecomp); // clear array that stores histogram valid before applying the custom curve outBeforeCCurveHistogram.clear(); @@ -548,14 +831,14 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou brightcurvePoints[1] = 0.; //black point. Value in [0 ; 1] range brightcurvePoints[2] = 0.; //black point. Value in [0 ; 1] range - if(br > 0) { + if (br > 0) { brightcurvePoints[3] = 0.1; //toe point brightcurvePoints[4] = 0.1 + br / 150.0; //value at toe point brightcurvePoints[5] = 0.7; //shoulder point - brightcurvePoints[6] = min(1.0, 0.7 + br / 300.0); //value at shoulder point + brightcurvePoints[6] = min(1.0, 0.7 + br / 300.0); //value at shoulder point } else { - brightcurvePoints[3] = max(0.0, 0.1 - br / 150.0); //toe point + brightcurvePoints[3] = max(0.0, 0.1 - br / 150.0); //toe point brightcurvePoints[4] = 0.1; //value at toe point brightcurvePoints[5] = 0.7 - br / 300.0; //shoulder point @@ -565,12 +848,12 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou brightcurvePoints[7] = 1.; // white point brightcurvePoints[8] = 1.; // value at white point - brightcurve = std::unique_ptr(new DiagonalCurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip)); + brightcurve = std::unique_ptr (new DiagonalCurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip)); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details + hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details float exp_scale = a; float scale = 65536.0; float comp = (max(0.0, ecomp) + 1.0) * hlcompr / 100.0; @@ -586,11 +869,11 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou #ifdef __SSE2__ int i = shoulder + 1; - if(i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference + if (i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference // change to [0,1] range float val = (float)i - shoulder; float R = val * comp / (scalemshoulder); - hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision + hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision i++; } @@ -615,7 +898,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou for (int i = shoulder + 1; i < 0x10000; i++) { // change to [0,1] range - hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision + hlCurve[i] = xlog(1.0 + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision R += increment; } @@ -629,7 +912,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou //%%%%%%%%%%%%%%%%%%%%%%%%%% // change to [0,1] range - shCurve.setClip(LUT_CLIP_ABOVE); // used LUT_CLIP_ABOVE, because the curve converges to 1.0 at the upper end and we don't want to exceed this value. + shCurve.setClip(LUT_CLIP_ABOVE); // used LUT_CLIP_ABOVE, because the curve converges to 1.0 at the upper end and we don't want to exceed this value. if (black == 0.0) { shCurve.makeConstant(1.f); } else { @@ -709,7 +992,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // apply contrast enhancement for (int i = 0; i <= 0xffff; i++) { - dcurve[i] = contrastcurve.getVal (dcurve[i]); + dcurve[i] = contrastcurve.getVal(dcurve[i]); } } @@ -726,7 +1009,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou customToneCurve2.Set(tcurve, gamma_); } - if (outBeforeCCurveHistogram ) { + if (outBeforeCCurveHistogram) { histNeeded = true; } } @@ -762,7 +1045,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou for (int i = 0; i <= 0xffff; i += 4) { vfloat valv = LVFU(dcurve[i]); - valv = igamma (valv, gamma_v, startv, slopev, mulv, addv); + valv = igamma(valv, gamma_v, startv, slopev, mulv, addv); STVFU(outCurve[i], c65535v * valv); } @@ -770,7 +1053,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou for (int i = 0; i <= 0xffff; i++) { float val = dcurve[i]; - val = igamma (val, gamma_, start, slope, mul, add); + val = igamma(val, gamma_, start, slope, mul, add); outCurve[i] = (65535.f * val); } @@ -788,16 +1071,342 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou } +void CurveFactory::Curvelocalhl(double ecomp, double hlcompr, double hlcomprthresh, LUTf & hlCurve) +{ + + // a: slope of the curve + const float a = powf(2.0f, ecomp); + + + + + hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details + float exp_scale = a; + float maxran = 65536.f; + float scale = maxran; + float comp = (max(0.0, ecomp) + 1.0) * hlcompr / 100.0; + float shoulder = ((scale / max(1.0f, exp_scale)) * (hlcomprthresh / 200.0)) + 0.1; + + if (comp <= 0.0f) { + hlCurve.makeConstant(exp_scale); + } else { + hlCurve.makeConstant(exp_scale, shoulder + 1); + + float scalemshoulder = scale - shoulder; + +#ifdef __SSE2__ + int i = shoulder + 1; + + if (i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference + // change to [0,1] range + float val = (float)i - shoulder; + float R = val * comp / (scalemshoulder); + hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision + i++; + } + + vdouble onev = _mm_set1_pd(1.0); + vdouble Rv = _mm_set_pd((i + 1 - shoulder) * (double)comp / scalemshoulder, (i - shoulder) * (double)comp / scalemshoulder); + vdouble incrementv = _mm_set1_pd(2.0 * comp / scalemshoulder); + vdouble exp_scalev = _mm_set1_pd(exp_scale); + + // for (; i < 0x10000; i += 2) { + for (; i < maxran; i += 2) { + // change to [0,1] range + vdouble resultv = xlog(onev + Rv * exp_scalev) / Rv; + vfloat resultfv = _mm_cvtpd_ps(resultv); + _mm_store_ss(&hlCurve[i], resultfv); + resultfv = PERMUTEPS(resultfv, _MM_SHUFFLE(1, 1, 1, 1)); + _mm_store_ss(&hlCurve[i + 1], resultfv); + Rv += incrementv; + } + +#else + float R = comp / scalemshoulder; + float increment = R; + + // for (int i = shoulder + 1; i < 0x10000; i++) { + for (int i = shoulder + 1; i < maxran; i++) { + // change to [0,1] range + hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision + R += increment; + } + +#endif + + } +} + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void CurveFactory::complexCurvelocal(double ecomp, double black, double hlcompr, double hlcomprthresh, + double shcompr, double br, double cont, double lumare, + LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTf & lightCurveloc, float avg, + int skip) +{ + + const float gamma_ = 2.22; //BT 709 + const float start = expf(gamma_ * logf(-0.0954f / ((1.0f / gamma_ - 1.0f) * 1.0954f))); + const float slope = 1.0954f * powf(start, 1.0f / gamma_ - 1.f) - 0.0954f / start; + const float mul = 1.0954f; + const float add = 0.0954f; + float maxran = 65536.f; //65536 + +// ecomp /= 100.; + + // check if brightness curve is needed + if (br > 0.00001 || br < -0.00001) { + // utili = true; + if(br > 0) { + br /= 4.f;//to avoid artifacts in some cases + } + std::vector brightcurvePoints; + brightcurvePoints.resize(9); + brightcurvePoints.at(0) = double (DCT_NURBS); + + brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range + + if (br > 0) { + brightcurvePoints.at(3) = 0.2; // toe point + brightcurvePoints.at(4) = 0.2 + br / 250.0; //value at toe point + + brightcurvePoints.at(5) = 0.6; // shoulder point + brightcurvePoints.at(6) = min(1.0, 0.6 + br / 200.0); //value at shoulder point + } else { + brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point + brightcurvePoints.at(4) = 0.1; // value at toe point + + brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point + brightcurvePoints.at(6) = 0.7; // value at shoulder point + } + + brightcurvePoints.at(7) = 1.; // white point + brightcurvePoints.at(8) = 1.; // value at white point + + DiagonalCurve brightcurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip); + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + // Applying brightness curve + for (int i = 0; i < 32768; i++) { // L values range up to 32767, higher values are for highlight overflow + + // change to [0,1] range + float val = (float)i / 32767.0; + + // apply brightness curve + val = brightcurve.getVal(val); + + // store result in a temporary array + lightCurveloc[i] = val; + } + + + + } else { + lightCurveloc.makeIdentity(32767.f); + } + + // check if contrast curve is needed + if (cont > 0.00001 || cont < -0.00001) { + + + int k = avg * 32768; + avg = lightCurveloc[k]; + // printf("avg=%f lumaref=%f\n", avg, lumare/100.f); + std::vector contrastcurvePoints; + bool lumm = true; + + if (lumm) { + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + contrastcurvePoints.resize(9); + contrastcurvePoints.at(0) = double (DCT_NURBS); + + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range + + contrastcurvePoints.at(3) = avg - avg * (0.6 - cont / 250.0); // toe point + contrastcurvePoints.at(4) = avg - avg * (0.6 + cont / 250.0); // value at toe point + + contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - cont / 250.0); // shoulder point + contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + cont / 250.0); // value at shoulder point + + contrastcurvePoints.at(7) = 1.; // white point + contrastcurvePoints.at(8) = 1.; // value at white point + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + } else { + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // sum has an invalid value (next to 0, producing a division by zero, so we create a fake contrast curve, producing a white image + contrastcurvePoints.resize(5); + contrastcurvePoints.at(0) = double (DCT_NURBS); + + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range + + contrastcurvePoints.at(3) = 1.; // white point + contrastcurvePoints.at(4) = 1.; // value at white point + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + } + + DiagonalCurve contrastcurve(contrastcurvePoints, CURVES_MIN_POLY_POINTS / skip); + + // apply contrast enhancement + for (int i = 0; i < 32768; i++) { + lightCurveloc[i] = contrastcurve.getVal(lightCurveloc[i]); + } + + } + + lightCurveloc *= 32767.f; + + for (int i = 32768; i < 32770; i++) { // set last two elements of lut to 32768 and 32769 to allow linear interpolation + lightCurveloc[i] = (float)i; + } + + + // a: slope of the curve, black: starting point at the x axis + const float a = powf(2.0f, ecomp); + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + hlCurve.setClip(LUT_CLIP_BELOW); // used LUT_CLIP_BELOW, because we want to have a baseline of 2^expcomp in this curve. If we don't clip the lut we get wrong values, see Issue 2621 #14 for details + float exp_scale = a; +// float scale = 65536.0; + float scale = maxran; + float comp = (max(0.0, ecomp) + 1.0) * hlcompr / 100.0; + float shoulder = ((scale / max(1.0f, exp_scale)) * (hlcomprthresh / 200.0)) + 0.1; + + if (comp <= 0.0f) { + hlCurve.makeConstant(exp_scale); + } else { + hlCurve.makeConstant(exp_scale, shoulder + 1); + + float scalemshoulder = scale - shoulder; + +#ifdef __SSE2__ + int i = shoulder + 1; + + if (i & 1) { // original formula, slower than optimized formulas below but only used once or none, so I let it as is for reference + // change to [0,1] range + float val = (float)i - shoulder; + float R = val * comp / (scalemshoulder); + hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision + i++; + } + + vdouble onev = _mm_set1_pd(1.0); + vdouble Rv = _mm_set_pd((i + 1 - shoulder) * (double)comp / scalemshoulder, (i - shoulder) * (double)comp / scalemshoulder); + vdouble incrementv = _mm_set1_pd(2.0 * comp / scalemshoulder); + vdouble exp_scalev = _mm_set1_pd(exp_scale); + + // for (; i < 0x10000; i += 2) { + for (; i < maxran; i += 2) { + // change to [0,1] range + vdouble resultv = xlog(onev + Rv * exp_scalev) / Rv; + vfloat resultfv = _mm_cvtpd_ps(resultv); + _mm_store_ss(&hlCurve[i], resultfv); + resultfv = PERMUTEPS(resultfv, _MM_SHUFFLE(1, 1, 1, 1)); + _mm_store_ss(&hlCurve[i + 1], resultfv); + Rv += incrementv; + } + +#else + float R = comp / scalemshoulder; + float increment = R; + + // for (int i = shoulder + 1; i < 0x10000; i++) { + for (int i = shoulder + 1; i < maxran; i++) { + // change to [0,1] range + hlCurve[i] = xlog(1.0f + R * exp_scale) / R; // don't use xlogf or 1.f here. Leads to errors caused by too low precision + R += increment; + } + +#endif + + } + + + // curve without contrast + LUTf dcurve(maxran); + + //%%%%%%%%%%%%%%%%%%%%%%%%%% + // change to [0,1] range + shCurve.setClip(LUT_CLIP_ABOVE); // used LUT_CLIP_ABOVE, because the curve converges to 1.0 at the upper end and we don't want to exceed this value. + if (black == 0.0) { + shCurve.makeConstant(1.f); + } else { + const float val = 1.f / (maxran - 1.f); + shCurve[0] = simplebasecurve(val, black, 0.015 * shcompr) / val; + } + + + + // gamma correction + + float val = Color::gammatab_bt709[0] / maxran; + // store result in a temporary array + dcurve[0] = LIM01(val); + + for (int i = 1; i < maxran; i++) { + float val = i / (maxran - 1.f); + +// float val2 = simplebasecurve(val, black, 0.015 * shcompr); +// shCurve[i] = val2 / val; + if (black != 0.0) { + const float val = i / 65535.f; + shCurve[i] = simplebasecurve(val, black, 0.015 * shcompr) / val; + } + + // gamma correction + val = Color::gammatab_bt709[i] / maxran; + // store result in a temporary array + dcurve[i] = val; + } + + +#ifdef __SSE2__ + vfloat gamma_v = F2V(gamma_); + vfloat startv = F2V(start); + vfloat slopev = F2V(slope); + vfloat mulv = F2V(mul); + vfloat addv = F2V(add); + // vfloat c65535v = F2V (65535.f); + vfloat c65535v = F2V(maxran - 1.f); + + for (int i = 0; i <= (maxran - 1.f); i += 4) { + vfloat valv = LVFU(dcurve[i]); + valv = igamma(valv, gamma_v, startv, slopev, mulv, addv); + STVFU(outCurve[i], c65535v * valv); + } + +#else + + for (int i = 0; i <= (maxran - 1.f); i++) { + float val = dcurve[i]; + val = igamma(val, gamma_, start, slope, mul, add); + outCurve[i] = ((maxran - 1.) * val); + } + +#endif + +} + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void CurveFactory::complexLCurve (double br, double contr, const std::vector& curvePoints, - const LUTu & histogram, LUTf & outCurve, - LUTu & outBeforeCCurveHistogram, int skip, bool & utili) +void CurveFactory::complexLCurve(double br, double contr, const std::vector& curvePoints, + const LUTu & histogram, LUTf & outCurve, + LUTu & outBeforeCCurveHistogram, int skip, bool & utili) { utili = false; + // clear array that stores histogram valid before applying the custom curve if (outBeforeCCurveHistogram) { outBeforeCCurveHistogram.clear(); @@ -813,27 +1422,27 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector brightcurvePoints; brightcurvePoints.resize(9); - brightcurvePoints.at(0) = double(DCT_NURBS); + brightcurvePoints.at(0) = double (DCT_NURBS); - brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range - brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range + brightcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + brightcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range if (br > 0) { - brightcurvePoints.at(3) = 0.1; // toe point - brightcurvePoints.at(4) = 0.1 + br / 150.0; //value at toe point + brightcurvePoints.at(3) = 0.1; // toe point + brightcurvePoints.at(4) = 0.1 + br / 150.0; //value at toe point - brightcurvePoints.at(5) = 0.7; // shoulder point - brightcurvePoints.at(6) = min(1.0, 0.7 + br / 300.0); //value at shoulder point + brightcurvePoints.at(5) = 0.7; // shoulder point + brightcurvePoints.at(6) = min(1.0, 0.7 + br / 300.0); //value at shoulder point } else { - brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point - brightcurvePoints.at(4) = 0.1; // value at toe point + brightcurvePoints.at(3) = 0.1 - br / 150.0; // toe point + brightcurvePoints.at(4) = 0.1; // value at toe point - brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point - brightcurvePoints.at(6) = 0.7; // value at shoulder point + brightcurvePoints.at(5) = min(1.0, 0.7 - br / 300.0); // shoulder point + brightcurvePoints.at(6) = 0.7; // value at shoulder point } - brightcurvePoints.at(7) = 1.; // white point - brightcurvePoints.at(8) = 1.; // value at white point + brightcurvePoints.at(7) = 1.; // white point + brightcurvePoints.at(8) = 1.; // value at white point DiagonalCurve brightcurve(brightcurvePoints, CURVES_MIN_POLY_POINTS / skip); //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -845,7 +1454,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector(val); @@ -874,37 +1483,37 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector contrastcurvePoints; - if(sum) { + if (sum) { avg /= sum; //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% contrastcurvePoints.resize(9); - contrastcurvePoints.at(0) = double(DCT_NURBS); + contrastcurvePoints.at(0) = double (DCT_NURBS); - contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range - contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 0.; // black point. Value in [0 ; 1] range - contrastcurvePoints.at(3) = avg - avg * (0.6 - contr / 250.0); // toe point - contrastcurvePoints.at(4) = avg - avg * (0.6 + contr / 250.0); // value at toe point + contrastcurvePoints.at(3) = avg - avg * (0.6 - contr / 250.0); // toe point + contrastcurvePoints.at(4) = avg - avg * (0.6 + contr / 250.0); // value at toe point - contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - contr / 250.0); // shoulder point - contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + contr / 250.0); // value at shoulder point + contrastcurvePoints.at(5) = avg + (1 - avg) * (0.6 - contr / 250.0); // shoulder point + contrastcurvePoints.at(6) = avg + (1 - avg) * (0.6 + contr / 250.0); // value at shoulder point - contrastcurvePoints.at(7) = 1.; // white point - contrastcurvePoints.at(8) = 1.; // value at white point + contrastcurvePoints.at(7) = 1.; // white point + contrastcurvePoints.at(8) = 1.; // value at white point //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% } else { //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // sum has an invalid value (next to 0, producing a division by zero, so we create a fake contrast curve, producing a white image contrastcurvePoints.resize(5); - contrastcurvePoints.at(0) = double(DCT_NURBS); + contrastcurvePoints.at(0) = double (DCT_NURBS); - contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range - contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(1) = 0.; // black point. Value in [0 ; 1] range + contrastcurvePoints.at(2) = 1.; // black point. Value in [0 ; 1] range - contrastcurvePoints.at(3) = 1.; // white point - contrastcurvePoints.at(4) = 1.; // value at white point + contrastcurvePoints.at(3) = 1.; // white point + contrastcurvePoints.at(4) = 1.; // value at white point //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% } @@ -913,7 +1522,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector(new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS / skip)); + tcurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); if (outBeforeCCurveHistogram) { histNeeded = true; @@ -950,7 +1559,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vectorgetVal (outCurve[i]); + val = tcurve->getVal(outCurve[i]); outCurve[i] = (32767.f * val); } @@ -958,7 +1567,7 @@ void CurveFactory::complexLCurve (double br, double contr, const std::vector& curvePoints, LUTf & outCurve, int skip) +void CurveFactory::RGBCurve(const std::vector& curvePoints, LUTf & outCurve, int skip) { // create a curve if needed std::unique_ptr tcurve; if (!curvePoints.empty() && curvePoints[0] != 0) { - tcurve = std::unique_ptr(new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); + tcurve = std::unique_ptr (new DiagonalCurve(curvePoints, CURVES_MIN_POLY_POINTS / skip)); } if (tcurve && tcurve->isIdentity()) { @@ -1008,6 +1617,1273 @@ void CurveFactory::RGBCurve (const std::vector& curvePoints, LUTf & outC } +LocretigainCurverab::LocretigainCurverab() : sum(0.f) {}; + +void LocretigainCurverab::Reset() +{ + lutLocretigainCurverab.reset(); + sum = 0.f; +} + +void LocretigainCurverab::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocretigainCurverab(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocretigainCurverab[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocretigainCurverab[i] < 0.02f) { + lutLocretigainCurverab[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocretigainCurverab[i]; + } + + //lutLocCurve.dump("wav"); +} + +void LocretigainCurverab::Set(const std::vector &curvePoints) +{ + + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + tcurve.setIdentityValue(0.); + Set(tcurve); + } else { + Reset(); + } +} +LocHHmaskblCurve::LocHHmaskblCurve() : sum(0.f) {}; + +void LocHHmaskblCurve::Reset() +{ + lutLocHHmaskblCurve.reset(); + sum = 0.f; +} + + +void LocHHmaskblCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmaskblCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmaskblCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmaskblCurve[i] < 0.02f) { + lutLocHHmaskblCurve[i] = 0.02f; + } + + sum += lutLocHHmaskblCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + +void LocHHmaskblCurve::Set(const std::vector &curvePoints, bool & lhmasblutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmasblutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocLLmaskblCurve::LocLLmaskblCurve() : sum(0.f) {}; + +void LocLLmaskblCurve::Reset() +{ + lutLocLLmaskblCurve.reset(); + sum = 0.f; +} + +void LocLLmaskblCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmaskblCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmaskblCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmaskblCurve[i] < 0.02f) { + lutLocLLmaskblCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmaskblCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmaskblCurve::Set(const std::vector &curvePoints, bool & llmasblutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmasblutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocCCmaskblCurve::LocCCmaskblCurve() : sum(0.f) {}; + +void LocCCmaskblCurve::Reset() +{ + lutLocCCmaskblCurve.reset(); + sum = 0.f; +} + +void LocCCmaskblCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmaskblCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmaskblCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmaskblCurve[i] < 0.02f) { + lutLocCCmaskblCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmaskblCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmaskblCurve::Set(const std::vector &curvePoints, bool & lcmasblutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmasblutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + +LocHHmasktmCurve::LocHHmasktmCurve() : sum(0.f) {}; + +void LocHHmasktmCurve::Reset() +{ + lutLocHHmasktmCurve.reset(); + sum = 0.f; +} + + +void LocHHmasktmCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmasktmCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmasktmCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmasktmCurve[i] < 0.02f) { + lutLocHHmasktmCurve[i] = 0.02f; + } + + sum += lutLocHHmasktmCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + +void LocHHmasktmCurve::Set(const std::vector &curvePoints, bool & lhmastmutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmastmutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocLLmasktmCurve::LocLLmasktmCurve() : sum(0.f) {}; + +void LocLLmasktmCurve::Reset() +{ + lutLocLLmasktmCurve.reset(); + sum = 0.f; +} + +void LocLLmasktmCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmasktmCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmasktmCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmasktmCurve[i] < 0.02f) { + lutLocLLmasktmCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmasktmCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmasktmCurve::Set(const std::vector &curvePoints, bool & llmastmutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmastmutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocCCmasktmCurve::LocCCmasktmCurve() : sum(0.f) {}; + +void LocCCmasktmCurve::Reset() +{ + lutLocCCmasktmCurve.reset(); + sum = 0.f; +} + +void LocCCmasktmCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmasktmCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmasktmCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmasktmCurve[i] < 0.02f) { + lutLocCCmasktmCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmasktmCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmasktmCurve::Set(const std::vector &curvePoints, bool & lcmastmutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmastmutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + +LocHHmaskretiCurve::LocHHmaskretiCurve() : sum(0.f) {}; + +void LocHHmaskretiCurve::Reset() +{ + lutLocHHmaskretiCurve.reset(); + sum = 0.f; +} + + +void LocHHmaskretiCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmaskretiCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmaskretiCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmaskretiCurve[i] < 0.02f) { + lutLocHHmaskretiCurve[i] = 0.02f; + } + + sum += lutLocHHmaskretiCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + +void LocHHmaskretiCurve::Set(const std::vector &curvePoints, bool & lhmasretiutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmasretiutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocLLmaskretiCurve::LocLLmaskretiCurve() : sum(0.f) {}; + +void LocLLmaskretiCurve::Reset() +{ + lutLocLLmaskretiCurve.reset(); + sum = 0.f; +} + +void LocLLmaskretiCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmaskretiCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmaskretiCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmaskretiCurve[i] < 0.02f) { + lutLocLLmaskretiCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmaskretiCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmaskretiCurve::Set(const std::vector &curvePoints, bool & llmasretiutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmasretiutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocCCmaskretiCurve::LocCCmaskretiCurve() : sum(0.f) {}; + +void LocCCmaskretiCurve::Reset() +{ + lutLocCCmaskretiCurve.reset(); + sum = 0.f; +} + +void LocCCmaskretiCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmaskretiCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmaskretiCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmaskretiCurve[i] < 0.02f) { + lutLocCCmaskretiCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmaskretiCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmaskretiCurve::Set(const std::vector &curvePoints, bool & lcmasretiutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmasretiutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + + + + + +LocHHmaskcbCurve::LocHHmaskcbCurve() : sum(0.f) {}; + +void LocHHmaskcbCurve::Reset() +{ + lutLocHHmaskcbCurve.reset(); + sum = 0.f; +} + + +void LocHHmaskcbCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmaskcbCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmaskcbCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmaskcbCurve[i] < 0.02f) { + lutLocHHmaskcbCurve[i] = 0.02f; + } + + sum += lutLocHHmaskcbCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + +void LocHHmaskcbCurve::Set(const std::vector &curvePoints, bool & lhmascbutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmascbutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocLLmaskcbCurve::LocLLmaskcbCurve() : sum(0.f) {}; + +void LocLLmaskcbCurve::Reset() +{ + lutLocLLmaskcbCurve.reset(); + sum = 0.f; +} + +void LocLLmaskcbCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmaskcbCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmaskcbCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmaskcbCurve[i] < 0.02f) { + lutLocLLmaskcbCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmaskcbCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmaskcbCurve::Set(const std::vector &curvePoints, bool & llmascbutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmascbutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocCCmaskcbCurve::LocCCmaskcbCurve() : sum(0.f) {}; + +void LocCCmaskcbCurve::Reset() +{ + lutLocCCmaskcbCurve.reset(); + sum = 0.f; +} + +void LocCCmaskcbCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmaskcbCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmaskcbCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmaskcbCurve[i] < 0.02f) { + lutLocCCmaskcbCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmaskcbCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmaskcbCurve::Set(const std::vector &curvePoints, bool & lcmascbutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmascbutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + +LocHHmaskSHCurve::LocHHmaskSHCurve() : sum(0.f) {}; + +void LocHHmaskSHCurve::Reset() +{ + lutLocHHmaskSHCurve.reset(); + sum = 0.f; +} + + +void LocHHmaskSHCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmaskSHCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmaskSHCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmaskSHCurve[i] < 0.02f) { + lutLocHHmaskSHCurve[i] = 0.02f; + } + + sum += lutLocHHmaskSHCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocHHmaskSHCurve::Set(const std::vector &curvePoints, bool & lhmasSHutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmasSHutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + +LocLLmaskSHCurve::LocLLmaskSHCurve() : sum(0.f) {}; + +void LocLLmaskSHCurve::Reset() +{ + lutLocLLmaskSHCurve.reset(); + sum = 0.f; +} + +void LocLLmaskSHCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmaskSHCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmaskSHCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmaskSHCurve[i] < 0.02f) { + lutLocLLmaskSHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmaskSHCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmaskSHCurve::Set(const std::vector &curvePoints, bool & llmasSHutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmasSHutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + +LocCCmaskSHCurve::LocCCmaskSHCurve() : sum(0.f) {}; + +void LocCCmaskSHCurve::Reset() +{ + lutLocCCmaskSHCurve.reset(); + sum = 0.f; +} + +void LocCCmaskSHCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmaskSHCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmaskSHCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmaskSHCurve[i] < 0.02f) { + lutLocCCmaskSHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmaskSHCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmaskSHCurve::Set(const std::vector &curvePoints, bool & lcmasSHutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmasSHutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + + +LocHHmaskexpCurve::LocHHmaskexpCurve() : sum(0.f) {}; + +void LocHHmaskexpCurve::Reset() +{ + lutLocHHmaskexpCurve.reset(); + sum = 0.f; +} + + +void LocHHmaskexpCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmaskexpCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmaskexpCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmaskexpCurve[i] < 0.02f) { + lutLocHHmaskexpCurve[i] = 0.02f; + } + + sum += lutLocHHmaskexpCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocHHmaskexpCurve::Set(const std::vector &curvePoints, bool & lhmasexputili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmasexputili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + +LocLLmaskexpCurve::LocLLmaskexpCurve() : sum(0.f) {}; + +void LocLLmaskexpCurve::Reset() +{ + lutLocLLmaskexpCurve.reset(); + sum = 0.f; +} + +void LocLLmaskexpCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmaskexpCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmaskexpCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmaskexpCurve[i] < 0.02f) { + lutLocLLmaskexpCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmaskexpCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmaskexpCurve::Set(const std::vector &curvePoints, bool & llmasexputili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmasexputili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + +LocCCmaskexpCurve::LocCCmaskexpCurve() : sum(0.f) {}; + +void LocCCmaskexpCurve::Reset() +{ + lutLocCCmaskexpCurve.reset(); + sum = 0.f; +} + +void LocCCmaskexpCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmaskexpCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmaskexpCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmaskexpCurve[i] < 0.02f) { + lutLocCCmaskexpCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmaskexpCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmaskexpCurve::Set(const std::vector &curvePoints, bool & lcmasexputili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmasexputili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocHHmaskCurve::LocHHmaskCurve() : sum(0.f) {}; + +void LocHHmaskCurve::Reset() +{ + lutLocHHmaskCurve.reset(); + sum = 0.f; +} + + +void LocHHmaskCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHmaskCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHmaskCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHmaskCurve[i] < 0.02f) { + lutLocHHmaskCurve[i] = 0.02f; + } + + sum += lutLocHHmaskCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocHHmaskCurve::Set(const std::vector &curvePoints, bool & lhmasutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lhmasutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + +LocCCmaskCurve::LocCCmaskCurve() : sum(0.f) {}; + +void LocCCmaskCurve::Reset() +{ + lutLocCCmaskCurve.reset(); + sum = 0.f; +} + + +void LocCCmaskCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocCCmaskCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocCCmaskCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocCCmaskCurve[i] < 0.02f) { + lutLocCCmaskCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocCCmaskCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocCCmaskCurve::Set(const std::vector &curvePoints, bool & lcmasutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + lcmasutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + +LocLLmaskCurve::LocLLmaskCurve() : sum(0.f) {}; + +void LocLLmaskCurve::Reset() +{ + lutLocLLmaskCurve.reset(); + sum = 0.f; +} + +void LocLLmaskCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLLmaskCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLLmaskCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLLmaskCurve[i] < 0.02f) { + lutLocLLmaskCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLLmaskCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocLLmaskCurve::Set(const std::vector &curvePoints, bool & llmasutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + llmasutili = true; + Set(ttcurve); + } else { + Reset(); + } +} + + + + +LocHHCurve::LocHHCurve() : sum(0.f) {}; + +void LocHHCurve::Reset() +{ + lutLocHHCurve.reset(); + sum = 0.f; +} +void LocHHCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocHHCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocHHCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocHHCurve[i] < 0.02f) { + lutLocHHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocHHCurve[i]; + } + + //lutLocHHCurve.dump("wav"); +} + + + +void LocHHCurve::Set(const std::vector &curvePoints, bool &HHutili) +{ + // if (HHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve ttcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + ttcurve.setIdentityValue(0.); + Set(ttcurve); + HHutili = true; + } else { + Reset(); + } +} + + +LocLHCurve::LocLHCurve() : sum(0.f) {}; + +void LocLHCurve::Reset() +{ + lutLocLHCurve.reset(); + sum = 0.f; +} + +void LocLHCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocLHCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocLHCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocLHCurve[i] < 0.02f) { + lutLocLHCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocLHCurve[i]; + } + + //lutLocCurve.dump("wav"); +} + + + + +void LocLHCurve::Set(const std::vector &curvePoints, bool &LHutili) +{ + + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { +// if (LHutili && !curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + tcurve.setIdentityValue(0.); + Set(tcurve); + LHutili = true; + } else { + Reset(); + } +} + +LocwavCurve::LocwavCurve() : sum(0.f) {}; + +void LocwavCurve::Reset() +{ + lutLocwavCurve.reset(); + sum = 0.f; +} + +void LocwavCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocwavCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocwavCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocwavCurve[i] < 0.02f) { + lutLocwavCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocwavCurve[i]; + } + + //lutLocCurve.dump("wav"); +} +void LocwavCurve::Set(const std::vector &curvePoints, bool & lcwavutili) +{ + + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + tcurve.setIdentityValue(0.); + lcwavutili = true; + Set(tcurve); + } else { + Reset(); + } +} + +LocretitransCurve::LocretitransCurve() : sum(0.f) {}; + +void LocretitransCurve::Reset() +{ + lutLocretitransCurve.reset(); + sum = 0.f; +} + +void LocretitransCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocretitransCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocretitransCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocretitransCurve[i] < 0.02f) { + lutLocretitransCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocretitransCurve[i]; + } + + //lutLocCurve.dump("wav"); +} +void LocretitransCurve::Set(const std::vector &curvePoints) +{ + + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + tcurve.setIdentityValue(0.); + Set(tcurve); + } else { + Reset(); + } +} + + +LocretigainCurve::LocretigainCurve() : sum(0.f) {}; + +void LocretigainCurve::Reset() +{ + lutLocretigainCurve.reset(); + sum = 0.f; +} + +void LocretigainCurve::Set(const Curve &pCurve) +{ + if (pCurve.isIdentity()) { + Reset(); // raise this value if the quality suffers from this number of samples + return; + } + + lutLocretigainCurve(501); // raise this value if the quality suffers from this number of samples + sum = 0.f; + + for (int i = 0; i < 501; i++) { + lutLocretigainCurve[i] = pCurve.getVal(double (i) / 500.); + + if (lutLocretigainCurve[i] < 0.02f) { + lutLocretigainCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value + } + + sum += lutLocretigainCurve[i]; + } + + //lutLocCurve.dump("wav"); +} +void LocretigainCurve::Set(const std::vector &curvePoints) +{ + + if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { + FlatCurve tcurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2); + tcurve.setIdentityValue(0.); + Set(tcurve); + } else { + Reset(); + } +} + + + void ColorAppearance::Reset() { lutColCurve.reset(); @@ -1019,7 +2895,7 @@ void ColorAppearance::Set(const Curve &pCurve) lutColCurve(65536); for (int i = 0; i < 65536; i++) { - lutColCurve[i] = pCurve.getVal(double(i) / 65535.) * 65535.; + lutColCurve[i] = pCurve.getVal(double (i) / 65535.) * 65535.; } } @@ -1038,10 +2914,10 @@ void RetinextransmissionCurve::Set(const Curve &pCurve) return; } - luttransmission(501); // raise this value if the quality suffers from this number of samples + luttransmission(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - luttransmission[i] = pCurve.getVal(double(i) / 500.); + luttransmission[i] = pCurve.getVal(double (i) / 500.); } } @@ -1071,10 +2947,10 @@ void RetinexgaintransmissionCurve::Set(const Curve &pCurve) return; } - lutgaintransmission(501); // raise this value if the quality suffers from this number of samples + lutgaintransmission(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - lutgaintransmission[i] = pCurve.getVal(double(i) / 500.); + lutgaintransmission[i] = pCurve.getVal(double (i) / 500.); } } @@ -1101,9 +2977,9 @@ void ToneCurve::Set(const Curve &pCurve, float gamma) if (gamma <= 0.0 || gamma == 1.) { for (int i = 0; i < 65536; i++) { - lutToneCurve[i] = (float)pCurve.getVal(float(i) / 65535.f) * 65535.f; + lutToneCurve[i] = (float)pCurve.getVal(float (i) / 65535.f) * 65535.f; } - } else if(gamma == (float)Color::sRGBGammaCurve) { + } else if (gamma == (float)Color::sRGBGammaCurve) { // for sRGB gamma we can use luts, which is much faster for (int i = 0; i < 65536; i++) { float val = Color::gammatab_srgb[i] / 65535.f; @@ -1113,17 +2989,17 @@ void ToneCurve::Set(const Curve &pCurve, float gamma) } } else { - const float start = expf(gamma * logf( -0.055 / ((1.0 / gamma - 1.0) * 1.055 ))); - const float slope = 1.055 * powf (start, 1.0 / gamma - 1) - 0.055 / start; + const float start = expf(gamma * logf(-0.055 / ((1.0 / gamma - 1.0) * 1.055))); + const float slope = 1.055 * powf(start, 1.0 / gamma - 1) - 0.055 / start; const float mul = 1.055; const float add = 0.055; // apply gamma, that is 'pCurve' is defined with the given gamma and here we convert it to a curve in linear space for (int i = 0; i < 65536; i++) { - float val = float(i) / 65535.f; - val = CurveFactory::gamma (val, gamma, start, slope, mul, add); + float val = float (i) / 65535.f; + val = CurveFactory::gamma(val, gamma, start, slope, mul, add); val = pCurve.getVal(val); - val = CurveFactory::igamma (val, gamma, start, slope, mul, add); + val = CurveFactory::igamma(val, gamma, start, slope, mul, add); lutToneCurve[i] = val * 65535.f; } } @@ -1141,10 +3017,10 @@ void OpacityCurve::Set(const Curve *pCurve) return; } - lutOpacityCurve(501); // raise this value if the quality suffers from this number of samples + lutOpacityCurve(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - lutOpacityCurve[i] = pCurve->getVal(double(i) / 500.); + lutOpacityCurve[i] = pCurve->getVal(double (i) / 500.); } //lutOpacityCurve.dump("opacity"); @@ -1155,7 +3031,7 @@ void OpacityCurve::Set(const std::vector &curvePoints, bool &opautili) std::unique_ptr tcurve; if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { - tcurve = std::unique_ptr(new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2)); + tcurve = std::unique_ptr (new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2)); tcurve->setIdentityValue(0.); } @@ -1182,13 +3058,13 @@ void WavCurve::Set(const Curve &pCurve) return; } - lutWavCurve(501); // raise this value if the quality suffers from this number of samples + lutWavCurve(501); // raise this value if the quality suffers from this number of samples sum = 0.f; for (int i = 0; i < 501; i++) { - lutWavCurve[i] = pCurve.getVal(double(i) / 500.); + lutWavCurve[i] = pCurve.getVal(double (i) / 500.); - if(lutWavCurve[i] < 0.02f) { + if (lutWavCurve[i] < 0.02f) { lutWavCurve[i] = 0.02f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value } @@ -1259,10 +3135,10 @@ void WavOpacityCurveRG::Set(const Curve &pCurve) return; } - lutOpacityCurveRG(501); // raise this value if the quality suffers from this number of samples + lutOpacityCurveRG(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - lutOpacityCurveRG[i] = pCurve.getVal(double(i) / 500.); + lutOpacityCurveRG[i] = pCurve.getVal(double (i) / 500.); } } @@ -1329,10 +3205,10 @@ void WavOpacityCurveBY::Set(const Curve &pCurve) return; } - lutOpacityCurveBY(501); // raise this value if the quality suffers from this number of samples + lutOpacityCurveBY(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - lutOpacityCurveBY[i] = pCurve.getVal(double(i) / 500.); + lutOpacityCurveBY[i] = pCurve.getVal(double (i) / 500.); } } @@ -1361,10 +3237,10 @@ void WavOpacityCurveW::Set(const Curve &pCurve) return; } - lutOpacityCurveW(501); // raise this value if the quality suffers from this number of samples + lutOpacityCurveW(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - lutOpacityCurveW[i] = pCurve.getVal(double(i) / 500.); + lutOpacityCurveW[i] = pCurve.getVal(double (i) / 500.); } } @@ -1393,10 +3269,10 @@ void WavOpacityCurveWL::Set(const Curve &pCurve) return; } - lutOpacityCurveWL(501); // raise this value if the quality suffers from this number of samples + lutOpacityCurveWL(501); // raise this value if the quality suffers from this number of samples for (int i = 0; i < 501; i++) { - lutOpacityCurveWL[i] = pCurve.getVal(double(i) / 500.); + lutOpacityCurveWL[i] = pCurve.getVal(double (i) / 500.); } } @@ -1427,13 +3303,13 @@ void NoiseCurve::Set(const Curve &pCurve) return; } - lutNoiseCurve(501); // raise this value if the quality suffers from this number of samples + lutNoiseCurve(501); // raise this value if the quality suffers from this number of samples sum = 0.f; for (int i = 0; i < 501; i++) { - lutNoiseCurve[i] = pCurve.getVal(double(i) / 500.); + lutNoiseCurve[i] = pCurve.getVal(double (i) / 500.); - if(lutNoiseCurve[i] < 0.01f) { + if (lutNoiseCurve[i] < 0.01f) { lutNoiseCurve[i] = 0.01f; //avoid 0.f for wavelet : under 0.01f quasi no action for each value } @@ -1507,7 +3383,7 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], //lr1=low; for (int i = 0; i <= upperBound; ++i) { - double x = double(i) / double(upperBound); + double x = double (i) / double (upperBound); if (x > nextX) { ++ptNum; @@ -1523,13 +3399,13 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], } if (!ptNum) { - Color::hsv2rgb(float(prevY), satur, lr1, r, g, b); + Color::hsv2rgb(float (prevY), satur, lr1, r, g, b); Color::rgbxyz(r, g, b, xx, yy, zz, xyz_rgb); lut1[i] = xx; lut2[i] = yy; lut3[i] = zz; } else if (ptNum >= nPoints) { - Color::hsv2rgb(float(nextY), satur, lr2, r, g, b); + Color::hsv2rgb(float (nextY), satur, lr2, r, g, b); Color::rgbxyz(r, g, b, xx, yy, zz, xyz_rgb); lut1[i] = xx; lut2[i] = yy; @@ -1539,8 +3415,8 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], if (dY > 0.000001 || dY < -0.000001) { float r1, g1, b1, r2, g2, b2; - Color::hsv2rgb(float(prevY), satur, lr1, r1, g1, b1); - Color::hsv2rgb(float(nextY), satur, lr2, r2, g2, b2); + Color::hsv2rgb(float (prevY), satur, lr1, r1, g1, b1); + Color::hsv2rgb(float (nextY), satur, lr2, r2, g2, b2); LUTf dum; float X1, X2, Y1, Y2, Z1, Z2, L1, a_1, b_1, c1, h1; Color::rgbxyz(r2, g2, b2, X2, Y2, Z2, xyz_rgb); @@ -1548,34 +3424,27 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], //I use XYZ to mix color 1 and 2 rather than rgb (gamut) and rather than Lab artifacts X1 = X1 + (X2 - X1) * currY / dY; - if(X1 < 0.f) { + if (X1 < 0.f) { X1 = 0.f; //negative value not good } Y1 = Y1 + (Y2 - Y1) * currY / dY; - if(Y1 < 0.f) { + if (Y1 < 0.f) { Y1 = 0.f; } Z1 = Z1 + (Z2 - Z1) * currY / dY; - if(Z1 < 0.f) { + if (Z1 < 0.f) { Z1 = 0.f; } - Color::XYZ2Lab(X1, Y1, Z1, L1, a_1, b_1);//prepare to gamut control + Color::XYZ2Lab(X1, Y1, Z1, L1, a_1, b_1); //prepare to gamut control Color::Lab2Lch(a_1, b_1, c1, h1); float Lr = L1 / 327.68f; float RR, GG, BB; -#ifndef NDEBUG - bool neg = false; - bool more_rgb = false; - //gamut control : Lab values are in gamut - Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f, neg, more_rgb); -#else Color::gamutLchonly(h1, Lr, c1, RR, GG, BB, xyz_rgb, false, 0.15f, 0.96f); -#endif L1 = Lr * 327.68f; float La, Lb, X, Y, Z; // converting back to rgb @@ -1585,7 +3454,7 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], lut2[i] = Y; lut3[i] = Z; } else { - Color::hsv2rgb(float(nextY), satur, lumin, r, g, b); + Color::hsv2rgb(float (nextY), satur, lumin, r, g, b); Color::rgbxyz(r, g, b, xx, yy, zz, xyz_rgb); lut1[i] = xx; lut2[i] = yy; @@ -1593,14 +3462,6 @@ void ColorGradientCurve::SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], } } } - - /* - #ifndef NDEBUG - lutRed.dump("red"); - lutGreen.dump("green"); - lutBlue.dump("blue"); - #endif - */ } void ColorGradientCurve::SetXYZ(const std::vector &curvePoints, const double xyz_rgb[3][3], float satur, float lumin) @@ -1608,7 +3469,7 @@ void ColorGradientCurve::SetXYZ(const std::vector &curvePoints, const do std::unique_ptr tcurve; if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { - tcurve = std::unique_ptr(new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2)); + tcurve = std::unique_ptr (new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2)); } if (tcurve) { @@ -1644,7 +3505,7 @@ void ColorGradientCurve::SetRGB(const Curve *pCurve) Color::eInterpolationDirection dir = Color::ID_DOWN; for (int i = 0; i <= upperBound; ++i) { - double x = double(i) / double(upperBound); + double x = double (i) / double (upperBound); if (x > nextX) { ++ptNum; @@ -1658,12 +3519,12 @@ void ColorGradientCurve::SetRGB(const Curve *pCurve) } if (!ptNum) { - Color::hsv2rgb(float(prevY), 1.f, 1.f, r, g, b); + Color::hsv2rgb(float (prevY), 1.f, 1.f, r, g, b); lut1[i] = r; lut2[i] = g; lut3[i] = b; } else if (ptNum >= nPoints) { - Color::hsv2rgb(float(nextY), 1.f, 1.f, r, g, b); + Color::hsv2rgb(float (nextY), 1.f, 1.f, r, g, b); lut1[i] = r; lut2[i] = g; lut3[i] = b; @@ -1677,29 +3538,21 @@ void ColorGradientCurve::SetRGB(const Curve *pCurve) Color::hsv2rgb(h2, 1.f, 1.f, ro, go, bo); #else float r1, g1, b1, r2, g2, b2, ro, go, bo; - Color::hsv2rgb(float(prevY), 1., 1., r1, g1, b1); - Color::hsv2rgb(float(nextY), 1., 1., r2, g2, b2); + Color::hsv2rgb(float (prevY), 1., 1., r1, g1, b1); + Color::hsv2rgb(float (nextY), 1., 1., r2, g2, b2); Color::interpolateRGBColor(currY / dY, r1, g1, b1, r2, g2, b2, Color::CHANNEL_LIGHTNESS | Color::CHANNEL_CHROMATICITY | Color::CHANNEL_HUE, xyz_rgb, rgb_xyz, ro, go, bo); #endif lut1[i] = ro; lut2[i] = go; lut3[i] = bo; } else { - Color::hsv2rgb(float(nextY), 1.f, 1.f, r, g, b); + Color::hsv2rgb(float (nextY), 1.f, 1.f, r, g, b); lut1[i] = r; lut2[i] = g; lut3[i] = b; } } } - - /* - #ifndef NDEBUG - lut1.dump("red"); - lut2.dump("green"); - lut3.dump("blue"); - #endif - */ } void ColorGradientCurve::SetRGB(const std::vector &curvePoints) @@ -1707,7 +3560,7 @@ void ColorGradientCurve::SetRGB(const std::vector &curvePoints) std::unique_ptr tcurve; if (!curvePoints.empty() && curvePoints[0] > FCT_Linear && curvePoints[0] < FCT_Unchanged) { - tcurve = std::unique_ptr(new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2)); + tcurve = std::unique_ptr (new FlatCurve(curvePoints, false, CURVES_MIN_POLY_POINTS / 2)); } if (tcurve) { @@ -1753,18 +3606,18 @@ void PerceptualToneCurve::cubic_spline(const float x[], const float y[], const i A[i][len - 1] = 6 * (b[i + 1] - b[i]); } - for(i = 1; i < len - 2; i++) { + for (i = 1; i < len - 2; i++) { float v = A[i + 1][i] / A[i][i]; - for(j = 1; j <= len - 1; j++) { + for (j = 1; j <= len - 1; j++) { A[i + 1][j] -= v * A[i][j]; } } - for(i = len - 2; i > 0; i--) { + for (i = len - 2; i > 0; i--) { float acc = 0; - for(j = i; j <= len - 2; j++) { + for (j = i; j <= len - 2; j++) { acc += A[i][j] * c[j]; } @@ -1884,7 +3737,7 @@ float PerceptualToneCurve::calculateToneCurveContrastValue() const // Note: the analysis is made on the gamma encoded curve, as the LUT is linear we make backwards gamma to struct find_tc_slope_fun_arg arg = { this }; - float k = find_minimum_interval_halving(find_tc_slope_fun, &arg, 0.1, 5.0, 0.01, 20); // normally found in 8 iterations + float k = find_minimum_interval_halving(find_tc_slope_fun, &arg, 0.1, 5.0, 0.01, 20); // normally found in 8 iterations //fprintf(stderr, "average slope: %f\n", k); float maxslope = 0; @@ -1941,7 +3794,7 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float if (oog_r && oog_g && oog_b) { continue; } - + float r = CLIP(rc[i]); float g = CLIP(gc[i]); float b = CLIP(bc[i]); @@ -1964,17 +3817,35 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float if (ar >= 65535.f && ag >= 65535.f && ab >= 65535.f) { // clip fast path, will also avoid strange colours of clipped highlights //rc[i] = gc[i] = bc[i] = 65535.f; - if (!oog_r) rc[i] = 65535.f; - if (!oog_g) gc[i] = 65535.f; - if (!oog_b) bc[i] = 65535.f; + if (!oog_r) { + rc[i] = 65535.f; + } + + if (!oog_g) { + gc[i] = 65535.f; + } + + if (!oog_b) { + bc[i] = 65535.f; + } + continue; } if (ar <= 0.f && ag <= 0.f && ab <= 0.f) { //rc[i] = gc[i] = bc[i] = 0; - if (!oog_r) rc[i] = 0.f; - if (!oog_g) gc[i] = 0.f; - if (!oog_b) bc[i] = 0.f; + if (!oog_r) { + rc[i] = 0.f; + } + + if (!oog_g) { + gc[i] = 0.f; + } + + if (!oog_b) { + bc[i] = 0.f; + } + continue; } @@ -1997,11 +3868,11 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float Color::Prophotoxyz(r, g, b, x, y, z); float J, C, h; - Ciecam02::xyz2jch_ciecam02float( J, C, h, - aw, fl, - x * 0.0015259022f, y * 0.0015259022f, z * 0.0015259022f, - xw, yw, zw, - c, nc, pow1, nbb, ncb, cz, d); + Ciecam02::xyz2jch_ciecam02float(J, C, h, + aw, fl, + x * 0.0015259022f, y * 0.0015259022f, z * 0.0015259022f, + xw, yw, zw, + c, nc, pow1, nbb, ncb, cz, d); if (!isfinite(J) || !isfinite(C) || !isfinite(h)) { @@ -2014,9 +3885,18 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float g = newg; b = newb; } - if (!oog_r) rc[i] = r; - if (!oog_g) gc[i] = g; - if (!oog_b) bc[i] = b; + + if (!oog_r) { + rc[i] = r; + } + + if (!oog_g) { + gc[i] = g; + } + + if (!oog_b) { + bc[i] = b; + } continue; } @@ -2108,10 +3988,10 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float C *= cmul; - Ciecam02::jch2xyz_ciecam02float( x, y, z, - J, C, h, - xw, yw, zw, - c, nc, pow1, nbb, ncb, fl, cz, d, aw ); + Ciecam02::jch2xyz_ciecam02float(x, y, z, + J, C, h, + xw, yw, zw, + c, nc, pow1, nbb, ncb, fl, cz, d, aw); if (!isfinite(x) || !isfinite(y) || !isfinite(z)) { // can happen for colours on the rim of being outside gamut, that worked without chroma scaling but not with. Then we return only the curve's result. @@ -2124,9 +4004,17 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float b = newb; } - if (!oog_r) rc[i] = r; - if (!oog_g) gc[i] = g; - if (!oog_b) bc[i] = b; + if (!oog_r) { + rc[i] = r; + } + + if (!oog_g) { + gc[i] = g; + } + + if (!oog_b) { + bc[i] = b; + } continue; } @@ -2187,9 +4075,18 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float g = newg; b = newb; } - if (!oog_r) rc[i] = r; - if (!oog_g) gc[i] = g; - if (!oog_b) bc[i] = b; + + if (!oog_r) { + rc[i] = r; + } + + if (!oog_g) { + gc[i] = g; + } + + if (!oog_b) { + bc[i] = b; + } } } float PerceptualToneCurve::cf_range[2]; @@ -2212,7 +4109,7 @@ void PerceptualToneCurve::init() Ciecam02::initcam1float(yb, 1.f, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c); - pow1 = pow_F( 1.64f - pow_F( 0.29f, n ), 0.73f ); + pow1 = pow_F(1.64f - pow_F(0.29f, n), 0.73f); { // init contrast-value-to-chroma-scaling conversion curve @@ -2280,7 +4177,7 @@ void PerceptualToneCurve::initApplyState(PerceptualToneCurveState & state, const state.Working2Prophoto[i][j] += prophoto_xyz[i][k] * Work[k][j]; } - Work = ICCStore::getInstance()->workingSpaceInverseMatrix (workingSpace); + Work = ICCStore::getInstance()->workingSpaceInverseMatrix(workingSpace); memset(state.Prophoto2Working, 0, sizeof(state.Prophoto2Working)); for (int i = 0; i < 3; i++) diff --git a/rtengine/curves.h b/rtengine/curves.h index 0fb58cc9e..9eb3d8e56 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -74,7 +74,8 @@ inline void setUnlessOOG(vfloat &r, vfloat &g, vfloat &b, const vfloat rr, const bool sanitizeCurve(std::vector& curve); -namespace curves { +namespace curves +{ inline void setLutVal(const LUTf &lut, float &val) { @@ -124,33 +125,33 @@ class CurveFactory 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); + 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) + 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 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) + 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) + static inline double cupper(double x, double m, double hr) { if (hr > 1.0) { - return baseu (x, m, 2.0 * (hr - 1.0) / m); + return baseu(x, m, 2.0 * (hr - 1.0) / m); } double x1 = (1.0 - hr) / m; @@ -167,12 +168,12 @@ protected: 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) + 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) + static inline double cupper2(double x, double m, double hr) { double x1 = (1.0 - hr) / m; double x2 = x1 + hr; @@ -187,7 +188,7 @@ protected: return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0.3 * hr); } - static inline double clower2 (double x, double m, double sr) + 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; @@ -201,7 +202,7 @@ protected: } // 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) + static inline double basecurve(double x, double a, double b, double D, double hr, double sr) { if (b < 0) { double m = 0.5;//midpoint @@ -219,7 +220,7 @@ protected: 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; + 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 { @@ -227,7 +228,7 @@ protected: } } } - static inline double simplebasecurve (double x, double b, double sr) + 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) { @@ -248,7 +249,7 @@ protected: double y = (m - b) * slope; if (x <= m) { - return clower (x / m, slope * m / y, sr) * y; + return clower(x / m, slope * m / y, sr) * y; } else { return y + (x - m) * slope; } @@ -281,58 +282,58 @@ public: } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - static inline double centercontrast (double x, double b, double m); + static inline double centercontrast(double x, double b, double m); // standard srgb gamma and its inverse - static inline double gamma2 (double x) + 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) + 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) + static inline float gamma2(float x) { return x <= 0.00304f ? x * 12.92310f : 1.055f * expf(logf(x) / static_cast(sRGBGammaCurve)) - 0.055f; } - static inline float igamma2 (float x) + static inline float igamma2(float x) { return x <= 0.03928f ? x / 12.92310f : expf(logf((x + 0.055f) / 1.055f) * static_cast(sRGBGammaCurve)); } // gamma function with adjustable parameters - static inline double gamma (double x, double gamma, double start, double slope, double mul, double add) + 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) + 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) ); + 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) + 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) + 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) ); + 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) + 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) ); + 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) + static inline float hlcurve(const float exp_scale, const float comp, const float hlrange, float level) { if (comp > 0.f) { float val = level + (hlrange - 65536.f); - if(val == 0.0f) { // to avoid division by zero + if (val == 0.0f) { // to avoid division by zero val = 0.000001f; } @@ -350,29 +351,49 @@ public: } } + 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, - const LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2, + 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, + const LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2, + int skip = 1); - 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, + static void complexCurvelocal(double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double cont, double lumare, + LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTf & lightCurveloc, float avg, 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 ( + static void Curvelocalhl(double ecomp, double hlcompr, double hlcomprthresh, LUTf & hlCurve); + + 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 curveLocal(bool & locallutili, const std::vector& curvePoints, LUTf & LocalLCurve, int skip); + static void curveCCLocal(bool & localcutili, const std::vector& curvePoints, LUTf & LocalCCurve, int skip); + static void curveskLocal(bool & localskutili, const std::vector& curvePoints, LUTf & LocalskCurve, int skip); + static void curveexLocal(bool & localexutili, const std::vector& curvePoints, LUTf & LocalexCurve, int skip); + static void curvemaskLocal(bool & localmaskutili, const std::vector& curvePoints, LUTf & LocalmaskCurve, 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 localLCurve(double br, double contr,/* const std::vector& curvePoints,*/ LUTu & histogram, LUTf & outCurve, int skip, bool & utili); + + static void updatechroma( + const std::vector& cccurvePoints, + LUTu & histogramC, LUTu & outBeforeCCurveHistogramC,//for chroma + 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, @@ -382,7 +403,7 @@ public: ColorAppearance & outColCurve2, ColorAppearance & outColCurve3, int skip = 1); - static void RGBCurve (const std::vector& curvePoints, LUTf & outCurve, int skip); + static void RGBCurve(const std::vector& curvePoints, LUTf & outCurve, int skip); }; @@ -420,23 +441,23 @@ protected: double increment; int nbr_points; - static inline double p00 (double x, double prot) + static inline double p00(double x, double prot) { - return CurveFactory::clower (x, 2.0, prot); + return CurveFactory::clower(x, 2.0, prot); } - static inline double p11 (double x, double prot) + static inline double p11(double x, double prot) { - return CurveFactory::cupper (x, 2.0, prot); + return CurveFactory::cupper(x, 2.0, prot); } - static inline double p01 (double x, double 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; + 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) + 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; + 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) + 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); } @@ -445,15 +466,15 @@ protected: void fillDyByDx(); public: - Curve (); - virtual ~Curve () {}; - void AddPolygons (); - int getSize () const; // return the number of control points + 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 double getVal(double t) const = 0; + virtual void getVal(const std::vector& t, std::vector& res) const = 0; - virtual bool isIdentity () const = 0; + virtual bool isIdentity() const = 0; }; class DiagonalCurve final : public Curve @@ -462,23 +483,23 @@ class DiagonalCurve final : public Curve protected: DiagonalCurveType kind; - void spline_cubic_set (); + void spline_cubic_set(); void catmull_rom_set(); - void NURBS_set (); + void NURBS_set(); public: - explicit DiagonalCurve (const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); - ~DiagonalCurve () override; + 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 + 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 final : public Curve, public rtengine::NonCopyable +class FlatCurve final : public Curve { private: @@ -488,17 +509,17 @@ private: double identityValue; bool periodic; - void CtrlPoints_set (); + void CtrlPoints_set(); public: - explicit FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); - ~FlatCurve () override; + 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 + 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; }; @@ -581,11 +602,11 @@ public: 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 + 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 + 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; @@ -599,6 +620,795 @@ public: } }; +class LocLHCurve +{ +private: + LUTf lutLocLHCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLHCurve() {}; + LocLHCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool &LHutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLHCurve[index]; + } + operator bool (void) const + { + return lutLocLHCurve; + } +}; + +class LocHHmaskblCurve +{ +private: + LUTf lutLocHHmaskblCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmaskblCurve() {}; + LocHHmaskblCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmasblutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmaskblCurve[index]; + } + operator bool (void) const + { + return lutLocHHmaskblCurve; + } +}; + +class LocCCmaskblCurve +{ +private: + LUTf lutLocCCmaskblCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmaskblCurve() {}; + LocCCmaskblCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmasblutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmaskblCurve[index]; + } + operator bool (void) const + { + return lutLocCCmaskblCurve; + } +}; + +class LocLLmaskblCurve +{ +private: + LUTf lutLocLLmaskblCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmaskblCurve() {}; + LocLLmaskblCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmasblutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmaskblCurve[index]; + } + operator bool (void) const + { + return lutLocLLmaskblCurve; + } +}; + + + +class LocHHmasktmCurve +{ +private: + LUTf lutLocHHmasktmCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmasktmCurve() {}; + LocHHmasktmCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmastmutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmasktmCurve[index]; + } + operator bool (void) const + { + return lutLocHHmasktmCurve; + } +}; + + +class LocCCmasktmCurve +{ +private: + LUTf lutLocCCmasktmCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmasktmCurve() {}; + LocCCmasktmCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmastmutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmasktmCurve[index]; + } + operator bool (void) const + { + return lutLocCCmasktmCurve; + } +}; + +class LocLLmasktmCurve +{ +private: + LUTf lutLocLLmasktmCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmasktmCurve() {}; + LocLLmasktmCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmastmutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmasktmCurve[index]; + } + operator bool (void) const + { + return lutLocLLmasktmCurve; + } +}; + + + +class LocHHmaskretiCurve +{ +private: + LUTf lutLocHHmaskretiCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmaskretiCurve() {}; + LocHHmaskretiCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmasretiutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmaskretiCurve[index]; + } + operator bool (void) const + { + return lutLocHHmaskretiCurve; + } +}; + + +class LocCCmaskretiCurve +{ +private: + LUTf lutLocCCmaskretiCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmaskretiCurve() {}; + LocCCmaskretiCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmasretiutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmaskretiCurve[index]; + } + operator bool (void) const + { + return lutLocCCmaskretiCurve; + } +}; + +class LocLLmaskretiCurve +{ +private: + LUTf lutLocLLmaskretiCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmaskretiCurve() {}; + LocLLmaskretiCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmasretiutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmaskretiCurve[index]; + } + operator bool (void) const + { + return lutLocLLmaskretiCurve; + } +}; + + + + + +class LocHHmaskcbCurve +{ +private: + LUTf lutLocHHmaskcbCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmaskcbCurve() {}; + LocHHmaskcbCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmascbutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmaskcbCurve[index]; + } + operator bool (void) const + { + return lutLocHHmaskcbCurve; + } +}; + + +class LocCCmaskcbCurve +{ +private: + LUTf lutLocCCmaskcbCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmaskcbCurve() {}; + LocCCmaskcbCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmascbutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmaskcbCurve[index]; + } + operator bool (void) const + { + return lutLocCCmaskcbCurve; + } +}; + +class LocLLmaskcbCurve +{ +private: + LUTf lutLocLLmaskcbCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmaskcbCurve() {}; + LocLLmaskcbCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmascbutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmaskcbCurve[index]; + } + operator bool (void) const + { + return lutLocLLmaskcbCurve; + } +}; + + + + +class LocHHmaskexpCurve +{ +private: + LUTf lutLocHHmaskexpCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmaskexpCurve() {}; + LocHHmaskexpCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmasexputili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmaskexpCurve[index]; + } + operator bool (void) const + { + return lutLocHHmaskexpCurve; + } +}; + + +class LocCCmaskexpCurve +{ +private: + LUTf lutLocCCmaskexpCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmaskexpCurve() {}; + LocCCmaskexpCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmasexputili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmaskexpCurve[index]; + } + operator bool (void) const + { + return lutLocCCmaskexpCurve; + } +}; + +class LocLLmaskexpCurve +{ +private: + LUTf lutLocLLmaskexpCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmaskexpCurve() {}; + LocLLmaskexpCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmasexputili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmaskexpCurve[index]; + } + operator bool (void) const + { + return lutLocLLmaskexpCurve; + } +}; + + +class LocHHmaskSHCurve +{ +private: + LUTf lutLocHHmaskSHCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmaskSHCurve() {}; + LocHHmaskSHCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmasSHutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmaskSHCurve[index]; + } + operator bool (void) const + { + return lutLocHHmaskSHCurve; + } +}; + + +class LocCCmaskSHCurve +{ +private: + LUTf lutLocCCmaskSHCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmaskSHCurve() {}; + LocCCmaskSHCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmasSHutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmaskSHCurve[index]; + } + operator bool (void) const + { + return lutLocCCmaskSHCurve; + } +}; + +class LocLLmaskSHCurve +{ +private: + LUTf lutLocLLmaskSHCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmaskSHCurve() {}; + LocLLmaskSHCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmasSHutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmaskSHCurve[index]; + } + operator bool (void) const + { + return lutLocLLmaskSHCurve; + } +}; + + + + +class LocHHmaskCurve +{ +private: + LUTf lutLocHHmaskCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHmaskCurve() {}; + LocHHmaskCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lhmasutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHmaskCurve[index]; + } + operator bool (void) const + { + return lutLocHHmaskCurve; + } +}; + + +class LocCCmaskCurve +{ +private: + LUTf lutLocCCmaskCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocCCmaskCurve() {}; + LocCCmaskCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & lcmasutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocCCmaskCurve[index]; + } + operator bool (void) const + { + return lutLocCCmaskCurve; + } +}; + +class LocLLmaskCurve +{ +private: + LUTf lutLocLLmaskCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocLLmaskCurve() {}; + LocLLmaskCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool & llmasutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocLLmaskCurve[index]; + } + operator bool (void) const + { + return lutLocLLmaskCurve; + } +}; + + +class LocHHCurve +{ +private: + LUTf lutLocHHCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocHHCurve() {}; + LocHHCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool &HHutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocHHCurve[index]; + } + operator bool (void) const + { + return lutLocHHCurve; + } +}; + +class LocretigainCurve +{ +private: + LUTf lutLocretigainCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocretigainCurve() {}; + LocretigainCurve(); + void Reset(); + void Set(const std::vector &curvePoints); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocretigainCurve[index]; + } + operator bool (void) const + { + return lutLocretigainCurve; + } +}; + +class LocretitransCurve +{ +private: + LUTf lutLocretitransCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocretitransCurve() {}; + LocretitransCurve(); + void Reset(); + void Set(const std::vector &curvePoints); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocretitransCurve[index]; + } + operator bool (void) const + { + return lutLocretitransCurve; + } +}; + + +class LocwavCurve +{ +private: + LUTf lutLocwavCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocwavCurve() {}; + LocwavCurve(); + void Reset(); + void Set(const std::vector &curvePoints, bool &lcwavutili); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocwavCurve[index]; + } + +#ifdef __SSE2__ + vfloat operator[](vfloat index) const + { + return lutLocwavCurve[index]; + } +#endif + + operator bool (void) const + { + return lutLocwavCurve; + } +}; + + +class LocretigainCurverab +{ +private: + LUTf lutLocretigainCurverab; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~LocretigainCurverab() {}; + LocretigainCurverab(); + void Reset(); + void Set(const std::vector &curvePoints); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutLocretigainCurverab[index]; + } + operator bool (void) const + { + return lutLocretigainCurverab; + } +}; + + class WavCurve { private: @@ -850,10 +1660,10 @@ public: }; //lightness curve -inline void Lightcurve::Apply (float& Li) const +inline void Lightcurve::Apply(float& Li) const { - assert (lutColCurve); + assert(lutColCurve); curves::setLutVal(lutColCurve, Li); } @@ -865,10 +1675,10 @@ public: }; //brightness curve -inline void Brightcurve::Apply (float& Br) const +inline void Brightcurve::Apply(float& Br) const { - assert (lutColCurve); + assert(lutColCurve); curves::setLutVal(lutColCurve, Br); } @@ -880,10 +1690,10 @@ public: }; //Chroma curve -inline void Chromacurve::Apply (float& Cr) const +inline void Chromacurve::Apply(float& Cr) const { - assert (lutColCurve); + assert(lutColCurve); curves::setLutVal(lutColCurve, Cr); } @@ -894,10 +1704,10 @@ public: }; //Saturation curve -inline void Saturcurve::Apply (float& Sa) const +inline void Saturcurve::Apply(float& Sa) const { - assert (lutColCurve); + assert(lutColCurve); curves::setLutVal(lutColCurve, Sa); } @@ -909,10 +1719,10 @@ public: }; //Colorfullness curve -inline void Colorfcurve::Apply (float& Cf) const +inline void Colorfcurve::Apply(float& Cf) const { - assert (lutColCurve); + assert(lutColCurve); curves::setLutVal(lutColCurve, Cf); } @@ -927,8 +1737,8 @@ public: // 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; + const size_t start, const size_t end, + float *r, float *g, float *b) const; }; class AdobeToneCurve : public ToneCurve @@ -997,27 +1807,30 @@ public: }; // Standard tone curve -inline void StandardToneCurve::Apply (float& r, float& g, float& b) const +inline void StandardToneCurve::Apply(float& r, float& g, float& b) const { - assert (lutToneCurve); + 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); + 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); + 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. @@ -1029,11 +1842,13 @@ inline void StandardToneCurve::BatchApply( 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]); @@ -1048,39 +1863,40 @@ inline void StandardToneCurve::BatchApply( 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 +inline void AdobeToneCurve::Apply(float& ir, float& ig, float& ib) const { - assert (lutToneCurve); + 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 + 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 + RGBTone(b, r, g); // Case 2: b > r >= g } else if (b > g) { - RGBTone (r, b, g); // Case 3: r >= 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 + 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 + RGBTone(b, g, r); // Case 6: b > g > r } else { - RGBTone (g, b, r); // Case 7: g >= b > r + RGBTone(g, b, r); // Case 7: g >= b > r } } @@ -1172,19 +1988,20 @@ inline void AdobeToneCurve::RGBTone (vfloat& maxval, vfloat& medval, vfloat& min // Modifying the Luminance channel only inline void LuminanceToneCurve::Apply(float &ir, float &ig, float &ib) const { - assert (lutToneCurve); + 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); + 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); } @@ -1210,21 +2027,21 @@ inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const #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); + 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 +inline void WeightedStdToneCurve::Apply(float& ir, float& ig, float& ib) const { - assert (lutToneCurve); + assert(lutToneCurve); float r = CLIP(ir); float g = CLIP(ig); @@ -1242,23 +2059,25 @@ inline void WeightedStdToneCurve::Apply (float& ir, float& ig, float& ib) const 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); + 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); +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); + 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. @@ -1270,6 +2089,7 @@ inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t en break; #endif } + Apply(r[i], g[i], b[i]); i++; } @@ -1311,6 +2131,7 @@ inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t en for (; i < end; ++i) { Apply(r[i], g[i], b[i]); } + #endif } diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index edb7dc317..a166ecf15 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -10048,7 +10048,7 @@ canon_a5: } else if (!strncmp(model, "X-A3", 4) || !strncmp(model, "X-A5", 4)) { width = raw_width = 6016; height = raw_height = 4014; - } else if (!strcmp(model, "X-Pro3") || !strcmp(model, "X-T3") || !strcmp(model, "X-T30")) { + } else if (!strcmp(model, "X-Pro3") || !strcmp(model, "X-T3") || !strcmp(model, "X-T30") || !strcmp(model, "X-T4") || !strcmp(model, "X100V")) { width = raw_width = 6384; height = raw_height = 4182; } diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 8bf27b53d..99bc2581d 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -34,6 +34,8 @@ #include "../rtgui/editcallbacks.h" #include "guidedfilter.h" +#pragma GCC diagnostic warning "-Wall" +#pragma GCC diagnostic warning "-Wextra" namespace { @@ -50,8 +52,8 @@ namespace rtengine { Crop::Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow) - : PipetteBuffer(editDataProvider), origCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr), - cropImg(nullptr), transCrop(nullptr), cieCrop(nullptr), + : PipetteBuffer(editDataProvider), origCrop(nullptr), laboCrop(nullptr), labnCrop(nullptr), reservCrop(nullptr), lastorigCrop(nullptr), + cropImg(nullptr), shbuf_real(nullptr), transCrop(nullptr), cieCrop(nullptr), shbuffer(nullptr), updating(false), newUpdatePending(false), skip(10), cropx(0), cropy(0), cropw(-1), croph(-1), trafx(0), trafy(0), trafw(-1), trafh(-1), @@ -184,8 +186,8 @@ void Crop::update(int todo) setCropSizes(rqcropx, rqcropy, rqcropw, rqcroph, skip, true); } - // printf("x=%d y=%d crow=%d croh=%d skip=%d\n",rqcropx, rqcropy, rqcropw, rqcroph, skip); - // printf("trafx=%d trafyy=%d trafwsk=%d trafHs=%d \n",trafx, trafy, trafw*skip, trafh*skip); + // printf("x=%d y=%d crow=%d croh=%d skip=%d\n",rqcropx, rqcropy, rqcropw, rqcroph, skip); + // printf("trafx=%d trafyy=%d trafwsk=%d trafHs=%d \n",trafx, trafy, trafw*skip, trafh*skip); Imagefloat *calclum = nullptr;//for Luminance denoise curve NoiseCurve noiseLCurve; @@ -735,8 +737,8 @@ void Crop::update(int todo) } if (need_fattal) { - parent->ipf.dehaze(f); - parent->ipf.ToneMapFattal02(f); + parent->ipf.dehaze(f, params.dehaze); + parent->ipf.ToneMapFattal02(f, params.fattal, 3, 0, nullptr, 0, 0, 0); } // crop back to the size expected by the rest of the pipeline @@ -849,9 +851,13 @@ void Crop::update(int todo) }*/ // apply luminance operations - if (todo & (M_LUMINANCE + M_COLOR)) { + //bool tutu = true; + if (todo & (M_LUMINANCE + M_COLOR)) { // + //if (tutu) { // //I made a little change here. Rather than have luminanceCurve (and others) use in/out lab images, we can do more if we copy right here. labnCrop->CopyFrom(laboCrop); + reservCrop->CopyFrom(laboCrop); + lastorigCrop->CopyFrom(laboCrop); //parent->ipf.luminanceCurve (labnCrop, labnCrop, parent->lumacurve); @@ -862,10 +868,387 @@ void Crop::update(int todo) bool clcutili = parent->clcutili; bool cclutili = parent->cclutili; - LUTu dummy; + bool locallutili = parent->locallutili; + LUTf lllocalcurve2(65536, 0); + bool localclutili = parent->localclutili; + LUTf cllocalcurve2(65536, 0); + bool locallcutili = parent->locallcutili; + LUTf lclocalcurve2(65536, 0); + bool localcutili = parent->locallutili; + LUTf cclocalcurve2(65536, 0); + bool localrgbutili = parent->localrgbutili; + LUTf rgblocalcurve2(65536, 0); + bool localexutili = parent->localexutili; + LUTf exlocalcurve2(65536, 0); + bool localmaskutili = parent->localmaskutili; + bool localmaskexputili = parent->localmaskexputili; + bool localmaskSHutili = parent->localmaskSHutili; + bool localmaskvibutili = parent->localmaskvibutili; + bool localmasktmutili = parent->localmasktmutili; + bool localmaskretiutili = parent->localmaskretiutili; + bool localmaskcbutili = parent->localmaskcbutili; + bool localmaskblutili = parent->localmaskblutili; + bool localmasklcutili = parent->localmasklcutili; + LUTf lmasklocalcurve2(65536, 0); + LUTf lmaskexplocalcurve2(65536, 0); + LUTf lmaskSHlocalcurve2(65536, 0); + LUTf lmaskviblocalcurve2(65536, 0); + LUTf lmasktmlocalcurve2(65536, 0); + LUTf lmaskretilocalcurve2(65536, 0); + LUTf lmaskcblocalcurve2(65536, 0); + LUTf lmaskbllocalcurve2(65536, 0); + LUTf lmasklclocalcurve2(65536, 0); + LUTf hltonecurveloc2(65536, 0); //65536 + LUTf shtonecurveloc2(65536, 0); + LUTf tonecurveloc2(65536, 0); + LUTf lightCurveloc2(32770, 0); + bool LHutili = parent->LHutili; + bool HHutili = parent->HHutili; + bool llmasutili = parent->llmasutili; + bool lhmasutili = parent->lhmasutili; + bool lhhmasutili = parent->lhhmasutili; + bool lcmasutili = parent->lcmasutili; + bool lhmasexputili = parent->lhmasexputili; + bool lcmasexputili = parent->lcmasexputili; + bool llmasexputili = parent->llmasexputili; + bool lhmasSHutili = parent->lhmasSHutili; + bool lcmasSHutili = parent->lcmasSHutili; + bool llmasSHutili = parent->llmasSHutili; + bool lhmasvibutili = parent->lhmasvibutili; + bool lcmasvibutili = parent->lcmasvibutili; + bool llmasvibutili = parent->llmasvibutili; + bool lhmaslcutili = parent->lhmaslcutili; + bool lcmaslcutili = parent->lcmaslcutili; + bool llmaslcutili = parent->llmaslcutili; + bool lhmascbutili = parent->lhmascbutili; + bool lcmascbutili = parent->lcmascbutili; + bool llmascbutili = parent->llmascbutili; + bool lhmasretiutili = parent->lhmasretiutili; + bool lcmasretiutili = parent->lcmasretiutili; + bool llmasretiutili = parent->llmasretiutili; + bool lhmastmutili = parent->lhmastmutili; + bool lcmastmutili = parent->lcmastmutili; + bool llmastmutili = parent->llmastmutili; + bool lhmasblutili = parent->lhmasblutili; + bool lcmasblutili = parent->lcmasblutili; + bool llmasblutili = parent->llmasblutili; + bool locwavutili = parent->locwavutili; + bool locwavdenutili = parent->locwavdenutili; + bool loclevwavutili = parent->loclevwavutili; + bool locconwavutili = parent->locconwavutili; + bool loccompwavutili = parent->loccompwavutili; + bool loccomprewavutili = parent->loccomprewavutili; + bool locedgwavutili = parent->locedgwavutili; + bool lmasutiliblwav = parent->lmasutiliblwav; + bool lmasutilicolwav = parent->lmasutilicolwav; + // float avg = parent->avg; + LUTu dummy; + bool needslocal = params.locallab.enabled; + LocretigainCurve locRETgainCurve; + LocretitransCurve locRETtransCurve; + LocLHCurve loclhCurve; + LocHHCurve lochhCurve; + LocCCmaskCurve locccmasCurve; + LocLLmaskCurve locllmasCurve; + LocHHmaskCurve lochhmasCurve; + LocHHmaskCurve lochhhmasCurve; + LocCCmaskCurve locccmasexpCurve; + LocLLmaskCurve locllmasexpCurve; + LocHHmaskCurve lochhmasexpCurve; + LocCCmaskCurve locccmasSHCurve; + LocLLmaskCurve locllmasSHCurve; + LocHHmaskCurve lochhmasSHCurve; + // LocHHmaskCurve lochhhmasSHCurve; + LocCCmaskCurve locccmasvibCurve; + LocLLmaskCurve locllmasvibCurve; + LocHHmaskCurve lochhmasvibCurve; + LocCCmaskCurve locccmaslcCurve; + LocLLmaskCurve locllmaslcCurve; + LocHHmaskCurve lochhmaslcCurve; + LocCCmaskCurve locccmascbCurve; + LocLLmaskCurve locllmascbCurve; + LocHHmaskCurve lochhmascbCurve; + LocCCmaskCurve locccmasretiCurve; + LocLLmaskCurve locllmasretiCurve; + LocHHmaskCurve lochhmasretiCurve; + LocCCmaskCurve locccmastmCurve; + LocLLmaskCurve locllmastmCurve; + LocHHmaskCurve lochhmastmCurve; + LocCCmaskCurve locccmasblCurve; + LocLLmaskCurve locllmasblCurve; + LocHHmaskCurve lochhmasblCurve; + LocwavCurve locwavCurve; + LocwavCurve loclmasCurveblwav; + LocwavCurve loclmasCurvecolwav; + LocwavCurve loclevwavCurve; + LocwavCurve locconwavCurve; + LocwavCurve loccompwavCurve; + LocwavCurve loccomprewavCurve; + LocwavCurve locedgwavCurve; + LocwavCurve locwavCurveden; + + LocretigainCurverab locRETgainCurverab; + locallutili = false; + int sca = skip; + + // bool tyty = false; + // int maxspot = 1; + + if (needslocal) { + for (int sp = 0; sp < (int)params.locallab.spots.size(); sp++) { + locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); + locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); + loclhCurve.Set(params.locallab.spots.at(sp).LHcurve, LHutili); + lochhCurve.Set(params.locallab.spots.at(sp).HHcurve, HHutili); + locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmasutili); + locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmasutili); + lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmasutili); + lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmasutili); + locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve, lcmasexputili); + locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve, llmasexputili); + lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve, lhmasexputili); + locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve, lcmasSHutili); + locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve, llmasSHutili); + lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve, lhmasSHutili); + locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve, lcmasvibutili); + locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve, llmasvibutili); + lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve, lhmasvibutili); + locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve, lcmascbutili); + locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve, llmascbutili); + lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve, lhmascbutili); + locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve, lcmasretiutili); + locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve, llmasretiutili); + lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve, lhmasretiutili); + locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve, lcmastmutili); + locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve, llmastmutili); + lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve, lhmastmutili); + locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); + locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve, llmasblutili); + lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); + loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); + loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); + locccmaslcCurve.Set(params.locallab.spots.at(sp).CCmasklccurve, lcmaslcutili); + locllmaslcCurve.Set(params.locallab.spots.at(sp).LLmasklccurve, llmaslcutili); + lochhmaslcCurve.Set(params.locallab.spots.at(sp).HHmasklccurve, lhmaslcutili); + + locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); + locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden, locwavdenutili); + loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve, loclevwavutili); + locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve, locconwavutili); + loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve, loccompwavutili); + loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); + locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve, locedgwavutili); + locallutili = false; + CurveFactory::curveLocal(locallutili, params.locallab.spots.at(sp).llcurve, lllocalcurve2, sca); + localclutili = false; + CurveFactory::curveLocal(localclutili, params.locallab.spots.at(sp).clcurve, cllocalcurve2, sca); + locallcutili = false; + CurveFactory::curveLocal(locallcutili, params.locallab.spots.at(sp).lccurve, lclocalcurve2, sca); + localrgbutili = false; + CurveFactory::curveLocal(localrgbutili, params.locallab.spots.at(sp).rgbcurve, rgblocalcurve2, sca); + localcutili = false; + CurveFactory::curveCCLocal(localcutili, params.locallab.spots.at(sp).cccurve, cclocalcurve2, sca); + localexutili = false; + CurveFactory::curveexLocal(localexutili, params.locallab.spots.at(sp).excurve, exlocalcurve2, sca); + localmaskutili = false; + CurveFactory::curvemaskLocal(localmaskutili, params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve2, sca); + localmaskexputili = false; + CurveFactory::curvemaskLocal(localmaskexputili, params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve2, sca); + localmaskSHutili = false; + CurveFactory::curvemaskLocal(localmaskSHutili, params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve2, sca); + localmaskvibutili = false; + CurveFactory::curvemaskLocal(localmaskvibutili, params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve2, sca); + localmasktmutili = false; + CurveFactory::curvemaskLocal(localmasktmutili, params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve2, sca); + localmaskretiutili = false; + CurveFactory::curvemaskLocal(localmaskretiutili, params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve2, sca); + localmaskcbutili = false; + CurveFactory::curvemaskLocal(localmaskcbutili, params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve2, sca); + localmasklcutili = false; + CurveFactory::curvemaskLocal(localmasklcutili, params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve2, sca); + localmaskblutili = false; + CurveFactory::curvemaskLocal(localmaskblutili, params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve2, sca); + + double ecomp = params.locallab.spots.at(sp).expcomp; + double black = params.locallab.spots.at(sp).black; + double hlcompr = params.locallab.spots.at(sp).hlcompr; + double hlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh; + double shcompr = params.locallab.spots.at(sp).shcompr; + double br = params.locallab.spots.at(sp).lightness; + if(black < 0. && params.locallab.spots.at(sp).expMethod == "pde" ) { + black *= 1.5; + } + + double cont = params.locallab.spots.at(sp).contrast; + double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; + int lastsav; + float avge; + huerefblu = parent->huerefblurs[sp]; + chromarefblu = parent->chromarefblurs[sp]; + lumarefblu = parent->lumarefblurs[sp]; + huere = parent->huerefs[sp]; + chromare = parent->chromarefs[sp]; + lumare = parent->lumarefs[sp]; + sobelre = parent->sobelrefs[sp]; + avge = parent->avgs[sp]; + + lastsav = parent->lastsavrests[sp]; + + float minCD; + float maxCD; + float mini; + float maxi; + float Tmean; + float Tsigma; + float Tmin; + float Tmax; + CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare, + hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, avge, + sca); + // Locallab mask are only shown for selected spot + if (sp == params.locallab.selspot) { + parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop, lastorigCrop, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, + lllocalcurve2,locallutili, + cllocalcurve2, localclutili, + lclocalcurve2, locallcutili, + loclhCurve, lochhCurve, + lmasklocalcurve2, localmaskutili, + lmaskexplocalcurve2, localmaskexputili, + lmaskSHlocalcurve2, localmaskSHutili, + lmaskviblocalcurve2, localmaskvibutili, + lmasktmlocalcurve2, localmasktmutili, + lmaskretilocalcurve2, localmaskretiutili, + lmaskcblocalcurve2, localmaskcbutili, + lmaskbllocalcurve2, localmaskblutili, + lmasklclocalcurve2, localmasklcutili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, + locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, + locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, + locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, + locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + loclmasCurveblwav,lmasutiliblwav, + loclmasCurvecolwav,lmasutilicolwav, + locwavCurve, locwavutili, + loclevwavCurve, loclevwavutili, + locconwavCurve, locconwavutili, + loccompwavCurve, loccompwavutili, + loccomprewavCurve, loccomprewavutili, + locwavCurveden, locwavdenutili, + locedgwavCurve, locedgwavutili, + LHutili, HHutili, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, + huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, + parent->previewDeltaE, parent->locallColorMask, parent->locallColorMaskinv, parent->locallExpMask, parent->locallExpMaskinv, parent->locallSHMask, parent->locallSHMaskinv, parent->locallvibMask, parent->localllcMask, parent->locallsharMask, parent->locallcbMask, parent->locallretiMask, parent->locallsoftMask, parent->localltmMask, parent->locallblMask, + minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + } else { + parent->ipf.Lab_Local(1, sp, (float**)shbuffer, labnCrop, labnCrop, reservCrop, lastorigCrop, cropx / skip, cropy / skip, skips(parent->fw, skip), skips(parent->fh, skip), skip, locRETgainCurve, locRETtransCurve, + lllocalcurve2,locallutili, + cllocalcurve2, localclutili, + lclocalcurve2, locallcutili, + loclhCurve, lochhCurve, + lmasklocalcurve2, localmaskutili, + lmaskexplocalcurve2, localmaskexputili, + lmaskSHlocalcurve2, localmaskSHutili, + lmaskviblocalcurve2, localmaskvibutili, + lmasktmlocalcurve2, localmasktmutili, + lmaskretilocalcurve2, localmaskretiutili, + lmaskcblocalcurve2, localmaskcbutili, + lmaskbllocalcurve2, localmaskblutili, + lmasklclocalcurve2, localmasklcutili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili,lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, + locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, + locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, + locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, + locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + loclmasCurveblwav,lmasutiliblwav, + loclmasCurvecolwav,lmasutilicolwav, + locwavCurve, locwavutili, + loclevwavCurve, loclevwavutili, + locconwavCurve, locconwavutili, + loccompwavCurve, loccompwavutili, + loccomprewavCurve, loccomprewavutili, + locwavCurveden, locwavdenutili, + locedgwavCurve, locedgwavutili, + LHutili, HHutili, cclocalcurve2, localcutili, rgblocalcurve2, localrgbutili, localexutili, exlocalcurve2, hltonecurveloc2, shtonecurveloc2, tonecurveloc2, lightCurveloc2, + huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + } + + lastorigCrop->CopyFrom(labnCrop); + + lllocalcurve2.clear(); + lclocalcurve2.clear(); + cllocalcurve2.clear(); + lightCurveloc2.clear(); + rgblocalcurve2.clear(); + cclocalcurve2.clear(); + exlocalcurve2.clear(); + lmasklocalcurve2.clear(); + lmaskexplocalcurve2.clear(); + lmaskSHlocalcurve2.clear(); + lmaskviblocalcurve2.clear(); + lmasktmlocalcurve2.clear(); + lmaskretilocalcurve2.clear(); + lmaskcblocalcurve2.clear(); + lmaskbllocalcurve2.clear(); + hltonecurveloc2.clear(); + shtonecurveloc2.clear(); + tonecurveloc2.clear(); + locRETgainCurve.Reset(); + locRETtransCurve.Reset(); + loclhCurve.Reset(); + lochhCurve.Reset(); + locccmasCurve.Reset(); + locllmasCurve.Reset(); + lochhmasCurve.Reset(); + lochhhmasCurve.Reset(); + locllmasexpCurve.Reset(); + locccmasexpCurve.Reset(); + lochhmasexpCurve.Reset(); + locllmasSHCurve.Reset(); + locccmasSHCurve.Reset(); + lochhmasSHCurve.Reset(); + locllmasvibCurve.Reset(); + locccmasvibCurve.Reset(); + lochhmasvibCurve.Reset(); + locllmascbCurve.Reset(); + locccmascbCurve.Reset(); + lochhmascbCurve.Reset(); + locllmasretiCurve.Reset(); + locccmasretiCurve.Reset(); + lochhmasretiCurve.Reset(); + locllmastmCurve.Reset(); + locccmastmCurve.Reset(); + lochhmastmCurve.Reset(); + locllmasblCurve.Reset(); + locccmasblCurve.Reset(); + lochhmasblCurve.Reset(); + locllmaslcCurve.Reset(); + locccmaslcCurve.Reset(); + lochhmaslcCurve.Reset(); + locwavCurve.Reset(); + loclevwavCurve.Reset(); + locconwavCurve.Reset(); + locconwavCurve.Reset(); + locwavCurveden.Reset(); + loclmasCurveblwav.Reset(); + loclmasCurvecolwav.Reset(); + + if (skip <= 2) { + Glib::usleep(settings->cropsleep); //wait to avoid crash when crop 100% and move window + } + } + } + + // int moderetinex; parent->ipf.chromiLuminanceCurve(this, 1, labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); - parent->ipf.vibrance(labnCrop); + parent->ipf.vibrance(labnCrop, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile); parent->ipf.labColorCorrectionRegions(labnCrop); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { @@ -973,6 +1356,7 @@ void Crop::update(int todo) WavOpacityCurveBY waOpacityCurveBY; WavOpacityCurveW waOpacityCurveW; WavOpacityCurveWL waOpacityCurveWL; + LUTf wavclCurve; params.wavelet.getCurves(wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL); @@ -1017,7 +1401,11 @@ void Crop::update(int todo) WaveParams.expnoise = false; } - parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); + parent->ipf.softLight(labnCrop, params.softlight); + +// parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); + + parent->ipf.ip_wavelet(labnCrop, labnCrop, kall, WaveParams, wavCLVCurve, wavblcurve, waOpacityCurveRG, waOpacityCurveSH, waOpacityCurveBY, waOpacityCurveW, waOpacityCurveWL, parent->wavclCurve, skip); if ((WaveParams.ushamethod == "sharp" || WaveParams.ushamethod == "clari") && WaveParams.expclari && WaveParams.CLmethod != "all") { WaveParams.expcontrast = procont; @@ -1143,8 +1531,6 @@ void Crop::update(int todo) } - parent->ipf.softLight(labnCrop); - if (params.colorappearance.enabled) { float fnum = parent->imgsrc->getMetaData()->getFNumber(); // F number float fiso = parent->imgsrc->getMetaData()->getISOSpeed() ; // ISO @@ -1243,11 +1629,28 @@ void Crop::freeAll() laboCrop = nullptr; } + if (labnCrop) { delete labnCrop; labnCrop = nullptr; } + if (reservCrop) { + delete reservCrop; + reservCrop = nullptr; + } + + if (lastorigCrop) { + delete lastorigCrop; + lastorigCrop = nullptr; + } + + + /* if (lablocCrop ) { + delete lablocCrop; + lablocCrop = NULL; + } + */ if (cropImg) { delete cropImg; cropImg = nullptr; @@ -1258,6 +1661,11 @@ void Crop::freeAll() cieCrop = nullptr; } + if (shbuffer) { + delete [] shbuffer; + shbuffer = nullptr; + } + PipetteBuffer::flush(); } @@ -1410,12 +1818,32 @@ bool Crop::setCropSizes(int rcx, int rcy, int rcw, int rch, int skip, bool inter laboCrop = new LabImage(cropw, croph); + // if (translabCrop) translabCrop->reallocLab(); + if (labnCrop) { delete labnCrop; // labnCrop can't be resized } labnCrop = new LabImage(cropw, croph); + if (reservCrop) { + delete reservCrop; // labnCrop can't be resized + } + + reservCrop = new LabImage(cropw, croph); + + if (lastorigCrop) { + delete lastorigCrop; // labnCrop can't be resized + } + + lastorigCrop = new LabImage(cropw, croph); + + /* if (lablocCrop) { + delete lablocCrop; // labnCrop can't be resized + } + + lablocCrop = new LabImage (cropw, croph); + */ if (!cropImg) { cropImg = new Image8; } @@ -1428,6 +1856,21 @@ bool Crop::setCropSizes(int rcx, int rcy, int rcw, int rch, int skip, bool inter cieCrop = nullptr; } + if (shbuffer) { + delete [] shbuffer; + } + + if (shbuf_real) { + delete [] shbuf_real; + } + + shbuffer = new float*[croph]; + shbuf_real = new float[(croph + 2)*cropw]; + + for (int i = 0; i < croph; i++) { + shbuffer[i] = shbuf_real + cropw * i + cropw; + } + if (editType == ET_PIPETTE) { PipetteBuffer::resize(cropw, croph); } else if (PipetteBuffer::bufferCreated()) { diff --git a/rtengine/dcrop.h b/rtengine/dcrop.h index 0b33854c5..3f8a8ad6d 100644 --- a/rtengine/dcrop.h +++ b/rtengine/dcrop.h @@ -40,12 +40,16 @@ protected: Imagefloat* origCrop; // "one chunk" allocation LabImage* laboCrop; // "one chunk" allocation LabImage* labnCrop; // "one chunk" allocation + LabImage* reservCrop; // "one chunk" allocation + LabImage* lastorigCrop; // "one chunk" allocation Image8* cropImg; // "one chunk" allocation ; displayed image in monitor color space, showing the output profile as well (soft-proofing enabled, which then correspond to workimg) or not + float * shbuf_real; // "one chunk" allocation // --- automatically allocated and deleted when necessary, and only renewed on size changes Imagefloat* transCrop; // "one chunk" allocation, allocated if necessary CieImage* cieCrop; // allocating 6 images, each in "one chunk" allocation // ----------------------------------------------------------------- + float** shbuffer; bool updating; /// Flag telling if an updater thread is currently processing bool newUpdatePending; /// Flag telling the updater thread that a new update is pending @@ -63,19 +67,19 @@ protected: ImProcCoordinator* const parent; const bool isDetailWindow; EditUniqueID getCurrEditID(); - bool setCropSizes (int cropX, int cropY, int cropW, int cropH, int skip, bool internal); - void freeAll (); + bool setCropSizes(int cropX, int cropY, int cropW, int cropH, int skip, bool internal); + void freeAll(); public: - Crop (ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow); + Crop(ImProcCoordinator* parent, EditDataProvider *editDataProvider, bool isDetailWindow); ~Crop () override; - +// MyMutex* locMutex; void setEditSubscriber(EditSubscriber* newSubscriber); bool hasListener(); - void update (int todo); + void update(int todo); void setWindow (int cropX, int cropY, int cropW, int cropH, int skip) override { - setCropSizes (cropX, cropY, cropW, cropH, skip, false); + setCropSizes(cropX, cropY, cropW, cropH, skip, false); } /** @brief Synchronously look out if a full update is necessary diff --git a/rtengine/dirpyr_equalizer.cc b/rtengine/dirpyr_equalizer.cc index 0624f4443..871c21ac4 100644 --- a/rtengine/dirpyr_equalizer.cc +++ b/rtengine/dirpyr_equalizer.cc @@ -25,9 +25,11 @@ #include "array2D.h" #include "cieimage.h" #include "color.h" +#include "curves.h" #include "improcfun.h" #include "LUT.h" #include "opthelper.h" +#include "boxblur.h" #include "rt_math.h" #include "settings.h" @@ -37,6 +39,7 @@ float rangeFn(float i) { return 1.f / (i + 1000.f); } + void dirpyr_channel(const float * const * data_fine, float ** data_coarse, int width, int height, int level, int scale) { // scale is spacing of directional averaging weights @@ -244,6 +247,48 @@ void fillLut(LUTf &irangefn, int level, double dirpyrThreshold, float mult, floa } } +//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +void idirpyr_eq_channel_loc(float ** data_coarse, float ** data_fine, float ** buffer, int width, int height, int level, float mult, const float blurcb, const double dirpyrThreshold, float ** hue, float ** chrom, const double skinprot, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scaleprev, bool multiThread) +{ + LUTf irangefn(0x20000); + fillLut(irangefn, level, dirpyrThreshold, mult, skinprot); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + + float hipass = (data_fine[i][j] - data_coarse[i][j]); + buffer[i][j] += irangefn[hipass + 0x10000] * hipass; + + } + } + + if(blurcb > 0.f && choice == 0 && level != 5) { + float multbis; + if (level == 4 && mult > 1.f) { + multbis = 1.f + 0.65f * (mult - 1.f); + } else if (level == 5 && mult > 1.f) { + multbis = 1.f + 0.45f * (mult - 1.f); + } else { + multbis = mult; //multbis to reduce artifacts for high values mult + } + AlignedBuffer blurbufcbdl(width * height); + float rad = 0.05f * blurcb * fabs((level + 1) * (multbis - 1.f)) / scaleprev; + // printf("rad=%f level=%i\n", rad, level); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif +// rtengine::boxblur(buffer, buffer, blurbufcbdl.data, rad, rad, width, height); + rtengine::boxblur(buffer, buffer, rad, width, height, false); + blurbufcbdl.resize(0); + } + + irangefn.clear(); +} + void idirpyr_eq_channel(const float * const * data_coarse, const float * const * data_fine, float ** buffer, int width, int height, int level, float mult, const double dirpyrThreshold, const float * const * hue, const float * const * chrom, const double skinprot, float b_l, float t_l, float t_r) { const float skinprotneg = -skinprot; @@ -507,4 +552,162 @@ void ImProcFunctions::dirpyr_equalizercam(const CieImage *ncie, float ** src, fl } } +void ImProcFunctions::cbdl_local_temp(float ** src, float ** loctemp, int srcwidth, int srcheight, const float * mult, float kchro, const double dirpyrThreshold, const float mergeL, const float contres, const float blurcb, const double skinprot, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scaleprev, bool multiThread) +{ + constexpr int maxlevelloc = 6; + constexpr int scalesloc[maxlevelloc] = {1, 2, 4, 8, 16, 32}; + const float atten123 = rtengine::LIM(settings->level123_cbdl, 0.f, 50.f); + const float atten0 = rtengine::LIM(settings->level0_cbdl, 0.f, 40.f); + int lastlevel = maxlevelloc; + + if (settings->verbose) { + printf("Dirpyr scaleprev=%i\n", scaleprev); + } + + while (lastlevel > 0 && fabs(mult[lastlevel - 1] - 1) < 0.001) { + + lastlevel--; + //printf("last level to process %d \n",lastlevel); + } + + if (lastlevel == 0) { + return; + } + + float multi[6]; + + for (int lv = 0; lv < 6; ++lv) { + if (scalesloc[lv] < scaleprev) { + const float factor = lv >= 1 ? atten123 : atten0; + multi[lv] = (factor * ((float) mult[lv] - 1.f) / 100.f) + 1.f; //modulate action if zoom < 100% + } else { + multi[lv] = mult[lv]; + } + } + + if (settings->verbose) { + printf("CbDL local mult0=%f 1=%f 2=%f 3=%f 4=%f 5%f\n", multi[0], multi[1], multi[2], multi[3], multi[4], multi[5]); + } + + multi_array2D dirpyrlo(srcwidth, srcheight); + + + dirpyr_channel(src, dirpyrlo[0], srcwidth, srcheight, 0, std::max(scalesloc[0] / scaleprev, 1)); + + + for (int level = 1; level < lastlevel; ++level) { + dirpyr_channel(dirpyrlo[level - 1], dirpyrlo[level], srcwidth, srcheight, level, std::max(scalesloc[level] / scaleprev, 1)); + } + + // with the current implementation of idirpyr_eq_channel we can safely use the buffer from last level as buffer, saves some memory +// float ** buffer = dirpyrlo[lastlevel - 1]; + array2D residbuff(srcwidth, srcheight); + array2D resid5(srcwidth, srcheight); + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < srcheight; i++) + for (int j = 0; j < srcwidth; j++) { + residbuff[i][j] = 0.f; + } + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < srcheight; i++) + for (int j = 0; j < srcwidth; j++) { + residbuff[i][j] = dirpyrlo[lastlevel - 1][i][j]; + resid5[i][j] = dirpyrlo[lastlevel - 1][i][j]; + } + + + double avg = 0.f; + if(contres != 0.f) { + int ng = 0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:avg, ng) +#endif + for (int i = 0; i < srcheight; i++) { + for (int j = 0; j < srcwidth; j++) { + avg += residbuff[i][j]; + ng++; + } + } + avg /= ng; + avg /= 32768.f; + avg = LIM01(avg); + } + float contreal = 0.3f * contres; + DiagonalCurve resid_contrast({ + DCT_NURBS, + 0, 0, + avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0), + avg + (1 - avg) * (0.6 - contreal / 250.0), avg + (1 - avg) * (0.6 + contreal / 250.0), + 1, 1 + }); + + if(contres != 0.f) { +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < srcheight; i++) + for (int j = 0; j < srcwidth; j++) { + float buf = LIM01(residbuff[i][j] / 32768.f); + buf = resid_contrast.getVal(buf); + buf *= 32768.f; + residbuff[i][j] = buf; + } + } + + + for (int level = lastlevel - 1; level > 0; level--) { + idirpyr_eq_channel_loc(dirpyrlo[level], dirpyrlo[level - 1], residbuff, srcwidth, srcheight, level, multi[level], blurcb, dirpyrThreshold, nullptr, nullptr, skinprot, gamutlab, b_l, t_l, t_r, b_r, choice, scaleprev, multiThread); + } + + scale = scalesloc[0]; + + idirpyr_eq_channel_loc(dirpyrlo[0], src, residbuff, srcwidth, srcheight, 0, multi[0], blurcb, dirpyrThreshold, nullptr, nullptr, skinprot, gamutlab, b_l, t_l, t_r, b_r, choice, scaleprev, multiThread); + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + array2D loct(srcwidth, srcheight); +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < srcheight; i++) { + for (int j = 0; j < srcwidth; j++) { + loct[i][j] = LIM(residbuff[i][j],0.f,32768.f); // TODO: Really a clip necessary? + } + } + + float clar = 0.01f * mergeL; + +/* + if(clar == 0.f) { + clar = 0.0f; + } +// printf("clar=%f \n", clar); +*/ + if(clar > 0.f) { +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < srcheight; i++) { + for (int j = 0; j < srcwidth; j++) { + loctemp[i][j] = LIM((1.f + clar) * loct[i][j] - clar * resid5[i][j],0.f,32768.f); + } + } + } else { +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < srcheight; i++) { + for (int j = 0; j < srcwidth; j++) { + loctemp[i][j] = LIM(loct[i][j],0.f,32768.f); + } + } + } +} + } diff --git a/rtengine/filmnegativeproc.cc b/rtengine/filmnegativeproc.cc index 6d4fe1ad6..30d0a052d 100644 --- a/rtengine/filmnegativeproc.cc +++ b/rtengine/filmnegativeproc.cc @@ -214,7 +214,7 @@ bool rtengine::RawImageSource::getFilmNegativeExponents(Coord2D spotA, Coord2D s std::array denseVals; // Get channel averages in the two spots, sampling from the original ri->data buffer. - // NOTE: rawData values might be affected by CA corection, FlatField, etc, so: + // NOTE: rawData values might be affected by CA correction, FlatField, etc, so: // rawData[y][x] == (ri->data[y][x] - cblacksom[c]) * scale_mul[c] // is not always true. To calculate exponents on the exact values, we should keep // a copy of the rawData buffer after preprocessing. Worth the memory waste? @@ -330,7 +330,7 @@ void rtengine::RawImageSource::filmNegativeProcess(const procparams::FilmNegativ // If film base values are set in params, use those if (filmBaseValues[0] <= 0.f) { // ...otherwise, the film negative tool might have just been enabled on this image, - // whithout any previous setting. So, estimate film base values from channel medians + // without any previous setting. So, estimate film base values from channel medians std::array medians; diff --git a/rtengine/gauss.cc b/rtengine/gauss.cc index 8d9e5de38..99201a860 100644 --- a/rtengine/gauss.cc +++ b/rtengine/gauss.cc @@ -1355,7 +1355,6 @@ template void gaussVerticalmult (T** src, T** dst, const int W, const i template void gaussianBlurImpl(T** src, T** dst, const int W, const int H, const double sigma, bool useBoxBlur, eGaussType gausstype = GAUSS_STANDARD, T** buffer2 = nullptr) { - static constexpr auto GAUSS_SKIP = 0.25; static constexpr auto GAUSS_3X3_LIMIT = 0.6; static constexpr auto GAUSS_5X5_LIMIT = 0.84; static constexpr auto GAUSS_7X7_LIMIT = 1.15; @@ -1405,6 +1404,9 @@ template void gaussianBlurImpl(T** src, T** dst, const int W, const int } else { if (sigma < GAUSS_SKIP) { // don't perform filtering +#ifdef _OPENMP +#pragma omp single +#endif if (src != dst) { for(int i = 0; i < H; ++i) { memcpy(dst[i], src[i], W * sizeof(T)); diff --git a/rtengine/gauss.h b/rtengine/gauss.h index e226bbc13..71e3506da 100644 --- a/rtengine/gauss.h +++ b/rtengine/gauss.h @@ -19,5 +19,7 @@ #pragma once enum eGaussType {GAUSS_STANDARD, GAUSS_MULT, GAUSS_DIV}; +static constexpr auto GAUSS_SKIP = 0.25; + void gaussianBlur(float** src, float** dst, const int W, const int H, const double sigma, bool useBoxBlur = false, eGaussType gausstype = GAUSS_STANDARD, float** buffer2 = nullptr); diff --git a/rtengine/guidedfilter.cc b/rtengine/guidedfilter.cc index f83560cfc..b3f843bc1 100644 --- a/rtengine/guidedfilter.cc +++ b/rtengine/guidedfilter.cc @@ -3,7 +3,6 @@ * This file is part of RawTherapee. * * Copyright (c) 2018 Alberto Griggio - * Optimized 2019 Ingo Weyrich * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,10 +15,10 @@ * 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 . -*/ + * along with RawTherapee. If not, see . + */ -/* +/** * This is a Fast Guided Filter implementation, derived directly from the * pseudo-code of the paper: * @@ -27,22 +26,36 @@ * by Kaiming He, Jian Sun * * available at https://arxiv.org/abs/1505.00996 -*/ + */ #include "array2D.h" #include "boxblur.h" #include "guidedfilter.h" -#include "imagefloat.h" +#include "boxblur.h" +#include "sleef.h" #include "rescale.h" +#include "imagefloat.h" -#define BENCHMARK -#include "StopWatch.h" +namespace rtengine { -namespace rtengine -{ +#if 0 +# define DEBUG_DUMP(arr) \ + do { \ + Imagefloat im(arr.width(), arr.height()); \ + const char *out = "/tmp/" #arr ".tif"; \ + for (int y = 0; y < im.getHeight(); ++y) { \ + for (int x = 0; x < im.getWidth(); ++x) { \ + im.r(y, x) = im.g(y, x) = im.b(y, x) = arr[y][x] * 65535.f; \ + } \ + } \ + im.saveTIFF(out, 16); \ + } while (false) +#else +# define DEBUG_DUMP(arr) +#endif -namespace -{ + +namespace { int calculate_subsampling(int w, int h, int r) { @@ -65,17 +78,21 @@ int calculate_subsampling(int w, int h, int r) } // namespace + void guidedFilter(const array2D &guide, const array2D &src, array2D &dst, int r, float epsilon, bool multithread, int subsampling) { - enum Op {MUL, DIVEPSILON, SUBMUL}; + + const int W = src.width(); + const int H = src.height(); + + if (subsampling <= 0) { + subsampling = calculate_subsampling(W, H, r); + } + + enum Op { MUL, DIVEPSILON, ADD, SUB, ADDMUL, SUBMUL }; const auto apply = -#ifdef _OPENMP - [multithread, epsilon](Op op, array2D &res, const array2D &a, const array2D &b, const array2D &c=array2D()) -> void -#else - // removed multithread to fix clang warning on msys2 clang builds, which don't support OpenMp - [epsilon](Op op, array2D &res, const array2D &a, const array2D &b, const array2D &c=array2D()) -> void -#endif + [=](Op op, array2D &res, const array2D &a, const array2D &b, const array2D &c=array2D()) -> void { const int w = res.width(); const int h = res.height(); @@ -85,31 +102,49 @@ void guidedFilter(const array2D &guide, const array2D &src, array2 #endif for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { + float r; + float aa = a[y][x]; + float bb = b[y][x]; switch (op) { - case MUL: - res[y][x] = a[y][x] * b[y][x]; - break; - case DIVEPSILON: - res[y][x] = a[y][x] / (b[y][x] + epsilon); // note: the value of epsilon intentionally has an impact on the result. It is not only to avoid divisions by zero - break; - case SUBMUL: - res[y][x] = c[y][x] - (a[y][x] * b[y][x]); - break; - default: - assert(false); - res[y][x] = 0; - break; + case MUL: + r = aa * bb; + break; + case DIVEPSILON: + r = aa / (bb + epsilon); + break; + case ADD: + r = aa + bb; + break; + case SUB: + r = aa - bb; + break; + case ADDMUL: + r = aa * bb + c[y][x]; + break; + case SUBMUL: + r = c[y][x] - (aa * bb); + break; + default: + assert(false); + r = 0; + break; } + res[y][x] = r; } } }; + // use the terminology of the paper (Algorithm 2) + const array2D &I = guide; + const array2D &p = src; + array2D &q = dst; + const auto f_subsample = - [multithread](array2D &d, const array2D &s) -> void + [=](array2D &d, const array2D &s) -> void { if (d.width() == s.width() && d.height() == s.height()) { #ifdef _OPENMP - #pragma omp parallel for if (multithread) +# pragma omp parallel for if (multithread) #endif for (int y = 0; y < s.height(); ++y) { for (int x = 0; x < s.width(); ++x) { @@ -121,84 +156,125 @@ void guidedFilter(const array2D &guide, const array2D &src, array2 } }; + // const auto f_upsample = f_subsample; + + const size_t w = W / subsampling; + const size_t h = H / subsampling; + const auto f_mean = [multithread](array2D &d, array2D &s, int rad) -> void { rad = LIM(rad, 0, (min(s.width(), s.height()) - 1) / 2 - 1); + // boxblur(s, d, rad, s.width(), s.height(), multithread); boxblur(static_cast(s), static_cast(d), rad, s.width(), s.height(), multithread); }; - const int W = src.width(); - const int H = src.height(); - - if (subsampling <= 0) { - subsampling = calculate_subsampling(W, H, r); - } - - const size_t w = W / subsampling; - const size_t h = H / subsampling; - const float r1 = float(r) / subsampling; - array2D I1(w, h); array2D p1(w, h); - f_subsample(I1, guide); + f_subsample(I1, I); + f_subsample(p1, p); - if (&guide == &src) { - f_mean(p1, I1, r1); + DEBUG_DUMP(I); + DEBUG_DUMP(p); + DEBUG_DUMP(I1); + DEBUG_DUMP(p1); - apply(MUL, I1, I1, I1); // I1 = I1 * I1 + float r1 = float(r) / subsampling; - f_mean(I1, I1, r1); + array2D meanI(w, h); + f_mean(meanI, I1, r1); + DEBUG_DUMP(meanI); - apply(SUBMUL, I1, p1, p1, I1); // I1 = I1 - p1 * p1 - apply(DIVEPSILON, I1, I1, I1); // I1 = I1 / (I1 + epsilon) - apply(SUBMUL, p1, I1, p1, p1); // p1 = p1 - I1 * p1 + array2D meanp(w, h); + f_mean(meanp, p1, r1); + DEBUG_DUMP(meanp); - } else { - f_subsample(p1, src); + array2D &corrIp = p1; + apply(MUL, corrIp, I1, p1); + f_mean(corrIp, corrIp, r1); + DEBUG_DUMP(corrIp); - array2D meanI(w, h); - f_mean(meanI, I1, r1); + array2D &corrI = I1; + apply(MUL, corrI, I1, I1); + f_mean(corrI, corrI, r1); + DEBUG_DUMP(corrI); - array2D meanp(w, h); - f_mean(meanp, p1, r1); + array2D &varI = corrI; + apply(SUBMUL, varI, meanI, meanI, corrI); + DEBUG_DUMP(varI); - apply(MUL, p1, I1, p1); + array2D &covIp = corrIp; + apply(SUBMUL, covIp, meanI, meanp, corrIp); + DEBUG_DUMP(covIp); - f_mean(p1, p1, r1); + array2D &a = varI; + apply(DIVEPSILON, a, covIp, varI); + DEBUG_DUMP(a); - apply(MUL, I1, I1, I1); + array2D &b = covIp; + apply(SUBMUL, b, a, meanI, meanp); + DEBUG_DUMP(b); - f_mean(I1, I1, r1); + array2D &meana = a; + f_mean(meana, a, r1); + DEBUG_DUMP(meana); - apply(SUBMUL, I1, meanI, meanI, I1); - apply(SUBMUL, p1, meanI, meanp, p1); - apply(DIVEPSILON, I1, p1, I1); - apply(SUBMUL, p1, I1, meanI, meanp); - } + array2D &meanb = b; + f_mean(meanb, b, r1); + DEBUG_DUMP(meanb); - f_mean(I1, I1, r1); - f_mean(p1, p1, r1); - - const int Ws = I1.width(); - const int Hs = I1.height(); - const int Wd = dst.width(); - const int Hd = dst.height(); - - const float col_scale = static_cast(Ws) / static_cast(Wd); - const float row_scale = static_cast(Hs) / static_cast(Hd); + // speedup by heckflosse67 + const int Ws = meana.width(); + const int Hs = meana.height(); + const int Wd = q.width(); + const int Hd = q.height(); + const float col_scale = float(Ws) / float(Wd); + const float row_scale = float(Hs) / float(Hd); #ifdef _OPENMP - #pragma omp parallel for if (multithread) +# pragma omp parallel for if (multithread) #endif - for (int y = 0; y < Hd; ++y) { - const float ymrs = y * row_scale; + float ymrs = y * row_scale; for (int x = 0; x < Wd; ++x) { - dst[y][x] = getBilinearValue(I1, x * col_scale, ymrs) * guide[y][x] + getBilinearValue(p1, x * col_scale, ymrs); + q[y][x] = getBilinearValue(meana, x * col_scale, ymrs) * I[y][x] + getBilinearValue(meanb, x * col_scale, ymrs); } } } + +void guidedFilterLog(const array2D &guide, float base, array2D &chan, int r, float eps, bool multithread, int subsampling) +{ +#ifdef _OPENMP +# pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < chan.height(); ++y) { + for (int x = 0; x < chan.width(); ++x) { + chan[y][x] = xlin2log(max(chan[y][x], 0.f), base); + } + } + + guidedFilter(guide, chan, chan, r, eps, multithread, subsampling); + +#ifdef _OPENMP +# pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < chan.height(); ++y) { + for (int x = 0; x < chan.width(); ++x) { + chan[y][x] = xlog2lin(max(chan[y][x], 0.f), base); + } + } +} + + +void guidedFilterLog(float base, array2D &chan, int r, float eps, bool multithread, int subsampling) +{ + guidedFilterLog(chan, base, chan, r, eps, multithread, subsampling); +} + } // namespace rtengine + + + + diff --git a/rtengine/guidedfilter.h b/rtengine/guidedfilter.h index 2d8b70369..94147b411 100644 --- a/rtengine/guidedfilter.h +++ b/rtengine/guidedfilter.h @@ -15,7 +15,7 @@ * 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 . + * along with RawTherapee. If not, see . */ #pragma once @@ -28,4 +28,8 @@ namespace rtengine void guidedFilter(const array2D &guide, const array2D &src, array2D &dst, int r, float epsilon, bool multithread, int subsampling=0); +void guidedFilterLog(float base, array2D &chan, int r, float eps, bool multithread, int subsampling=0); + +void guidedFilterLog(const array2D &guide, float base, array2D &chan, int r, float eps, bool multithread, int subsampling=0); + } // namespace rtengine diff --git a/rtengine/hilite_recon.cc b/rtengine/hilite_recon.cc index 1ba42a68b..1a4c4c7f4 100644 --- a/rtengine/hilite_recon.cc +++ b/rtengine/hilite_recon.cc @@ -394,7 +394,9 @@ void RawImageSource::HLRecovery_inpaint(float** red, float** green, float** blue int miny = height - 1; int maxy = 0; +#ifdef _OPENMP #pragma omp parallel for reduction(min:minx,miny) reduction(max:maxx,maxy) schedule(dynamic, 16) +#endif for (int i = 0; i < height; ++i) { for (int j = 0; j< width; ++j) { if (red[i][j] >= max_f[0] || green[i][j] >= max_f[1] || blue[i][j] >= max_f[2]) { diff --git a/rtengine/homogeneouscoordinates.cc b/rtengine/homogeneouscoordinates.cc new file mode 100644 index 000000000..85b189b9f --- /dev/null +++ b/rtengine/homogeneouscoordinates.cc @@ -0,0 +1,191 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Lawrence Lee + * + * 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 . + */ +#include + +#include "homogeneouscoordinates.h" + +namespace rtengine +{ + +template +homogeneous::Vector operator*(const homogeneous::Matrix& a, const homogeneous::Vector& b) +{ + homogeneous::Vector prod; + + prod.fill(0); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + prod[i] += a[i][j] * b[j]; + } + } + + return prod; +} + +template +homogeneous::Matrix operator*(const homogeneous::Matrix& a, const homogeneous::Matrix& b) +{ + homogeneous::Matrix prod; + + for (int i = 0; i < 4; i++) { + prod[i].fill(0); + + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 4; k++) { + prod[i][j] += a[i][k] * b[k][j]; + } + } + } + + return prod; +} + +namespace homogeneous +{ + +template +Matrix projectionMatrix(T location, Axis normal) +{ + Matrix matrix; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + matrix[i][j] = 0; + } + } + + matrix[0][0] = location; + matrix[1][1] = location; + matrix[2][2] = location; + matrix[3][3] = 0; + + switch (normal) { + case X: + matrix[3][0] = 1; + break; + + case Y: + matrix[3][1] = 1; + break; + + case Z: + matrix[3][2] = 1; + break; + } + + return matrix; +} + +template +Matrix rotationMatrix(double radians, Axis axis) +{ + Matrix matrix; + + switch (axis) { + case X: + matrix[0][0] = 1; + matrix[0][1] = 0; + matrix[0][2] = 0; + matrix[1][0] = 0; + matrix[1][1] = cos(radians); + matrix[1][2] = -sin(radians); + matrix[2][0] = 0; + matrix[2][1] = sin(radians); + matrix[2][2] = cos(radians); + break; + + case Y: + matrix[0][0] = cos(radians); + matrix[0][1] = 0; + matrix[0][2] = sin(radians); + matrix[1][0] = 0; + matrix[1][1] = 1; + matrix[1][2] = 0; + matrix[2][0] = -sin(radians); + matrix[2][1] = 0; + matrix[2][2] = cos(radians); + break; + + case Z: + matrix[0][0] = cos(radians); + matrix[0][1] = -sin(radians); + matrix[0][2] = 0; + matrix[1][0] = sin(radians); + matrix[1][1] = cos(radians); + matrix[1][2] = 0; + matrix[2][0] = 0; + matrix[2][1] = 0; + matrix[2][2] = 1; + break; + } + + matrix[0][3] = 0; + matrix[1][3] = 0; + matrix[2][3] = 0; + matrix[3][0] = 0; + matrix[3][1] = 0; + matrix[3][2] = 0; + matrix[3][3] = 1; + + return matrix; +} + +template +Matrix scaleMatrix(T x, T y, T z) +{ + Matrix matrix; + + for (int i = 0; i < 4; i++) { + matrix[i].fill(0); + } + + matrix[0][0] = x; + matrix[1][1] = y; + matrix[2][2] = z; + matrix[3][3] = 1; + + return matrix; +} + +template +Matrix translationMatrix(T x, T y, T z) +{ + Matrix matrix; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + matrix[i][j] = 0; + } + } + + matrix[0][0] = 1; + matrix[1][1] = 1; + matrix[2][2] = 1; + matrix[0][3] = x; + matrix[1][3] = y; + matrix[2][3] = z; + matrix[3][3] = 1; + + return matrix; +} + +} + +} diff --git a/rtengine/homogeneouscoordinates.h b/rtengine/homogeneouscoordinates.h new file mode 100644 index 000000000..12a2227ae --- /dev/null +++ b/rtengine/homogeneouscoordinates.h @@ -0,0 +1,84 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Lawrence Lee + * + * 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 . + */ +#pragma once + +#include + +namespace rtengine +{ + +namespace homogeneous +{ + +enum Axis {X, Y, Z}; + +template +using Matrix = std::array, 4>; + +/** + * 3 dimensional homogeneous vector. + */ +template +using Vector = std::array; + +/** + * Creates a 3 dimensional transformation matrix for projection onto a plane. + * @param location Distance from the origin to the plane. + * @param normal Direction of the plane's normal. + */ +template +Matrix projectionMatrix(T location, Axis normal); + +/** + * Creates a 3 dimensional transformation matrix for rotation. + * @param radians Rotation angle. + * @param axis Axis of rotation. + */ +template +Matrix rotationMatrix(double radians, Axis axis); + +/** + * Creates a 3 dimensional transformation matrix for scaling. + * @param x Scale in x-direction + * @param y Scale in y-direction + * @param z Scale in z-direction + */ +template +Matrix scaleMatrix(T x, T y, T z); + +/** + * Creates a 3 dimensional transformation matrix for translation. + * @param x Translation in the the x-direction. + * @param y Translation in the the y-direction. + * @param z Translation in the the z-direction. + */ +template +Matrix translationMatrix(T x, T y, T z); + +} + +template +homogeneous::Vector operator*(const homogeneous::Matrix& a, const homogeneous::Vector& b); + +template +homogeneous::Matrix operator*(const homogeneous::Matrix& a, const homogeneous::Matrix& b); + +} + +#include "homogeneouscoordinates.cc" diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index a631a6c8d..5025b31e5 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -39,17 +39,17 @@ using namespace rtengine; -extern "C" IptcData *iptc_data_new_from_jpeg_file (FILE* infile); +extern "C" IptcData *iptc_data_new_from_jpeg_file(FILE* infile); namespace { -Glib::ustring to_utf8 (const std::string& str) +Glib::ustring to_utf8(const std::string& str) { try { - return Glib::locale_to_utf8 (str); + return Glib::locale_to_utf8(str); } catch (Glib::Error&) { - return Glib::convert_with_fallback (str, "UTF-8", "ISO-8859-1", "?"); + return Glib::convert_with_fallback(str, "UTF-8", "ISO-8859-1", "?"); } } @@ -71,9 +71,9 @@ T getFromFrame( } -FramesMetaData* FramesMetaData::fromFile (const Glib::ustring& fname, std::unique_ptr rml, bool firstFrameOnly) +FramesMetaData* FramesMetaData::fromFile(const Glib::ustring& fname, std::unique_ptr rml, bool firstFrameOnly) { - return new FramesData (fname, std::move(rml), firstFrameOnly); + return new FramesData(fname, std::move(rml), firstFrameOnly); } FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir) : @@ -113,36 +113,39 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* if (!tag) { newFrameRootDir = rootDir; tag = newFrameRootDir->findTag("Make"); + if (!tag) { // For some raw files (like Canon's CR2 files), the metadata are contained in the first root directory newFrameRootDir = firstRootDir; tag = newFrameRootDir->findTag("Make"); } } + if (tag) { make = tag->valueToString(); + // Same dcraw treatment for (const auto& corp : { - "Canon", - "NIKON", - "EPSON", - "KODAK", - "Kodak", - "OLYMPUS", - "PENTAX", - "RICOH", - "MINOLTA", - "Minolta", - "Konica", - "CASIO", - "Sinar", - "Phase One", - "SAMSUNG", - "Mamiya", - "MOTOROLA", - "Leaf", - "Panasonic" - }) { + "Canon", + "NIKON", + "EPSON", + "KODAK", + "Kodak", + "OLYMPUS", + "PENTAX", + "RICOH", + "MINOLTA", + "Minolta", + "Konica", + "CASIO", + "Sinar", + "Phase One", + "SAMSUNG", + "Mamiya", + "MOTOROLA", + "Leaf", + "Panasonic" + }) { if (make.find(corp) != std::string::npos) { // Simplify company names make = corp; break; @@ -153,6 +156,7 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } tag = newFrameRootDir->findTagUpward("Model"); + if (tag) { model = tag->valueToString(); } @@ -179,7 +183,7 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } - if (model.find( "Digital Camera ") != std::string::npos) { + if (model.find("Digital Camera ") != std::string::npos) { model.erase(0, 15); } } else { @@ -194,8 +198,9 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } tag = newFrameRootDir->findTagUpward("Orientation"); + if (tag) { - orientation = tag->valueToString (); + orientation = tag->valueToString(); } // Look for Rating metadata in the following order: @@ -217,45 +222,47 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* tag = newFrameRootDir->findTagUpward("MakerNote"); rtexif::TagDirectory* mnote = nullptr; + if (tag) { mnote = tag->getDirectory(); } rtexif::TagDirectory* exif = nullptr; tag = newFrameRootDir->findTagUpward("Exif"); + if (tag) { - exif = tag->getDirectory (); + exif = tag->getDirectory(); } if (exif) { // standard exif tags - if ((tag = exif->getTag ("ShutterSpeedValue"))) { - shutter = tag->toDouble (); + if ((tag = exif->getTag("ShutterSpeedValue"))) { + shutter = tag->toDouble(); } - if ((tag = exif->getTag ("ExposureTime"))) { - shutter = tag->toDouble (); + if ((tag = exif->getTag("ExposureTime"))) { + shutter = tag->toDouble(); } - if ((tag = exif->getTag ("ApertureValue"))) { - aperture = tag->toDouble (); + if ((tag = exif->getTag("ApertureValue"))) { + aperture = tag->toDouble(); } - if ((tag = exif->getTag ("FNumber"))) { - aperture = tag->toDouble (); + if ((tag = exif->getTag("FNumber"))) { + aperture = tag->toDouble(); } - if ((tag = exif->getTag ("ExposureBiasValue"))) { - expcomp = tag->toDouble (); + if ((tag = exif->getTag("ExposureBiasValue"))) { + expcomp = tag->toDouble(); } - if ((tag = exif->getTag ("FocalLength"))) { - focal_len = tag->toDouble (); + if ((tag = exif->getTag("FocalLength"))) { + focal_len = tag->toDouble(); } - if ((tag = exif->getTag ("FocalLengthIn35mmFilm"))) { - focal_len35mm = tag->toDouble (); + if ((tag = exif->getTag("FocalLengthIn35mmFilm"))) { + focal_len35mm = tag->toDouble(); } // Focus distance from EXIF or XMP. MakerNote ones are scattered and partly encrypted @@ -283,12 +290,12 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } - if ((tag = exif->getTag ("ISOSpeedRatings"))) { - iso_speed = tag->toDouble (); + if ((tag = exif->getTag("ISOSpeedRatings"))) { + iso_speed = tag->toDouble(); } if ((tag = exif->findTag("DateTimeOriginal", true))) { - if (sscanf ((const char*)tag->getValue(), "%d:%d:%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec) == 6) { + if (sscanf((const char*)tag->getValue(), "%d:%d:%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec) == 6) { time.tm_year -= 1900; time.tm_mon -= 1; time.tm_isdst = -1; @@ -296,10 +303,10 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } - tag = exif->findTag ("SerialNumber"); + tag = exif->findTag("SerialNumber"); - if(!tag) { - tag = exif->findTag ("InternalSerialNumber"); + if (!tag) { + tag = exif->findTag("InternalSerialNumber"); } if (tag) { @@ -311,15 +318,15 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* // Sometimes (e.g. DNG) EXIF already contains lens data - if(!make.compare (0, 8, "FUJIFILM")) { - if(exif->getTag ("LensModel")) { - lens = exif->getTag ("LensModel")->valueToString (); + if (!make.compare(0, 8, "FUJIFILM")) { + if (exif->getTag("LensModel")) { + lens = exif->getTag("LensModel")->valueToString(); } - } else if(!make.compare (0, 4, "SONY")) { + } else if (!make.compare(0, 4, "SONY")) { if (iso_speed == 65535 || iso_speed == 0) { - rtexif::Tag* isoTag = exif->getTag ("RecommendedExposureIndex"); + rtexif::Tag* isoTag = exif->getTag("RecommendedExposureIndex"); - if(isoTag) { + if (isoTag) { iso_speed = isoTag->toDouble(); } } @@ -362,7 +369,7 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* if (mnote) { - if (!make.compare (0, 5, "NIKON")) { + if (!make.compare(0, 5, "NIKON")) { // ISO at max value supported, check manufacturer specific if (iso_speed == 65535 || iso_speed == 0) { rtexif::Tag* isoTag = mnote->getTagP("ISOInfo/ISO"); @@ -374,25 +381,25 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* bool lensOk = false; - if (mnote->getTag ("LensData")) { - std::string ldata = mnote->getTag ("LensData")->valueToString (); + if (mnote->getTag("LensData")) { + std::string ldata = mnote->getTag("LensData")->valueToString(); size_t pos; - if (ldata.size() > 10 && (pos = ldata.find ("Lens = ")) != Glib::ustring::npos) { - lens = ldata.substr (pos + 7); + if (ldata.size() > 10 && (pos = ldata.find("Lens = ")) != Glib::ustring::npos) { + lens = ldata.substr(pos + 7); - if (lens.compare (0, 7, "Unknown")) { + if (lens.compare(0, 7, "Unknown")) { lensOk = true; } else { size_t pos = lens.find("$FL$"); // is there a placeholder for focallength? - if(pos != Glib::ustring::npos) { // then fill in focallength - lens = lens.replace(pos, 4, exif->getTag ("FocalLength")->valueToString ()); + if (pos != Glib::ustring::npos) { // then fill in focallength + lens = lens.replace(pos, 4, exif->getTag("FocalLength")->valueToString()); - if(mnote->getTag ("LensType")) { - std::string ltype = mnote->getTag ("LensType")->valueToString (); + if (mnote->getTag("LensType")) { + std::string ltype = mnote->getTag("LensType")->valueToString(); - if(ltype.find("MF = Yes") != Glib::ustring::npos) { // check, whether it's a MF lens, should be always + if (ltype.find("MF = Yes") != Glib::ustring::npos) { // check, whether it's a MF lens, should be always lens = lens.replace(0, 7, "MF"); } @@ -408,8 +415,8 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } - if (!lensOk && mnote->getTag ("Lens")) { - std::string ldata = mnote->getTag ("Lens")->valueToString (); + if (!lensOk && mnote->getTag("Lens")) { + std::string ldata = mnote->getTag("Lens")->valueToString(); size_t i = 0, j = 0; double n[4] = {0.0}; @@ -429,7 +436,7 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* int den = atoi(ldata.substr(j, i).c_str()); j = i + 2; i += 2; - n[m] = (double) nom / std::max(den,1); + n[m] = (double) nom / std::max(den, 1); } std::ostringstream str; @@ -445,17 +452,17 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* lens = str.str(); // Look whether it's MF or AF - if(mnote->getTag ("LensType")) { - std::string ltype = mnote->getTag ("LensType")->valueToString (); + if (mnote->getTag("LensType")) { + std::string ltype = mnote->getTag("LensType")->valueToString(); - if(ltype.find("MF = Yes") != Glib::ustring::npos) { // check, whether it's a MF lens + if (ltype.find("MF = Yes") != Glib::ustring::npos) { // check, whether it's a MF lens lens = lens.replace(0, 7, "MF"); // replace 'Unknwon' with 'MF' } else { lens = lens.replace(0, 7, "AF"); // replace 'Unknwon' with 'AF' } } } - } else if (!make.compare (0, 5, "Canon")) { + } else if (!make.compare(0, 5, "Canon")) { // ISO at max value supported, check manufacturer specific if (iso_speed == 65535 || iso_speed == 0) { rtexif::Tag* baseIsoTag = mnote->getTagP("CanonShotInfo/BaseISO"); @@ -469,7 +476,7 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* // canon EXIF have a string for lens model rtexif::Tag *lt = mnote->getTag("LensType"); - if ( lt ) { + if (lt) { if (lt->toInt()) { std::string ldata = lt->valueToString (); @@ -491,8 +498,8 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* if( !found || remaining_size < 7U ) { lt = mnote->findTag("LensID"); - if ( lt ) { - std::string ldata = lt->valueToString (); + if (lt) { + std::string ldata = lt->valueToString(); if (ldata.size() > 1) { lens = ldata; @@ -503,14 +510,17 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* // ISO at max value supported, check manufacturer specific if (iso_speed == 65535 || iso_speed == 0) { const rtexif::Tag* const baseIsoTag = mnote->getTag("ISO"); + if (baseIsoTag) { const std::string isoData = baseIsoTag->valueToString(); + if (isoData.size() > 1) { iso_speed = std::stoi(isoData); } } } - if (mnote->getTag ("LensType")) { + + if (mnote->getTag("LensType")) { lens = mnote->getTag ("LensType")->valueToString(); // 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 @@ -522,61 +532,61 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } // Try to get the FocalLength from the LensInfo structure, where length below 10mm will be correctly set - rtexif::Tag* flt = mnote->getTagP ("LensInfo/FocalLength"); + rtexif::Tag* flt = mnote->getTagP("LensInfo/FocalLength"); if (flt) { // Don't replace Exif focal_len if Makernotes focal_len is 0 if (flt->toDouble() > 0) { - focal_len = flt->toDouble (); + focal_len = flt->toDouble(); } } else if ((flt = mnote->getTagP ("FocalLength"))) { focal_len = mnote->getTag("FocalLength")->toDouble (); } - if (mnote->getTag ("FocalLengthIn35mmFilm")) { - focal_len35mm = mnote->getTag ("FocalLengthIn35mmFilm")->toDouble (); + if (mnote->getTag("FocalLengthIn35mmFilm")) { + focal_len35mm = mnote->getTag("FocalLengthIn35mmFilm")->toDouble(); } - } else if (mnote && (!make.compare (0, 4, "SONY") || !make.compare (0, 6, "KONICA"))) { + } else if (!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")) { - rtexif::TagDirectory* eq = mnote->getTag ("Equipment")->getDirectory (); + } else if (!make.compare(0, 7, "OLYMPUS")) { + if (mnote->getTag("Equipment")) { + rtexif::TagDirectory* eq = mnote->getTag("Equipment")->getDirectory(); - if (eq->getTag ("LensType")) { - lens = eq->getTag ("LensType")->valueToString (); + if (eq->getTag("LensType")) { + lens = eq->getTag("LensType")->valueToString(); } } if (lens == "Unknown") { lens_from_make_and_model(); } - } else if (mnote && !make.compare (0, 9, "Panasonic")) { + } else if (!make.compare (0, 9, "Panasonic")) { if (mnote->getTag ("LensType")) { std::string panalens = mnote->getTag("LensType")->valueToString(); if (panalens.find("LUMIX") != Glib::ustring::npos) { lens = "Panasonic " + panalens; - } - else { + } else { lens = panalens; } } } - } else if (exif->getTag ("DNGLensInfo")) { - lens = exif->getTag ("DNGLensInfo")->valueToString (); + } else if (exif->getTag("DNGLensInfo")) { + lens = exif->getTag("DNGLensInfo")->valueToString(); } else if (!lens_from_make_and_model() && exif->getTag ("LensInfo")) { - lens = exif->getTag ("LensInfo")->valueToString (); + lens = exif->getTag("LensInfo")->valueToString(); } } } rtexif::Tag* t = newFrameRootDir->getTag(0x83BB); + if (t) { - iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ()); + iptc = iptc_data_new_from_data((unsigned char*)t->getValue(), (unsigned)t->getValueSize()); } @@ -590,8 +600,9 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* const rtexif::Tag* const pi = frameRootDir->findTag("PhotometricInterpretation"); const rtexif::Tag* const c = frameRootDir->findTag("Compression"); - if (mnote && (!make.compare (0, 6, "PENTAX") || (!make.compare (0, 5, "RICOH") && !model.compare (0, 6, "PENTAX")))) { + if (mnote && (!make.compare(0, 6, "PENTAX") || (!make.compare(0, 5, "RICOH") && !model.compare(0, 6, "PENTAX")))) { const rtexif::Tag* const hdr = mnote->findTag("HDR"); + if (hdr) { if (hdr->toInt() > 0) { isHDR = true; @@ -601,10 +612,12 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } else { const rtexif::Tag* const dm = mnote->findTag("DriveMode"); + if (dm) { char buffer[60]; dm->toString(buffer, 3); buffer[3] = 0; + if (!strcmp(buffer, "HDR")) { isHDR = true; #if PRINT_HDR_PS_DETECTION @@ -652,6 +665,7 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* samplesperpixel = spp->toInt(); photometric = pi->toInt(); + if (photometric == PHOTOMETRIC_LOGLUV) { if (!c) { compression = COMPRESSION_NONE; @@ -732,7 +746,8 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* sampleFormat = IIOSF_UNSIGNED_CHAR; } else if (bitspersample <= 16) { sampleFormat = IIOSF_UNSIGNED_SHORT; - if (mnote && (!make.compare (0, 4, "SONY")) && bitspersample >= 12 && samplesperpixel == 4) { + + if (mnote && (!make.compare(0, 4, "SONY")) && bitspersample >= 12 && samplesperpixel == 4) { isPixelShift = true; #if PRINT_HDR_PS_DETECTION printf("PixelShift detected ! -> \"Make\" = SONY, bitsPerPixel > 8, samplesPerPixel == 4\n"); @@ -757,20 +772,20 @@ FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* } } -FrameData::~FrameData () +FrameData::~FrameData() { if (iptc) { - iptc_data_free (iptc); + iptc_data_free(iptc); } } -procparams::IPTCPairs FrameData::getIPTCData () const +procparams::IPTCPairs FrameData::getIPTCData() const { return getIPTCData(iptc); } -procparams::IPTCPairs FrameData::getIPTCData (IptcData* iptc_) +procparams::IPTCPairs FrameData::getIPTCData(IptcData* iptc_) { procparams::IPTCPairs iptcc; @@ -782,34 +797,34 @@ procparams::IPTCPairs FrameData::getIPTCData (IptcData* iptc_) unsigned char buffer[2100]; for (int i = 0; i < 16; i++) { - IptcDataSet* ds = iptc_data_get_next_dataset (iptc_, nullptr, IPTC_RECORD_APP_2, strTags[i].tag); + IptcDataSet* ds = iptc_data_get_next_dataset(iptc_, nullptr, IPTC_RECORD_APP_2, strTags[i].tag); if (ds) { - iptc_dataset_get_data (ds, buffer, 2100); + iptc_dataset_get_data(ds, buffer, 2100); std::vector icValues; - icValues.push_back (to_utf8((char*)buffer)); + icValues.push_back(to_utf8((char*)buffer)); iptcc[strTags[i].field] = icValues; - iptc_dataset_unref (ds); + iptc_dataset_unref(ds); } } IptcDataSet* ds = nullptr; std::vector keywords; - while ((ds = iptc_data_get_next_dataset (iptc_, ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS))) { - iptc_dataset_get_data (ds, buffer, 2100); - keywords.push_back (to_utf8((char*)buffer)); + while ((ds = iptc_data_get_next_dataset(iptc_, ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS))) { + iptc_dataset_get_data(ds, buffer, 2100); + keywords.push_back(to_utf8((char*)buffer)); } iptcc["Keywords"] = keywords; ds = nullptr; std::vector suppCategories; - while ((ds = iptc_data_get_next_dataset (iptc_, ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY))) { - iptc_dataset_get_data (ds, buffer, 2100); - suppCategories.push_back (to_utf8((char*)buffer)); - iptc_dataset_unref (ds); + while ((ds = iptc_data_get_next_dataset(iptc_, ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY))) { + iptc_dataset_get_data(ds, buffer, 2100); + suppCategories.push_back(to_utf8((char*)buffer)); + iptc_dataset_unref(ds); } iptcc["SupplementalCategories"] = suppCategories; @@ -817,11 +832,11 @@ procparams::IPTCPairs FrameData::getIPTCData (IptcData* iptc_) } -bool FrameData::getPixelShift () const +bool FrameData::getPixelShift() const { return isPixelShift; } -bool FrameData::getHDR () const +bool FrameData::getHDR() const { return isHDR; } @@ -829,75 +844,75 @@ std::string FrameData::getImageType () const { return isPixelShift ? "PS" : isHDR ? "HDR" : "STD"; } -IIOSampleFormat FrameData::getSampleFormat () const +IIOSampleFormat FrameData::getSampleFormat() const { return sampleFormat; } -rtexif::TagDirectory* FrameData::getExifData () const +rtexif::TagDirectory* FrameData::getExifData() const { return frameRootDir; } -bool FrameData::hasExif () const +bool FrameData::hasExif() const { return frameRootDir && frameRootDir->getCount(); } -bool FrameData::hasIPTC () const +bool FrameData::hasIPTC() const { return iptc; } -tm FrameData::getDateTime () const +tm FrameData::getDateTime() const { return time; } -time_t FrameData::getDateTimeAsTS () const +time_t FrameData::getDateTimeAsTS() const { return timeStamp; } -int FrameData::getISOSpeed () const +int FrameData::getISOSpeed() const { return iso_speed; } -double FrameData::getFNumber () const +double FrameData::getFNumber() const { return aperture; } -double FrameData::getFocalLen () const +double FrameData::getFocalLen() const { return focal_len; } -double FrameData::getFocalLen35mm () const +double FrameData::getFocalLen35mm() const { return focal_len35mm; } -float FrameData::getFocusDist () const +float FrameData::getFocusDist() const { return focus_dist; } -double FrameData::getShutterSpeed () const +double FrameData::getShutterSpeed() const { return shutter; } -double FrameData::getExpComp () const +double FrameData::getExpComp() const { return expcomp; } -std::string FrameData::getMake () const +std::string FrameData::getMake() const { return make; } -std::string FrameData::getModel () const +std::string FrameData::getModel() const { return model; } -std::string FrameData::getLens () const +std::string FrameData::getLens() const { return lens; } -std::string FrameData::getSerialNumber () const +std::string FrameData::getSerialNumber() const { return serial; } -std::string FrameData::getOrientation () const +std::string FrameData::getOrientation() const { return orientation; } @@ -909,17 +924,17 @@ int FrameData::getRating () const -void FramesData::setDCRawFrameCount (unsigned int frameCount) +void FramesData::setDCRawFrameCount(unsigned int frameCount) { dcrawFrameCount = frameCount; } -unsigned int FramesData::getRootCount () const +unsigned int FramesData::getRootCount() const { return roots.size(); } -unsigned int FramesData::getFrameCount () const +unsigned int FramesData::getFrameCount() const { return dcrawFrameCount ? dcrawFrameCount : frames.size(); } @@ -933,14 +948,14 @@ bool FramesData::getPixelShift () const return frames.empty() ? false : frames.at(0)->getPixelShift (); } -bool FramesData::getHDR (unsigned int frame) const +bool FramesData::getHDR(unsigned int frame) const { // So far only Pentax provides multi-frame HDR file. // Only the first frame contains the HDR tag // If more brand have to be supported, this rule may need // to evolve - return frames.empty() || frame >= frames.size() ? false : frames.at(0)->getHDR (); + return frames.empty() || frame >= frames.size() ? false : frames.at(0)->getHDR(); } std::string FramesData::getImageType (unsigned int frame) const @@ -948,53 +963,58 @@ std::string FramesData::getImageType (unsigned int frame) const return frames.empty() || frame >= frames.size() ? "STD" : frames.at(0)->getImageType(); } -IIOSampleFormat FramesData::getSampleFormat (unsigned int frame) const +IIOSampleFormat FramesData::getSampleFormat(unsigned int frame) const { - return frames.empty() || frame >= frames.size() ? IIOSF_UNKNOWN : frames.at(frame)->getSampleFormat (); + return frames.empty() || frame >= frames.size() ? IIOSF_UNKNOWN : frames.at(frame)->getSampleFormat(); } -rtexif::TagDirectory* FramesData::getFrameExifData (unsigned int frame) const +rtexif::TagDirectory* FramesData::getFrameExifData(unsigned int frame) const { - return frames.empty() || frame >= frames.size() ? nullptr : frames.at(frame)->getExifData (); + return frames.empty() || frame >= frames.size() ? nullptr : frames.at(frame)->getExifData(); } -rtexif::TagDirectory* FramesData::getBestExifData (ImageSource *imgSource, procparams::RAWParams *rawParams) const +rtexif::TagDirectory* FramesData::getBestExifData(ImageSource *imgSource, procparams::RAWParams *rawParams) const { rtexif::TagDirectory *td = nullptr; + if (frames.empty()) { return nullptr; } + if (imgSource && rawParams) { eSensorType sensorType = imgSource->getSensorType(); unsigned int imgNum = 0; + if (sensorType == ST_BAYER) { imgNum = rtengine::LIM(rawParams->bayersensor.imageNum, 0, frames.size() - 1); - /* - // might exist someday ? - } else if (sensorType == ST_FUJI_XTRANS) { - imgNum = rtengine::LIM(rawParams->xtranssensor.imageNum, 0, frames.size() - 1); - } else if (sensorType == ST_NONE && !imgSource->isRAW()) { - // standard image multiframe support should come here (when implemented in GUI) - */ + /* + // might exist someday ? + } else if (sensorType == ST_FUJI_XTRANS) { + imgNum = rtengine::LIM(rawParams->xtranssensor.imageNum, 0, frames.size() - 1); + } else if (sensorType == ST_NONE && !imgSource->isRAW()) { + // standard image multiframe support should come here (when implemented in GUI) + */ } - td = getFrameExifData (imgNum); + td = getFrameExifData(imgNum); rtexif::Tag* makeTag; + if (td && (makeTag = td->findTag("Make", true))) { td = makeTag->getParent(); } else { td = getRootExifData(0); } } + return td; } -rtexif::TagDirectory* FramesData::getRootExifData (unsigned int root) const +rtexif::TagDirectory* FramesData::getRootExifData(unsigned int root) const { return roots.empty() || root >= roots.size() ? nullptr : roots.at(root); } -procparams::IPTCPairs FramesData::getIPTCData (unsigned int frame) const +procparams::IPTCPairs FramesData::getIPTCData(unsigned int frame) const { if (frame < frames.size() && frames.at(frame)->hasIPTC()) { return frames.at(frame)->getIPTCData(); @@ -1214,62 +1234,62 @@ int FramesData::getRating(unsigned int frame) const //------inherited functions--------------// -std::string FramesMetaData::apertureToString (double aperture) +std::string FramesMetaData::apertureToString(double aperture) { char buffer[256]; - sprintf (buffer, "%0.1f", aperture); + sprintf(buffer, "%0.1f", aperture); return buffer; } -std::string FramesMetaData::shutterToString (double shutter) +std::string FramesMetaData::shutterToString(double shutter) { char buffer[256]; if (shutter > 0.0 && shutter <= 0.5) { - sprintf (buffer, "1/%0.0f", 1.0 / shutter); + sprintf(buffer, "1/%0.0f", 1.0 / shutter); } else { - sprintf (buffer, "%0.1f", shutter); + sprintf(buffer, "%0.1f", shutter); } return buffer; } -std::string FramesMetaData::expcompToString (double expcomp, bool maskZeroexpcomp) +std::string FramesMetaData::expcompToString(double expcomp, bool maskZeroexpcomp) { char buffer[256]; if (maskZeroexpcomp) { if (expcomp != 0.0) { - sprintf (buffer, "%0.2f", expcomp); + sprintf(buffer, "%0.2f", expcomp); return buffer; } else { return ""; } } else { - sprintf (buffer, "%0.2f", expcomp); + sprintf(buffer, "%0.2f", expcomp); return buffer; } } -double FramesMetaData::shutterFromString (std::string s) +double FramesMetaData::shutterFromString(std::string s) { - size_t i = s.find_first_of ('/'); + size_t i = s.find_first_of('/'); if (i == std::string::npos) { - return atof (s.c_str()); + return atof(s.c_str()); } else { - return atof (s.substr(0, i).c_str()) / atof (s.substr(i + 1).c_str()); + return atof(s.substr(0, i).c_str()) / atof(s.substr(i + 1).c_str()); } } -double FramesMetaData::apertureFromString (std::string s) +double FramesMetaData::apertureFromString(std::string s) { - return atof (s.c_str()); + return atof(s.c_str()); } extern "C" { @@ -1285,7 +1305,7 @@ extern "C" { }; IptcData * - iptc_data_new_from_jpeg_file (FILE *infile) + iptc_data_new_from_jpeg_file(FILE *infile) { IptcData *d; unsigned char * buf; @@ -1297,52 +1317,52 @@ extern "C" { return nullptr; } - d = iptc_data_new (); + d = iptc_data_new(); if (!d) { return nullptr; } - buf = (unsigned char*)iptc_mem_alloc (d->priv->mem, buf_len); + buf = (unsigned char*)iptc_mem_alloc(d->priv->mem, buf_len); if (!buf) { - iptc_data_unref (d); + iptc_data_unref(d); return nullptr; } - len = iptc_jpeg_read_ps3 (infile, buf, buf_len); + len = iptc_jpeg_read_ps3(infile, buf, buf_len); if (len <= 0) { goto failure; } - offset = iptc_jpeg_ps3_find_iptc (buf, len, &iptc_len); + offset = iptc_jpeg_ps3_find_iptc(buf, len, &iptc_len); if (offset <= 0) { goto failure; } - iptc_data_load (d, buf + offset, iptc_len); + iptc_data_load(d, buf + offset, iptc_len); - iptc_mem_free (d->priv->mem, buf); + iptc_mem_free(d->priv->mem, buf); return d; failure: - iptc_mem_free (d->priv->mem, buf); - iptc_data_unref (d); + iptc_mem_free(d->priv->mem, buf); + iptc_data_unref(d); return nullptr; } } -FramesData::FramesData (const Glib::ustring& fname, std::unique_ptr rml, bool firstFrameOnly) : - iptc(nullptr), dcrawFrameCount (0) +FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr rml, bool firstFrameOnly) : + iptc(nullptr), dcrawFrameCount(0) { if (rml && (rml->exifBase >= 0 || rml->ciffBase >= 0)) { - FILE* f = g_fopen (fname.c_str (), "rb"); + FILE* f = g_fopen(fname.c_str(), "rb"); if (f) { - rtexif::ExifManager exifManager (f, std::move(rml), firstFrameOnly); + rtexif::ExifManager exifManager(f, std::move(rml), firstFrameOnly); if (exifManager.f && exifManager.rml) { if (exifManager.rml->exifBase >= 0) { exifManager.parseRaw (); @@ -1358,6 +1378,7 @@ FramesData::FramesData (const Glib::ustring& fname, std::unique_ptr(new FrameData(currFrame, currFrame->getRoot(), roots.at(0)))); } + for (auto currRoot : roots) { rtexif::Tag* t = currRoot->getTag(0x83BB); @@ -1367,29 +1388,34 @@ FramesData::FramesData (const Glib::ustring& fname, std::unique_ptr(new FrameData(currFrame, currFrame->getRoot(), roots.at(0)))); } - rewind (exifManager.f); // Not sure this is necessary - iptc = iptc_data_new_from_jpeg_file (exifManager.f); + + rewind(exifManager.f); // Not sure this is necessary + iptc = iptc_data_new_from_jpeg_file(exifManager.f); } - fclose (f); + + fclose(f); } } else if (hasTiffExtension(fname)) { - FILE* f = g_fopen (fname.c_str (), "rb"); + FILE* f = g_fopen(fname.c_str(), "rb"); if (f) { - rtexif::ExifManager exifManager (f, std::move(rml), firstFrameOnly); + rtexif::ExifManager exifManager(f, std::move(rml), firstFrameOnly); exifManager.parseTIFF(); roots = exifManager.roots; @@ -1398,26 +1424,28 @@ FramesData::FramesData (const Glib::ustring& fname, 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 ()); + iptc = iptc_data_new_from_data((unsigned char*)t->getValue(), (unsigned)t->getValueSize()); break; } } - fclose (f); + + fclose(f); } } } -FramesData::~FramesData () +FramesData::~FramesData() { for (auto currRoot : roots) { delete currRoot; } if (iptc) { - iptc_data_free (iptc); + iptc_data_free(iptc); } } diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index fce181c3f..aa18f1265 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -1483,6 +1483,19 @@ int ImageIO::saveTIFF (const Glib::ustring &fname, int bps, bool isFloat, bool u TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_ADOBE_DEFLATE); TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, (bps == 16 || bps == 32) && isFloat ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); + [out]() + { + const std::vector default_tags = rtexif::ExifManager::getDefaultTIFFTags(nullptr); + + TIFFSetField (out, TIFFTAG_XRESOLUTION, default_tags[2]->toDouble()); + TIFFSetField (out, TIFFTAG_YRESOLUTION, default_tags[3]->toDouble()); + TIFFSetField (out, TIFFTAG_RESOLUTIONUNIT, default_tags[4]->toInt()); + + for (auto default_tag : default_tags) { + delete default_tag; + } + }(); + if (!uncompressed) { TIFFSetField (out, TIFFTAG_PREDICTOR, (bps == 16 || bps == 32) && isFloat ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL); } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 5261675a0..019486198 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -26,6 +26,7 @@ #include "cieimage.h" #include "color.h" #include "colortemp.h" +#include "jaggedarray.h" #include "curves.h" #include "dcp.h" #include "iccstore.h" @@ -153,6 +154,7 @@ ImProcCoordinator::ImProcCoordinator() : pdSharpenAutoRadiusListener(nullptr), frameCountListener(nullptr), imageTypeListener(nullptr), + filmNegListener(nullptr), actListener(nullptr), adnListener(nullptr), awavListener(nullptr), @@ -181,7 +183,121 @@ ImProcCoordinator::ImProcCoordinator() : highQualityComputed(false), customTransformIn(nullptr), customTransformOut(nullptr), - ipf(params.get(), true) + ipf(params.get(), true), + + // Locallab + locallListener(nullptr), + reserv(nullptr), + lastorigimp(nullptr), + coordX(0), coordY(0), localX(0), localY(0), + lllocalcurve(65536, 0), + cllocalcurve(65536, 0), + lclocalcurve(65536, 0), + cclocalcurve(65536, 0), + rgblocalcurve(65536, 0), + exlocalcurve(65536, 0), + hltonecurveloc(65536, 0), //32768 + shtonecurveloc(65536, 0), + tonecurveloc(65536, 0), + lightCurveloc(32770, 0), + lmasklocalcurve(65536, 0), + lmaskexplocalcurve(65536, 0), + lmaskSHlocalcurve(65536, 0), + lmaskviblocalcurve(65536, 0), + lmasktmlocalcurve(65536, 0), + lmaskretilocalcurve(65536, 0), + lmaskcblocalcurve(65536, 0), + lmaskbllocalcurve(65536, 0), + lmasklclocalcurve(65536, 0), + locallutili(false), + localclutili(false), + locallcutili(false), + localcutili(false), + localrgbutili(false), + localexutili(false), + llmasutili(false), + lhmasutili(false), + lhhmasutili(false), + lcmasutili(false), + localmaskutili(false), + localmaskexputili(false), + localmaskSHutili(false), + localmaskvibutili(false), + localmasktmutili(false), + localmaskretiutili(false), + localmaskcbutili(false), + localmaskblutili(false), + localmasklcutili(false), + lcmasexputili(false), + lhmasexputili(false), + llmasexputili(false), + lcmasSHutili(false), + lhmasSHutili(false), + llmasSHutili(false), + lcmasvibutili(false), + lhmasvibutili(false), + llmasvibutili(false), + lcmaslcutili(false), + lhmaslcutili(false), + llmaslcutili(false), + lcmascbutili(false), + lhmascbutili(false), + llmascbutili(false), + lcmasretiutili(false), + lhmasretiutili(false), + llmasretiutili(false), + lcmastmutili(false), + lhmastmutili(false), + llmastmutili(false), + lcmasblutili(false), + lhmasblutili(false), + llmasblutili(false), + locwavutili(false), + locwavdenutili(false), + loclevwavutili(false), + locconwavutili(false), + loccompwavutili(false), + loccomprewavutili(false), + locedgwavutili(false), + lmasutiliblwav(false), + lmasutilicolwav(false), + LHutili(false), + HHutili(false), + lastsavrests(500, -10000), + huerefs(500, -100000.f), + huerefblurs(500, -100000.f), + chromarefblurs(500, -100000.f), + lumarefblurs(500, -100000.f), + chromarefs(500, -100000.f), + lumarefs(500, -100000.f), + sobelrefs(500, -100000.f), + avgs(500, -100000.f), + huer(0), + huerblu(0), + chromarblu(0), + lumarblu(0), + chromar(0), + lumar(0), + sobeler(0), + lastsav(0), + avg(0), + lastspotdup(false), + previewDeltaE(false), + locallColorMask(0), + locallColorMaskinv(0), + locallExpMask(0), + locallExpMaskinv(0), + locallSHMask(0), + locallSHMaskinv(0), + locallvibMask(0), + localllcMask(0), + locallcbMask(0), + locallretiMask(0), + locallsoftMask(0), + localltmMask(0), + locallblMask(0), + locallsharMask(0), + retistrsav(nullptr) { } @@ -245,6 +361,7 @@ DetailedCrop* ImProcCoordinator::createCrop(::EditDataProvider *editDataProvider // todo: bitmask containing desired actions, taken from changesSinceLast void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) { + // TODO Locallab printf MyMutex::MyLock processingLock(mProcessing); @@ -377,6 +494,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) xtransAutoContrastListener->autoContrastChanged(contrastThreshold); } + // if a demosaic happened we should also call getimage later, so we need to set the M_INIT flag todo |= (M_INIT | M_CSHARP); @@ -386,9 +504,11 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) double pdSharpencontrastThreshold = params->pdsharpening.contrast; double pdSharpenRadius = params->pdsharpening.deconvradius; imgsrc->captureSharpening(params->pdsharpening, sharpMask, pdSharpencontrastThreshold, pdSharpenRadius); + if (pdSharpenAutoContrastListener && params->pdsharpening.autoContrast) { pdSharpenAutoContrastListener->autoContrastChanged(pdSharpencontrastThreshold); } + if (pdSharpenAutoRadiusListener && params->pdsharpening.autoRadius) { pdSharpenAutoRadiusListener->autoRadiusChanged(pdSharpenRadius); } @@ -605,8 +725,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) fattal_11_dcrop_cache = nullptr; } - ipf.dehaze(orig_prev); - ipf.ToneMapFattal02(orig_prev); + ipf.dehaze(orig_prev, params->dehaze); + ipf.ToneMapFattal02(orig_prev, params->fattal, 3, 0, nullptr, 0, 0, 0); if (oprevi != orig_prev) { delete oprevi; @@ -674,6 +794,101 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) aeListener->autoMatchedToneCurveChanged(params->toneCurve.curveMode, params->toneCurve.curve); } } + + // Encoding log with locallab + if (params->locallab.enabled) { + const int sizespot = (int)params->locallab.spots.size(); + + float *sourceg = nullptr; + sourceg = new float[sizespot]; + float *targetg = nullptr; + targetg = new float[sizespot]; + bool *log = nullptr; + log = new bool[sizespot]; + bool *autocomput = nullptr; + autocomput = new bool[sizespot]; + float *blackev = nullptr; + blackev = new float[sizespot]; + float *whiteev = nullptr; + whiteev = new float[sizespot]; + bool *Autogr = nullptr; + Autogr = new bool[sizespot]; + + float *locx = nullptr; + locx = new float[sizespot]; + float *locy = nullptr; + locy = new float[sizespot]; + float *locxL = nullptr; + locxL = new float[sizespot]; + float *locyT = nullptr; + locyT = new float[sizespot]; + float *centx = nullptr; + centx = new float[sizespot]; + float *centy = nullptr; + centy = new float[sizespot]; + + for (int sp = 0; sp < sizespot; sp++) { + log[sp] = params->locallab.spots.at(sp).explog; + autocomput[sp] = params->locallab.spots.at(sp).autocompute; + blackev[sp] = params->locallab.spots.at(sp).blackEv; + whiteev[sp] = params->locallab.spots.at(sp).whiteEv; + sourceg[sp] = params->locallab.spots.at(sp).sourceGray; + Autogr[sp] = params->locallab.spots.at(sp).Autogray; + targetg[sp] = params->locallab.spots.at(sp).targetGray; + locx[sp] = params->locallab.spots.at(sp).loc.at(0) / 2000.0; + locy[sp] = params->locallab.spots.at(sp).loc.at(2) / 2000.0; + locxL[sp] = params->locallab.spots.at(sp).loc.at(1) / 2000.0; + locyT[sp] = params->locallab.spots.at(sp).loc.at(3) / 2000.0; + centx[sp] = params->locallab.spots.at(sp).centerX / 2000.0 + 0.5; + centy[sp] = params->locallab.spots.at(sp).centerY / 2000.0 + 0.5; + + const bool fullim = params->locallab.spots.at(sp).fullimage; + + if (log[sp] && autocomput[sp]) { + constexpr int SCALE = 10; + int fw, fh, tr = TR_NONE; + imgsrc->getFullSize(fw, fh, tr); + PreviewProps pp(0, 0, fw, fh, SCALE); + + float ysta = std::max(static_cast(centy[sp] - locyT[sp]), 0.f); + float yend = std::min(static_cast(centy[sp] + locy[sp]), 1.f); + float xsta = std::max(static_cast(centx[sp] - locxL[sp]), 0.f); + float xend = std::min(static_cast(centx[sp] + locx[sp]), 1.f); + + if (fullim) { + ysta = 0.f; + yend = 1.f; + xsta = 0.f; + xend = 1.f; + } + + ipf.getAutoLogloc(sp, imgsrc, sourceg, blackev, whiteev, Autogr, fw, fh, xsta, xend, ysta, yend, SCALE); + + params->locallab.spots.at(sp).blackEv = blackev[sp]; + params->locallab.spots.at(sp).whiteEv = whiteev[sp]; + params->locallab.spots.at(sp).sourceGray = sourceg[sp]; + + if (locallListener) { + locallListener->logencodChanged(blackev[sp], whiteev[sp], sourceg[sp], targetg[sp]); + } + } + } + + delete [] locx; + delete [] locy; + delete [] locxL; + delete [] locyT; + delete [] centx; + delete [] centy; + + delete [] Autogr; + delete [] whiteev; + delete [] blackev; + delete [] targetg; + delete [] sourceg; + delete [] log; + delete [] autocomput; + } } if (todo & (M_AUTOEXP | M_RGBCURVE)) { @@ -822,11 +1037,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) // correct GUI black and white with value } + // ipf.Lab_Tile(oprevl, oprevl, scale); + // compute L channel histogram int x1, y1, x2, y2; params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2); } +// lhist16(32768); if (todo & (M_LUMACURVE | M_CROP)) { LUTu lhist16(32768); lhist16.clear(); @@ -866,13 +1084,358 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) params->labCurve.lccurve, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, scale == 1 ? 1 : 16); } - if (todo & (M_LUMINANCE + M_COLOR)) { + //scale = 1; + + if ((todo & (M_LUMINANCE + M_COLOR)) || (todo & M_AUTOEXP)) { nprevl->CopyFrom(oprevl); + reserv->CopyFrom(oprevl); + lastorigimp->CopyFrom(oprevl); + + // int maxspot = 1; + //************************************************************* + // locallab + //************************************************************* + + if (params->locallab.enabled) { + /* + * 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 . + * 2017 2018 Jacques Desmis + * 2019 Pierre Cabrera + */ + + float **shbuffer = nullptr; + int sca = 1; + double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; + float avge; + std::vector locallref; + std::vector locallretiminmax; + + for (int sp = 0; sp < (int)params->locallab.spots.size(); sp++) { + // Set local curves of current spot to LUT + LHutili = false; + HHutili = false; + locallutili = false; + localclutili = false; + locallcutili = false; + localexutili = false; + localrgbutili = false; + localcutili = false; + llmasutili = false; + lhmasutili = false; + lhhmasutili = false; + lcmasutili = false; + localmaskutili = false; + lcmasexputili = false; + lhmasexputili = false; + llmasexputili = false; + localmaskexputili = false; + localmaskSHutili = false; + localmaskvibutili = false; + localmasktmutili = false; + localmaskretiutili = false; + localmaskcbutili = false; + localmaskblutili = false; + localmasklcutili = false; + lcmasSHutili = false; + lhmasSHutili = false; + llmasSHutili = false; + lcmasvibutili = false; + lhmasvibutili = false; + llmasvibutili = false; + lcmascbutili = false; + lhmascbutili = false; + llmascbutili = false; + lcmaslcutili = false; + lhmaslcutili = false; + llmaslcutili = false; + lcmasretiutili = false; + lhmasretiutili = false; + llmasretiutili = false; + lcmastmutili = false; + lhmastmutili = false; + llmastmutili = false; + lcmasblutili = false; + lhmasblutili = false; + llmasblutili = false; + lcmasutili = false; + locwavutili = false; + locwavdenutili = false; + loclevwavutili = false; + locconwavutili = false; + loccompwavutili = false; + loccomprewavutili = false; + locedgwavutili = false; + lmasutiliblwav = false; + lmasutilicolwav = false; + locRETgainCurve.Set(params->locallab.spots.at(sp).localTgaincurve); + locRETtransCurve.Set(params->locallab.spots.at(sp).localTtranscurve); + loclhCurve.Set(params->locallab.spots.at(sp).LHcurve, LHutili); + lochhCurve.Set(params->locallab.spots.at(sp).HHcurve, HHutili); + locccmasCurve.Set(params->locallab.spots.at(sp).CCmaskcurve, lcmasutili); + locllmasCurve.Set(params->locallab.spots.at(sp).LLmaskcurve, llmasutili); + lochhmasCurve.Set(params->locallab.spots.at(sp).HHmaskcurve, lhmasutili); + lochhhmasCurve.Set(params->locallab.spots.at(sp).HHhmaskcurve, lhhmasutili); + locllmasexpCurve.Set(params->locallab.spots.at(sp).LLmaskexpcurve, llmasexputili); + locccmasexpCurve.Set(params->locallab.spots.at(sp).CCmaskexpcurve, lcmasexputili); + lochhmasexpCurve.Set(params->locallab.spots.at(sp).HHmaskexpcurve, lhmasexputili); + locllmasSHCurve.Set(params->locallab.spots.at(sp).LLmaskSHcurve, llmasSHutili); + locccmasSHCurve.Set(params->locallab.spots.at(sp).CCmaskSHcurve, lcmasSHutili); + lochhmasSHCurve.Set(params->locallab.spots.at(sp).HHmaskSHcurve, lhmasSHutili); + locllmasvibCurve.Set(params->locallab.spots.at(sp).LLmaskvibcurve, llmasvibutili); + locccmasvibCurve.Set(params->locallab.spots.at(sp).CCmaskvibcurve, lcmasvibutili); + lochhmasvibCurve.Set(params->locallab.spots.at(sp).HHmaskvibcurve, lhmasvibutili); + locllmascbCurve.Set(params->locallab.spots.at(sp).LLmaskcbcurve, llmascbutili); + locccmascbCurve.Set(params->locallab.spots.at(sp).CCmaskcbcurve, lcmascbutili); + lochhmascbCurve.Set(params->locallab.spots.at(sp).HHmaskcbcurve, lhmascbutili); + locllmaslcCurve.Set(params->locallab.spots.at(sp).LLmasklccurve, llmaslcutili); + locccmaslcCurve.Set(params->locallab.spots.at(sp).CCmasklccurve, lcmaslcutili); + lochhmaslcCurve.Set(params->locallab.spots.at(sp).HHmasklccurve, lhmaslcutili); + locllmasretiCurve.Set(params->locallab.spots.at(sp).LLmaskreticurve, llmasretiutili); + locccmasretiCurve.Set(params->locallab.spots.at(sp).CCmaskreticurve, lcmasretiutili); + lochhmasretiCurve.Set(params->locallab.spots.at(sp).HHmaskreticurve, lhmasretiutili); + locllmastmCurve.Set(params->locallab.spots.at(sp).LLmasktmcurve, llmastmutili); + locccmastmCurve.Set(params->locallab.spots.at(sp).CCmasktmcurve, lcmastmutili); + lochhmastmCurve.Set(params->locallab.spots.at(sp).HHmasktmcurve, lhmastmutili); + locllmasblCurve.Set(params->locallab.spots.at(sp).LLmaskblcurve, llmasblutili); + locccmasblCurve.Set(params->locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); + lochhmasblCurve.Set(params->locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); + loclmasCurveblwav.Set(params->locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); + loclmasCurvecolwav.Set(params->locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); + locwavCurve.Set(params->locallab.spots.at(sp).locwavcurve, locwavutili); + loclevwavCurve.Set(params->locallab.spots.at(sp).loclevwavcurve, loclevwavutili); + locconwavCurve.Set(params->locallab.spots.at(sp).locconwavcurve, locconwavutili); + loccompwavCurve.Set(params->locallab.spots.at(sp).loccompwavcurve, loccompwavutili); + loccomprewavCurve.Set(params->locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); + locwavCurveden.Set(params->locallab.spots.at(sp).locwavcurveden, locwavdenutili); + locedgwavCurve.Set(params->locallab.spots.at(sp).locedgwavcurve, locedgwavutili); + CurveFactory::curveLocal(locallutili, params->locallab.spots.at(sp).llcurve, lllocalcurve, sca); + CurveFactory::curveLocal(localclutili, params->locallab.spots.at(sp).clcurve, cllocalcurve, sca); + CurveFactory::curveLocal(locallcutili, params->locallab.spots.at(sp).lccurve, lclocalcurve, sca); + CurveFactory::curveCCLocal(localcutili, params->locallab.spots.at(sp).cccurve, cclocalcurve, sca); + CurveFactory::curveLocal(localrgbutili, params->locallab.spots.at(sp).rgbcurve, rgblocalcurve, sca); + CurveFactory::curveexLocal(localexutili, params->locallab.spots.at(sp).excurve, exlocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskutili, params->locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskexputili, params->locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskSHutili, params->locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskvibutili, params->locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, sca); + CurveFactory::curvemaskLocal(localmasktmutili, params->locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskretiutili, params->locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskcbutili, params->locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, sca); + CurveFactory::curvemaskLocal(localmaskblutili, params->locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, sca); + CurveFactory::curvemaskLocal(localmasklcutili, params->locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, sca); + double ecomp = params->locallab.spots.at(sp).expcomp; + double black = params->locallab.spots.at(sp).black; + double hlcompr = params->locallab.spots.at(sp).hlcompr; + double hlcomprthresh = params->locallab.spots.at(sp).hlcomprthresh; + double shcompr = params->locallab.spots.at(sp).shcompr; + double br = params->locallab.spots.at(sp).lightness; + double cont = params->locallab.spots.at(sp).contrast; + + if (black < 0. && params->locallab.spots.at(sp).expMethod == "pde") { + black *= 1.5; + } + + // Reference parameters computation + if (params->locallab.spots.at(sp).spotMethod == "exc") { + ipf.calc_ref(sp, reserv, reserv, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + } else { + ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + } + + huerblu = huerefblurs[sp] = huerefblu; + chromarblu = chromarefblurs[sp] = chromarefblu; + lumarblu = lumarefblurs[sp] = lumarefblu; + huer = huerefs[sp] = huere; + chromar = chromarefs[sp] = chromare; + lumar = lumarefs[sp] = lumare ; + sobeler = sobelrefs[sp] = sobelre; + avg = avgs[sp] = avge; + CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumar, + hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, avg, + sca); + + // Save Locallab mask curve references for current spot + LocallabListener::locallabRef spotref; + spotref.huer = huer; + spotref.lumar = lumar; + spotref.chromar = chromar; + locallref.push_back(spotref); + + // Locallab tools computation + /* Notes: + * - shbuffer is used as nullptr + */ + // Locallab mask is only showed in detailed image + float minCD; + float maxCD; + float mini; + float maxi; + float Tmean; + float Tsigma; + float Tmin; + float Tmax; + ipf.Lab_Local(3, sp, (float**)shbuffer, nprevl, nprevl, reserv, lastorigimp, 0, 0, pW, pH, scale, locRETgainCurve, locRETtransCurve, + lllocalcurve, locallutili, + cllocalcurve, localclutili, + lclocalcurve, locallcutili, + loclhCurve, lochhCurve, + lmasklocalcurve, localmaskutili, + lmaskexplocalcurve, localmaskexputili, + lmaskSHlocalcurve, localmaskSHutili, + lmaskviblocalcurve, localmaskvibutili, + lmasktmlocalcurve, localmasktmutili, + lmaskretilocalcurve, localmaskretiutili, + lmaskcblocalcurve, localmaskcbutili, + lmaskbllocalcurve, localmaskblutili, + lmasklclocalcurve, localmasklcutili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, + locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, + locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, + locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, + locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + loclmasCurveblwav, lmasutiliblwav, + loclmasCurvecolwav, lmasutilicolwav, + locwavCurve, locwavutili, + loclevwavCurve, loclevwavutili, + locconwavCurve, locconwavutili, + loccompwavCurve, loccompwavutili, + loccomprewavCurve, loccomprewavutili, + locwavCurveden, locwavdenutili, + locedgwavCurve, locedgwavutili, + LHutili, HHutili, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, + huerblu, chromarblu, lumarblu, huer, chromar, lumar, sobeler, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + + lastorigimp->CopyFrom(nprevl); + + // Save Locallab Retinex min/max for current spot + LocallabListener::locallabRetiMinMax retiMinMax; + retiMinMax.cdma = maxCD; + retiMinMax.cdmin = minCD; + retiMinMax.mini = mini; + retiMinMax.maxi = maxi; + retiMinMax.Tmean = Tmean; + retiMinMax.Tsigma = Tsigma; + retiMinMax.Tmin = Tmin; + retiMinMax.Tmax = Tmax; + locallretiminmax.push_back(retiMinMax); + + // Recalculate references after + if (params->locallab.spots.at(sp).spotMethod == "exc") { + ipf.calc_ref(sp, reserv, reserv, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili); + } else { + ipf.calc_ref(sp, nprevl, nprevl, 0, 0, pW, pH, scale, huerefblu, chromarefblu, lumarefblu, huer, chromar, lumar, sobeler, avg, locwavCurveden, locwavdenutili); + } + + // Update Locallab reference values according to recurs parameter + if (params->locallab.spots.at(sp).recurs) { + locallref.at(sp).chromar = chromar; + locallref.at(sp).lumar = lumar; + locallref.at(sp).huer = huer; + } + + /* + //very bad idea : it's the story of the cat biting its tail + // brings big bugs.. + //restore ref values + huerefs[sp] = huer; + chromarefs[sp] = chromar; + lumarefs[sp] = lumar ; + sobelrefs[sp] = sobeler; + */ + lllocalcurve.clear(); + lclocalcurve.clear(); + cllocalcurve.clear(); + lightCurveloc.clear(); + cclocalcurve.clear(); + rgblocalcurve.clear(); + exlocalcurve.clear(); + lmasklocalcurve.clear(); + lmaskexplocalcurve.clear(); + lmaskSHlocalcurve.clear(); + lmaskviblocalcurve.clear(); + lmasktmlocalcurve.clear(); + lmaskretilocalcurve.clear(); + lmaskcblocalcurve.clear(); + lmaskbllocalcurve.clear(); + lmasklclocalcurve.clear(); + hltonecurveloc.clear(); + shtonecurveloc.clear(); + tonecurveloc.clear(); + locRETgainCurve.Reset(); + locRETtransCurve.Reset(); + loclhCurve.Reset(); + lochhCurve.Reset(); + locccmasCurve.Reset(); + locllmasCurve.Reset(); + lochhmasCurve.Reset(); + lochhhmasCurve.Reset(); + locllmasexpCurve.Reset(); + locccmasexpCurve.Reset(); + lochhmasexpCurve.Reset(); + locllmasSHCurve.Reset(); + locccmasSHCurve.Reset(); + lochhmasSHCurve.Reset(); + locllmasvibCurve.Reset(); + locccmasvibCurve.Reset(); + lochhmasvibCurve.Reset(); + locllmascbCurve.Reset(); + locccmascbCurve.Reset(); + lochhmascbCurve.Reset(); + locllmasretiCurve.Reset(); + locccmasretiCurve.Reset(); + lochhmasretiCurve.Reset(); + locllmastmCurve.Reset(); + locccmastmCurve.Reset(); + lochhmastmCurve.Reset(); + locllmasblCurve.Reset(); + locccmasblCurve.Reset(); + lochhmasblCurve.Reset(); + locllmaslcCurve.Reset(); + locccmaslcCurve.Reset(); + lochhmaslcCurve.Reset(); + locwavCurve.Reset(); + loclevwavCurve.Reset(); + locconwavCurve.Reset(); + locwavCurveden.Reset(); + locwavCurve.Reset(); + loclmasCurveblwav.Reset(); + loclmasCurvecolwav.Reset(); + } + + // Transmit Locallab reference values and Locallab Retinex min/max to LocallabListener + if (locallListener) { + locallListener->refChanged(locallref, params->locallab.selspot); + locallListener->minmaxChanged(locallretiminmax, params->locallab.selspot); + } + } + + //************************************************************* + // end locallab + //************************************************************* histCCurve.clear(); histLCurve.clear(); ipf.chromiLuminanceCurve(nullptr, pW, nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, histCCurve, histLCurve); - ipf.vibrance(nprevl); + ipf.vibrance(nprevl, params->vibrance, params->toneCurve.hrenabled, params->icm.workingProfile); ipf.labColorCorrectionRegions(nprevl); if ((params->colorappearance.enabled && !params->colorappearance.tonecie) || (!params->colorappearance.enabled)) { @@ -1102,7 +1665,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } - ipf.softLight(nprevl); + ipf.softLight(nprevl, params->softlight); if (params->colorappearance.enabled) { // L histo and Chroma histo for ciecam @@ -1173,7 +1736,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) CAMBrightCurveJ.dirty = true; CAMBrightCurveQ.dirty = true; - ipf.ciecam_02float(ncie, float (adap), pW, 2, nprevl, params.get(), customColCurve1, customColCurve2, customColCurve3, histLCAM, histCCAM, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 0 , scale, execsharp, d, dj, yb, 1); + ipf.ciecam_02float(ncie, float (adap), pW, 2, nprevl, params.get(), customColCurve1, customColCurve2, customColCurve3, histLCAM, histCCAM, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 0, scale, execsharp, d, dj, yb, 1); if ((params->colorappearance.autodegree || params->colorappearance.autodegreeout) && acListener && params->colorappearance.enabled && !params->colorappearance.presetcat02) { acListener->autoCamChanged(100.* (double)d, 100.* (double)dj); @@ -1219,7 +1782,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } } - // process crop, if needed +// process crop, if needed for (size_t i = 0; i < crops.size(); i++) if (crops[i]->hasListener() && (panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar) || (todo & (M_MONITOR | M_RGBCURVE | M_LUMACURVE)) || crops[i]->get_skip() == 1)) { crops[i]->update(todo); // may call ourselves @@ -1285,6 +1848,10 @@ void ImProcCoordinator::freeAll() oprevl = nullptr; delete nprevl; nprevl = nullptr; + delete reserv; + reserv = nullptr; + delete lastorigimp; + lastorigimp = nullptr; if (ncie) { delete ncie; @@ -1338,6 +1905,10 @@ void ImProcCoordinator::setScale(int prevscale) oprevi = orig_prev; oprevl = new LabImage(pW, pH); nprevl = new LabImage(pW, pH); + reserv = new LabImage(pW, pH); + lastorigimp = new LabImage(pW, pH); + + // nprevloc = new LabImage (pW, pH); //ncie is only used in ImProcCoordinator::updatePreviewImage, it will be allocated on first use and deleted if not used anymore previmg = new Image8(pW, pH); workimg = new Image8(pW, pH); @@ -1578,7 +2149,7 @@ void ImProcCoordinator::getSoftProofing(bool &softProof, bool &gamutCheck) gamutCheck = this->gamutCheck; } -ProcEvent ImProcCoordinator::setSharpMask (bool sharpMask) +ProcEvent ImProcCoordinator::setSharpMask(bool sharpMask) { if (this->sharpMask != sharpMask) { sharpMaskChanged = true; @@ -1755,8 +2326,9 @@ void ImProcCoordinator::process() while (changeSinceLast) { const bool panningRelatedChange = - params->toneCurve.isPanningRelatedChange(nextParams->toneCurve) + params->toneCurve.isPanningRelatedChange(nextParams->toneCurve) || params->labCurve != nextParams->labCurve + || params->locallab != nextParams->locallab || params->localContrast != nextParams->localContrast || params->rgbCurves != nextParams->rgbCurves || params->colorToning != nextParams->colorToning diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index e4858c76b..da253c072 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -92,7 +92,7 @@ protected: bool highDetailRawComputed; bool allocated; - void freeAll (); + void freeAll(); // Precomputed values used by DetailedCrop ---------------------------------------------- @@ -177,12 +177,13 @@ protected: FrameCountListener *frameCountListener; ImageTypeListener *imageTypeListener; FilmNegListener *filmNegListener; - AutoColorTonListener* actListener; AutoChromaListener* adnListener; WaveletListener* awavListener; RetinexListener* dehaListener; +// LocallabListener* locallListener; + HistogramListener* hListener; std::vector sizeListeners; @@ -192,9 +193,9 @@ protected: MyMutex minit; // to gain mutually exclusive access to ... to what exactly? - void reallocAll (); - void updateLRGBHistograms (); - void setScale (int prevscale); + void reallocAll(); + void updateLRGBHistograms(); + void setScale(int prevscale); void updatePreviewImage (int todo, bool panningRelatedChange); MyMutex mProcessing; @@ -223,15 +224,165 @@ protected: bool clcutili; bool opautili; bool wavcontlutili; - void startProcessing (); - void process (); + void startProcessing(); + void process(); float colourToningSatLimit; float colourToningSatLimitOpacity; bool highQualityComputed; cmsHTRANSFORM customTransformIn; cmsHTRANSFORM customTransformOut; - ImProcFunctions ipf; + + //locallab + LocallabListener* locallListener; + LabImage *reserv; + LabImage *lastorigimp; + int coordX, coordY, localX, localY; + LUTf lllocalcurve; + LUTf cllocalcurve; + LUTf lclocalcurve; + LUTf cclocalcurve; + LUTf rgblocalcurve; + LUTf exlocalcurve; + LUTf hltonecurveloc; + LUTf shtonecurveloc; + LUTf tonecurveloc; + LUTf lightCurveloc; + LUTf lmasklocalcurve; + LUTf lmaskexplocalcurve; + LUTf lmaskSHlocalcurve; + LUTf lmaskviblocalcurve; + LUTf lmasktmlocalcurve; + LUTf lmaskretilocalcurve; + LUTf lmaskcblocalcurve; + LUTf lmaskbllocalcurve; + LUTf lmasklclocalcurve; +// LUTu lhist16loc; + LocretigainCurve locRETgainCurve; + LocretitransCurve locRETtransCurve; + LocretigainCurverab locRETgainCurverab; + LocLHCurve loclhCurve; + LocHHCurve lochhCurve; + LocCCmaskCurve locccmasCurve; + LocLLmaskCurve locllmasCurve; + LocHHmaskCurve lochhmasCurve; + LocHHmaskCurve lochhhmasCurve; + LocCCmaskCurve locccmasexpCurve; + LocLLmaskCurve locllmasexpCurve; + LocHHmaskCurve lochhmasexpCurve; + LocCCmaskCurve locccmasSHCurve; + LocLLmaskCurve locllmasSHCurve; + LocHHmaskCurve lochhmasSHCurve; + LocCCmaskCurve locccmasvibCurve; + LocLLmaskCurve locllmasvibCurve; + LocHHmaskCurve lochhmasvibCurve; + LocCCmaskCurve locccmaslcCurve; + LocLLmaskCurve locllmaslcCurve; + LocHHmaskCurve lochhmaslcCurve; + LocCCmaskCurve locccmascbCurve; + LocLLmaskCurve locllmascbCurve; + LocHHmaskCurve lochhmascbCurve; + LocCCmaskCurve locccmasretiCurve; + LocLLmaskCurve locllmasretiCurve; + LocHHmaskCurve lochhmasretiCurve; + LocCCmaskCurve locccmastmCurve; + LocLLmaskCurve locllmastmCurve; + LocHHmaskCurve lochhmastmCurve; + LocCCmaskCurve locccmasblCurve; + LocLLmaskCurve locllmasblCurve; + LocHHmaskCurve lochhmasblCurve; + LocwavCurve locwavCurve; + LocwavCurve loclmasCurveblwav; + LocwavCurve loclmasCurvecolwav; + LocwavCurve loclevwavCurve; + LocwavCurve locconwavCurve; + LocwavCurve loccompwavCurve; + LocwavCurve loccomprewavCurve; + LocwavCurve locwavCurveden; + LocwavCurve locedgwavCurve; + + bool locallutili; + bool localclutili; + bool locallcutili; + bool localcutili; + bool localrgbutili; + bool localexutili; + bool llmasutili; + bool lhmasutili; + bool lhhmasutili; + bool lcmasutili; + bool localmaskutili; + bool localmaskexputili; + bool localmaskSHutili; + bool localmaskvibutili; + bool localmasktmutili; + bool localmaskretiutili; + bool localmaskcbutili; + bool localmaskblutili; + bool localmasklcutili; + bool lcmasexputili; + bool lhmasexputili; + bool llmasexputili; + bool lcmasSHutili; + bool lhmasSHutili; + bool llmasSHutili; + bool lcmasvibutili; + bool lhmasvibutili; + bool llmasvibutili; + bool lcmaslcutili; + bool lhmaslcutili; + bool llmaslcutili; + bool lcmascbutili; + bool lhmascbutili; + bool llmascbutili; + bool lcmasretiutili; + bool lhmasretiutili; + bool llmasretiutili; + bool lcmastmutili; + bool lhmastmutili; + bool llmastmutili; + bool lcmasblutili; + bool lhmasblutili; + bool llmasblutili; + bool locwavutili; + bool locwavdenutili; + bool loclevwavutili; + bool locconwavutili; + bool loccompwavutili; + bool loccomprewavutili; + bool locedgwavutili; + bool lmasutiliblwav; + bool lmasutilicolwav; + bool LHutili; + bool HHutili; + LUTu lastsavrests; + LUTf huerefs; + LUTf huerefblurs; + LUTf chromarefblurs; + LUTf lumarefblurs; + LUTf chromarefs; + LUTf lumarefs; + LUTf sobelrefs; + LUTf avgs; + double huer, huerblu, chromarblu, lumarblu, chromar, lumar, sobeler; + int lastsav; + float avg; + bool lastspotdup; + bool previewDeltaE; + int locallColorMask; + int locallColorMaskinv; + int locallExpMask; + int locallExpMaskinv; + int locallSHMask; + int locallSHMaskinv; + int locallvibMask; + int localllcMask; + int locallcbMask; + int locallretiMask; + int locallsoftMask; + int localltmMask; + int locallblMask; + int locallsharMask; public: @@ -247,10 +398,11 @@ public: void endUpdateParams (int changeFlags) override; void stopProcessing () override; + std::string *retistrsav; void setPreviewScale (int scale) override { - setScale (scale); + setScale(scale); } int getPreviewScale () override { @@ -301,6 +453,25 @@ public: updaterThreadStart.unlock(); } + void setLocallabMaskVisibility(bool previewDeltaE, int locallColorMask, int locallColorMaskinv, int locallExpMask, int locallExpMaskinv, int locallSHMask, int locallSHMaskinv, int locallvibMask, int locallsoftMask, int locallblMask, int localltmMask, int locallretiMask, int locallsharMask, int localllcMask, int locallcbMask) override + { + this->previewDeltaE = previewDeltaE; + this->locallColorMask = locallColorMask; + this->locallColorMaskinv = locallColorMaskinv; + this->locallExpMask = locallExpMask; + this->locallExpMaskinv = locallExpMaskinv; + this->locallSHMask = locallSHMask; + this->locallSHMaskinv = locallSHMaskinv; + this->locallvibMask = locallvibMask; + this->locallsoftMask = locallsoftMask; + this->locallblMask = locallblMask; + this->localltmMask = localltmMask; + this->locallretiMask = locallretiMask; + this->locallsharMask = locallsharMask; + this->localllcMask = localllcMask; + this->locallcbMask = locallcbMask; + } + void setProgressListener (ProgressListener* pl) override { plistener = pl; @@ -311,14 +482,14 @@ public: } void setSizeListener (SizeListener* il) override { - sizeListeners.push_back (il); + sizeListeners.push_back(il); } void delSizeListener (SizeListener* il) override { - std::vector::iterator it = std::find (sizeListeners.begin(), sizeListeners.end(), il); + std::vector::iterator it = std::find(sizeListeners.begin(), sizeListeners.end(), il); if (it != sizeListeners.end()) { - sizeListeners.erase (it); + sizeListeners.erase(it); } } void setAutoExpListener (AutoExpListener* ael) override @@ -353,6 +524,10 @@ public: { dehaListener = adh; } + void setLocallabListener (LocallabListener* lla) override + { + locallListener = lla; + } void setWaveletListener (WaveletListener* awa) override { awavListener = awa; @@ -415,7 +590,7 @@ public: } struct DenoiseInfoStore { - DenoiseInfoStore () : chM (0), max_r{}, max_b{}, ch_M{}, valid (false) {} + DenoiseInfoStore() : chM(0), max_r{}, max_b{}, ch_M{}, valid(false) {} float chM; float max_r[9]; float max_b[9]; diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index aa5515e79..6051b6b0b 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -51,10 +51,6 @@ #include "../rtgui/editcallbacks.h" -#ifdef _DEBUG -#include "mytime.h" -#endif - namespace { using namespace rtengine; @@ -173,31 +169,37 @@ void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, flo } } -void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) { +void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) +{ // this is a hack to avoid the blue=>black bug (Issue 2141) for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; #ifdef __SSE2__ - for (; j < tW - 3; j+=4, tj+=4) { + + for (; j < tW - 3; j += 4, tj += 4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); vfloat gv = LVF(gtemp[ti * tileSize + tj]); vmask zeromask = vorm(vmaskf_eq(rv, ZEROV), vmaskf_eq(gv, ZEROV)); - if(_mm_movemask_ps((vfloat)zeromask)) { + + if (_mm_movemask_ps((vfloat)zeromask)) { for (int k = 0; k < 4; ++k) { float r = rtemp[ti * tileSize + tj + k]; float g = gtemp[ti * tileSize + tj + k]; + float b = btemp[ti * tileSize + tj + k]; if ((r == 0.0f || g == 0.0f) && rtengine::min(r, g, b) >= 0.f) { float h, s, v; - Color::rgb2hsv (r, g, b, h, s, v); + Color::rgb2hsv(r, g, b, h, s, v); s *= 0.99f; - Color::hsv2rgb (h, s, v, rtemp[ti * tileSize + tj + k], gtemp[ti * tileSize + tj + k], btemp[ti * tileSize + tj + k]); + Color::hsv2rgb(h, s, v, rtemp[ti * tileSize + tj + k], gtemp[ti * tileSize + tj + k], btemp[ti * tileSize + tj + k]); } } } } + #endif + for (; j < tW; j++, tj++) { float r = rtemp[ti * tileSize + tj]; float g = gtemp[ti * tileSize + tj]; @@ -205,9 +207,9 @@ void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, if ((r == 0.0f || g == 0.0f) && rtengine::min(r, g, b) >= 0.f) { float h, s, v; - Color::rgb2hsv (r, g, b, h, s, v); + Color::rgb2hsv(r, g, b, h, s, v); s *= 0.99f; - Color::hsv2rgb (h, s, v, rtemp[ti * tileSize + tj], gtemp[ti * tileSize + tj], btemp[ti * tileSize + tj]); + Color::hsv2rgb(h, s, v, rtemp[ti * tileSize + tj], gtemp[ti * tileSize + tj], btemp[ti * tileSize + tj]); } } } @@ -253,7 +255,8 @@ void customToneCurve(const ToneCurve &customToneCurve, ToneCurveMode curveMode, } } -void fillEditFloat(float *editIFloatTmpR, float *editIFloatTmpG, float *editIFloatTmpB, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) { +void fillEditFloat(float *editIFloatTmpR, float *editIFloatTmpG, float *editIFloatTmpB, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) +{ for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { editIFloatTmpR[ti * tileSize + tj] = Color::gamma2curve[rtemp[ti * tileSize + tj]] / 65535.f; @@ -274,22 +277,23 @@ using namespace procparams; ImProcFunctions::~ImProcFunctions () { if (monitorTransform) { - cmsDeleteTransform (monitorTransform); + cmsDeleteTransform(monitorTransform); } } -void ImProcFunctions::setScale (double iscale) +void ImProcFunctions::setScale(double iscale) { scale = iscale; } -void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck) +void ImProcFunctions::updateColorProfiles(const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck) { // set up monitor transform if (monitorTransform) { - cmsDeleteTransform (monitorTransform); + cmsDeleteTransform(monitorTransform); } + gamutWarning.reset(nullptr); monitorTransform = nullptr; @@ -298,17 +302,17 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, if (!monitorProfile.empty()) { #if !defined(__APPLE__) // No support for monitor profiles on OS X, all data is sRGB - monitor = ICCStore::getInstance()->getProfile (monitorProfile); + monitor = ICCStore::getInstance()->getProfile(monitorProfile); #else monitor = ICCStore::getInstance()->getProfile (settings->srgb); #endif } if (monitor) { - MyMutex::MyLock lcmsLock (*lcmsMutex); + MyMutex::MyLock lcmsLock(*lcmsMutex); cmsUInt32Number flags; - cmsHPROFILE iprof = cmsCreateLab4Profile (nullptr); + cmsHPROFILE iprof = cmsCreateLab4Profile(nullptr); cmsHPROFILE gamutprof = nullptr; cmsUInt32Number gamutbpc = 0; RenderingIntent gamutintent = RI_RELATIVE; @@ -322,7 +326,8 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, flags = cmsFLAGS_SOFTPROOFING | cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE; if (!settings->printerProfile.empty()) { - oprof = ICCStore::getInstance()->getProfile (settings->printerProfile); + oprof = ICCStore::getInstance()->getProfile(settings->printerProfile); + if (settings->printerBPC) { flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } @@ -332,6 +337,7 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, if (params->icm.outputBPC) { flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } + outIntent = params->icm.outputIntent; } @@ -363,7 +369,7 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, make_gamma_table(softproof, cmsSigBlueTRCTag); } - monitorTransform = cmsCreateProofingTransform ( + monitorTransform = cmsCreateProofingTransform( iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_FLT, softproof, //oprof, @@ -381,9 +387,11 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, if (gamutCheck) { gamutprof = oprof; + if (params->icm.outputBPC) { gamutbpc = cmsFLAGS_BLACKPOINTCOMPENSATION; } + gamutintent = outIntent; } } @@ -399,9 +407,11 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, // softProofCreated = true; // } gamutprof = monitor; + if (settings->monitorBPC) { gamutbpc = cmsFLAGS_BLACKPOINTCOMPENSATION; } + gamutintent = monitorIntent; } @@ -412,18 +422,18 @@ void ImProcFunctions::updateColorProfiles (const Glib::ustring& monitorProfile, flags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } - monitorTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_FLT, monitorIntent, flags); + monitorTransform = cmsCreateTransform(iprof, TYPE_Lab_FLT, monitor, TYPE_RGB_FLT, monitorIntent, flags); } if (gamutCheck && gamutprof) { gamutWarning.reset(new GamutWarning(iprof, gamutprof, gamutintent, gamutbpc)); } - cmsCloseProfile (iprof); + cmsCloseProfile(iprof); } } -void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const ProcParams ¶ms, LUTu & histogram) +void ImProcFunctions::firstAnalysis(const Imagefloat* const original, const ProcParams ¶ms, LUTu & histogram) { TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix (params.icm.workingProfile); @@ -434,7 +444,7 @@ void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const Pro int W = original->getWidth(); int H = original->getHeight(); - float lumimulf[3] = {static_cast (lumimul[0]), static_cast (lumimul[1]), static_cast (lumimul[2])}; + float lumimulf[3] = {static_cast(lumimul[0]), static_cast(lumimul[1]), static_cast(lumimul[2])}; // calculate histogram of the y channel needed for contrast curve calculation in exposure adjustments histogram.clear(); @@ -442,11 +452,11 @@ void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const Pro if (multiThread) { #ifdef _OPENMP - const int numThreads = min (max (W * H / (int)histogram.getSize(), 1), omp_get_max_threads()); + const int numThreads = min(max(W * H / (int)histogram.getSize(), 1), omp_get_max_threads()); #pragma omp parallel num_threads(numThreads) if(numThreads>1) #endif { - LUTu hist (histogram.getSize()); + LUTu hist(histogram.getSize()); hist.clear(); #ifdef _OPENMP #pragma omp for nowait @@ -455,9 +465,9 @@ void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const Pro for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { - float r = original->r (i, j); - float g = original->g (i, j); - float b = original->b (i, j); + float r = original->r(i, j); + float g = original->g(i, j); + float b = original->b(i, j); int y = (lumimulf[0] * r + lumimulf[1] * g + lumimulf[2] * b); hist[y]++; @@ -471,15 +481,15 @@ void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const Pro } #ifdef _OPENMP - static_cast (numThreads); // to silence cppcheck warning + static_cast(numThreads); // to silence cppcheck warning #endif } else { for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { - float r = original->r (i, j); - float g = original->g (i, j); - float b = original->b (i, j); + float r = original->r(i, j); + float g = original->g(i, j); + float b = original->b(i, j); int y = (lumimulf[0] * r + lumimulf[1] * g + lumimulf[2] * b); histogram[y]++; @@ -488,27 +498,23 @@ void ImProcFunctions::firstAnalysis (const Imagefloat* const original, const Pro } } + // Copyright (c) 2012 Jacques Desmis -void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pwb, LabImage* lab, const ProcParams* params, - const ColorAppearance & customColCurve1, const ColorAppearance & customColCurve2, const ColorAppearance & customColCurve3, - LUTu & histLCAM, LUTu & histCCAM, LUTf & CAMBrightCurveJ, LUTf & CAMBrightCurveQ, float &mean, int Iterates, int scale, bool execsharp, float &d, float &dj, float &yb, int rtt, - bool showSharpMask) + +void ImProcFunctions::ciecam_02float(CieImage* ncie, float adap, int pW, int pwb, LabImage* lab, const ProcParams* params, + const ColorAppearance & customColCurve1, const ColorAppearance & customColCurve2, const ColorAppearance & customColCurve3, + LUTu & histLCAM, LUTu & histCCAM, LUTf & CAMBrightCurveJ, LUTf & CAMBrightCurveQ, float &mean, int Iterates, int scale, bool execsharp, float &d, float &dj, float &yb, int rtt, + bool showSharpMask) { if (params->colorappearance.enabled) { - -#ifdef _DEBUG - MyTime t1e, t2e; - t1e.set(); -#endif - //preparate for histograms CIECAM LUTu hist16JCAM; LUTu hist16_CCAM; if (pW != 1 && params->colorappearance.datacie) { //only with improccoordinator - hist16JCAM (32768); + hist16JCAM(32768); hist16JCAM.clear(); - hist16_CCAM (48000); + hist16_CCAM(48000); hist16_CCAM.clear(); } @@ -527,13 +533,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw double Xwsc, Zwsc; const bool epdEnabled = params->epd.enabled; - bool ciedata = (params->colorappearance.datacie && pW != 1) && ! ((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) + bool ciedata = (params->colorappearance.datacie && pW != 1) && !((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) || (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab) - || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)); + || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)); - ColorTemp::temp2mulxyz (params->wb.temperature, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB - ColorTemp::temp2mulxyz (params->colorappearance.tempout, "Custom", Xwout, Zwout); - ColorTemp::temp2mulxyz (params->colorappearance.tempsc, "Custom", Xwsc, Zwsc); + ColorTemp::temp2mulxyz(params->wb.temperature, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB + ColorTemp::temp2mulxyz(params->colorappearance.tempout, "Custom", Xwout, Zwout); + ColorTemp::temp2mulxyz(params->colorappearance.tempsc, "Custom", Xwsc, Zwsc); //viewing condition for surrsrc if (params->colorappearance.surrsrc == "Average") { @@ -581,7 +587,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw } */ //with which algorithm - if (params->colorappearance.algo == "JC") { + if (params->colorappearance.algo == "JC") { alg = 0; } else if (params->colorappearance.algo == "JS") { alg = 1; @@ -677,6 +683,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw const float deg = (params->colorappearance.degree) / 100.0f; const float pilot = params->colorappearance.autodegree ? 2.0f : deg; + const float degout = (params->colorappearance.degreeout) / 100.0f; const float pilotout = params->colorappearance.autodegreeout ? 2.0f : degout; @@ -751,19 +758,19 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw if ((needJ && CAMBrightCurveJ.dirty) || (needQ && CAMBrightCurveQ.dirty) || (std::isnan(mean) && settings->viewinggreySc != 0)) { if (needJ) { - hist16J (32768); + hist16J(32768); hist16J.clear(); } if (needQ) { - hist16Q (32768); + hist16Q(32768); hist16Q.clear(); } double sum = 0.0; // use double precision for large summations #ifdef _OPENMP - const int numThreads = min (max (width * height / 65536, 1), omp_get_max_threads()); + const int numThreads = min(max(width * height / 65536, 1), omp_get_max_threads()); #pragma omp parallel num_threads(numThreads) if(numThreads>1) #endif { @@ -771,12 +778,12 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw LUTu hist16Qthr; if (needJ) { - hist16Jthr (hist16J.getSize()); + hist16Jthr(hist16J.getSize()); hist16Jthr.clear(); } if (needQ) { - hist16Qthr (hist16Q.getSize()); + hist16Qthr(hist16Q.getSize()); hist16Qthr.clear(); } @@ -825,7 +832,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw } if (needJ) { - hist16Jthr[ (int) ((koef * lab->L[i][j]))]++; //evaluate histogram luminance L # J + hist16Jthr[(int)((koef * lab->L[i][j]))]++; //evaluate histogram luminance L # J } //estimation of wh only with La @@ -841,7 +848,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw } */ if (needQ) { - hist16Qthr[CLIP ((int) (32768.f * sqrt ((koef * (lab->L[i][j])) / 32768.f)))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L + hist16Qthr[CLIP((int)(32768.f * sqrt((koef * (lab->L[i][j])) / 32768.f)))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L //perhaps needs to introduce whestim ?? //hist16Qthr[ (int) (sqrtf ((koef * (lab->L[i][j])) * 32768.f))]++; //for brightness Q : approximation for Q=wh*sqrt(J/100) J not equal L } @@ -882,7 +889,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // if (settings->viewinggreySc == 0) { //auto if (params->colorappearance.autoybscen && pwb == 2) {//auto - if (mean < 15.f) { + if (mean < 15.f) { yb = 3.0f; } else if (mean < 30.f) { yb = 5.0f; @@ -908,7 +915,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // } else if (settings->viewinggreySc == 1) { } else { - yb = (float) params->colorappearance.ybscen; + yb = (float) params->colorappearance.ybscen; } const bool highlight = params->toneCurve.hrenabled; //Get the value if "highlight reconstruction" is activated @@ -948,33 +955,33 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw Ciecam02::initcam1float (yb, pilot, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c); //printf ("wh=%f \n", wh); - const float pow1 = pow_F ( 1.64f - pow_F ( 0.29f, n ), 0.73f ); + const float pow1 = pow_F(1.64f - pow_F(0.29f, n), 0.73f); float nj, nbbj, ncbj, czj, awj, flj; Ciecam02::initcam2float (yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); #ifdef __SSE2__ const float reccmcz = 1.f / (c2 * czj); #endif - const float pow1n = pow_F ( 1.64f - pow_F ( 0.29f, nj ), 0.73f ); + const float pow1n = pow_F(1.64f - pow_F(0.29f, nj), 0.73f); const float epsil = 0.0001f; const float coefQ = 32767.f / wh; const float a_w = aw; const float c_ = c; const float f_l = fl; - const float coe = pow_F (fl, 0.25f); - const float QproFactor = ( 0.4f / c ) * ( aw + 4.0f ) ; - const bool LabPassOne = ! ((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) - || (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab) - || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)); + const float coe = pow_F(fl, 0.25f); + const float QproFactor = (0.4f / c) * (aw + 4.0f) ; + const bool LabPassOne = !((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) + || (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab) + || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)); //printf("coQ=%f\n", coefQ); if (needJ) { if (!CAMBrightCurveJ) { - CAMBrightCurveJ (32768, LUT_CLIP_ABOVE); + CAMBrightCurveJ(32768, LUT_CLIP_ABOVE); } if (CAMBrightCurveJ.dirty) { - Ciecam02::curveJfloat (params->colorappearance.jlight, params->colorappearance.contrast, hist16J, CAMBrightCurveJ);//lightness and contrast J + Ciecam02::curveJfloat(params->colorappearance.jlight, params->colorappearance.contrast, hist16J, CAMBrightCurveJ); //lightness and contrast J CAMBrightCurveJ /= 327.68f; CAMBrightCurveJ.dirty = false; } @@ -982,11 +989,11 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw if (needQ) { if (!CAMBrightCurveQ) { - CAMBrightCurveQ (32768, LUT_CLIP_ABOVE); + CAMBrightCurveQ(32768, LUT_CLIP_ABOVE); } if (CAMBrightCurveQ.dirty) { - Ciecam02::curveJfloat (params->colorappearance.qbright, params->colorappearance.qcontrast, hist16Q, CAMBrightCurveQ);//brightness and contrast Q + Ciecam02::curveJfloat(params->colorappearance.qbright, params->colorappearance.qcontrast, hist16Q, CAMBrightCurveQ); //brightness and contrast Q // CAMBrightCurveQ /= coefQ; CAMBrightCurveQ.dirty = false; } @@ -1004,10 +1011,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw #ifdef __SSE2__ int bufferLength = ((width + 3) / 4) * 4; // bufferLength has to be a multiple of 4 #endif -#ifndef _DEBUG #ifdef _OPENMP #pragma omp parallel -#endif #endif { float minQThr = 10000.f; @@ -1021,10 +1026,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float Mbuffer[bufferLength] ALIGNED16; float sbuffer[bufferLength] ALIGNED16; #endif -#ifndef _DEBUG #ifdef _OPENMP #pragma omp for schedule(dynamic, 16) -#endif #endif for (int i = 0; i < height; i++) { @@ -1034,24 +1037,24 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw vfloat x, y, z; vfloat J, C, h, Q, M, s; - vfloat c655d35 = F2V (655.35f); + vfloat c655d35 = F2V(655.35f); for (k = 0; k < width - 3; k += 4) { - Color::Lab2XYZ (LVFU (lab->L[i][k]), LVFU (lab->a[i][k]), LVFU (lab->b[i][k]), x, y, z); + Color::Lab2XYZ(LVFU(lab->L[i][k]), LVFU(lab->a[i][k]), LVFU(lab->b[i][k]), x, y, z); x = x / c655d35; y = y / c655d35; z = z / c655d35; - Ciecam02::xyz2jchqms_ciecam02float ( J, C, h, - Q, M, s, F2V (aw), F2V (fl), F2V (wh), - x, y, z, - F2V (xw1), F2V (yw1), F2V (zw1), - F2V (c), F2V (nc), F2V (pow1), F2V (nbb), F2V (ncb), F2V (pfl), F2V (cz), F2V (d)); - STVF (Jbuffer[k], J); - STVF (Cbuffer[k], C); - STVF (hbuffer[k], h); - STVF (Qbuffer[k], Q); - STVF (Mbuffer[k], M); - STVF (sbuffer[k], s); + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, F2V(aw), F2V(fl), F2V(wh), + x, y, z, + F2V(xw1), F2V(yw1), F2V(zw1), + F2V(c), F2V(nc), F2V(pow1), F2V(nbb), F2V(ncb), F2V(pfl), F2V(cz), F2V(d)); + STVF(Jbuffer[k], J); + STVF(Cbuffer[k], C); + STVF(hbuffer[k], h); + STVF(Qbuffer[k], Q); + STVF(Mbuffer[k], M); + STVF(sbuffer[k], s); } for (; k < width; k++) { @@ -1060,15 +1063,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float b = lab->b[i][k]; float x, y, z; //convert Lab => XYZ - Color::Lab2XYZ (L, a, b, x, y, z); + Color::Lab2XYZ(L, a, b, x, y, z); x = x / 655.35f; y = y / 655.35f; z = z / 655.35f; float J, C, h, Q, M, s; - Ciecam02::xyz2jchqms_ciecam02float ( J, C, h, - Q, M, s, aw, fl, wh, - x, y, z, - xw1, yw1, zw1, + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, aw, fl, wh, + x, y, z, + xw1, yw1, zw1, c, nc, pow1, nbb, ncb, pfl, cz, d); Jbuffer[k] = J; Cbuffer[k] = C; @@ -1098,15 +1101,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float b = lab->b[i][j]; float x1, y1, z1; //convert Lab => XYZ - Color::Lab2XYZ (L, a, b, x1, y1, z1); + Color::Lab2XYZ(L, a, b, x1, y1, z1); x = (float)x1 / 655.35f; y = (float)y1 / 655.35f; z = (float)z1 / 655.35f; //process source==> normal - Ciecam02::xyz2jchqms_ciecam02float ( J, C, h, - Q, M, s, aw, fl, wh, - x, y, z, - xw1, yw1, zw1, + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, aw, fl, wh, + x, y, z, + xw1, yw1, zw1, c, nc, pow1, nbb, ncb, pfl, cz, d); #endif float Jpro, Cpro, hpro, Qpro, Mpro, spro; @@ -1120,77 +1123,77 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // we cannot have all algorithms with all chroma curves if (alg == 0) { Jpro = CAMBrightCurveJ[Jpro * 327.68f]; //lightness CIECAM02 + contrast - Qpro = QproFactor * sqrtf (Jpro); + Qpro = QproFactor * sqrtf(Jpro); float Cp = (spro * spro * Qpro) / (1000000.f); Cpro = Cp * 100.f; float sres; - Ciecam02::curvecolorfloat (chr, Cp, sres, 1.8f); - Color::skinredfloat (Jpro, hpro, sres, Cp, 55.f, 30.f, 1, rstprotection, 100.f, Cpro); + Ciecam02::curvecolorfloat(chr, Cp, sres, 1.8f); + Color::skinredfloat(Jpro, hpro, sres, Cp, 55.f, 30.f, 1, rstprotection, 100.f, Cpro); } else if (alg == 1) { // Lightness saturation Jpro = CAMBrightCurveJ[Jpro * 327.68f]; //lightness CIECAM02 + contrast float sres; float Sp = spro / 100.0f; float parsat = 1.5f; //parsat=1.5 =>saturation ; 1.8 => chroma ; 2.5 => colorfullness (personal evaluation) - Ciecam02::curvecolorfloat (schr, Sp, sres, parsat); + Ciecam02::curvecolorfloat(schr, Sp, sres, parsat); float dred = 100.f; // in C mode float protect_red = 80.0f; // in C mode - dred = 100.0f * sqrtf ((dred * coe) / Qpro); - protect_red = 100.0f * sqrtf ((protect_red * coe) / Qpro); - Color::skinredfloat (Jpro, hpro, sres, Sp, dred, protect_red, 0, rstprotection, 100.f, spro); - Qpro = QproFactor * sqrtf (Jpro); + dred = 100.0f * sqrtf((dred * coe) / Qpro); + protect_red = 100.0f * sqrtf((protect_red * coe) / Qpro); + Color::skinredfloat(Jpro, hpro, sres, Sp, dred, protect_red, 0, rstprotection, 100.f, spro); + Qpro = QproFactor * sqrtf(Jpro); Cpro = (spro * spro * Qpro) / (10000.0f); } else if (alg == 2) { //printf("Qp0=%f ", Qpro); - Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)] / coefQ; //brightness and contrast + Qpro = CAMBrightCurveQ[(float)(Qpro * coefQ)] / coefQ; //brightness and contrast //printf("Qpaf=%f ", Qpro); float Mp, sres; Mp = Mpro / 100.0f; - Ciecam02::curvecolorfloat (mchr, Mp, sres, 2.5f); + Ciecam02::curvecolorfloat(mchr, Mp, sres, 2.5f); float dred = 100.f; //in C mode float protect_red = 80.0f; // in C mode dred *= coe; //in M mode protect_red *= coe; //M mode - Color::skinredfloat (Jpro, hpro, sres, Mp, dred, protect_red, 0, rstprotection, 100.f, Mpro); - Jpro = SQR ((10.f * Qpro) / wh); + Color::skinredfloat(Jpro, hpro, sres, Mp, dred, protect_red, 0, rstprotection, 100.f, Mpro); + Jpro = SQR((10.f * Qpro) / wh); Cpro = Mpro / coe; Qpro = (Qpro == 0.f ? epsil : Qpro); // avoid division by zero - spro = 100.0f * sqrtf ( Mpro / Qpro ); + spro = 100.0f * sqrtf(Mpro / Qpro); } else { /*if(alg == 3) */ - Qpro = CAMBrightCurveQ[ (float) (Qpro * coefQ)] / coefQ; //brightness and contrast + Qpro = CAMBrightCurveQ[(float)(Qpro * coefQ)] / coefQ; //brightness and contrast float Mp, sres; Mp = Mpro / 100.0f; - Ciecam02::curvecolorfloat (mchr, Mp, sres, 2.5f); + Ciecam02::curvecolorfloat(mchr, Mp, sres, 2.5f); float dred = 100.f; //in C mode float protect_red = 80.0f; // in C mode dred *= coe; //in M mode protect_red *= coe; //M mode - Color::skinredfloat (Jpro, hpro, sres, Mp, dred, protect_red, 0, rstprotection, 100.f, Mpro); - Jpro = SQR ((10.f * Qpro) / wh); + Color::skinredfloat(Jpro, hpro, sres, Mp, dred, protect_red, 0, rstprotection, 100.f, Mpro); + Jpro = SQR((10.f * Qpro) / wh); Cpro = Mpro / coe; Qpro = (Qpro == 0.f ? epsil : Qpro); // avoid division by zero - spro = 100.0f * sqrtf ( Mpro / Qpro ); + spro = 100.0f * sqrtf(Mpro / Qpro); if (Jpro > 99.9f) { Jpro = 99.9f; } - Jpro = CAMBrightCurveJ[ (float) (Jpro * 327.68f)]; //lightness CIECAM02 + contrast + Jpro = CAMBrightCurveJ[(float)(Jpro * 327.68f)]; //lightness CIECAM02 + contrast float Sp = spro / 100.0f; - Ciecam02::curvecolorfloat (schr, Sp, sres, 1.5f); + Ciecam02::curvecolorfloat(schr, Sp, sres, 1.5f); dred = 100.f; // in C mode protect_red = 80.0f; // in C mode - dred = 100.0f * sqrtf ((dred * coe) / Q); - protect_red = 100.0f * sqrtf ((protect_red * coe) / Q); - Color::skinredfloat (Jpro, hpro, sres, Sp, dred, protect_red, 0, rstprotection, 100.f, spro); - Qpro = QproFactor * sqrtf (Jpro); + dred = 100.0f * sqrtf((dred * coe) / Q); + protect_red = 100.0f * sqrtf((protect_red * coe) / Q); + Color::skinredfloat(Jpro, hpro, sres, Sp, dred, protect_red, 0, rstprotection, 100.f, spro); + Qpro = QproFactor * sqrtf(Jpro); float Cp = (spro * spro * Qpro) / (1000000.f); Cpro = Cp * 100.f; - Ciecam02::curvecolorfloat (chr, Cp, sres, 1.8f); - Color::skinredfloat (Jpro, hpro, sres, Cp, 55.f, 30.f, 1, rstprotection, 100.f, Cpro); + Ciecam02::curvecolorfloat(chr, Cp, sres, 1.8f); + Color::skinredfloat(Jpro, hpro, sres, Cp, 55.f, 30.f, 1, rstprotection, 100.f, Cpro); // disabled this code, Issue 2690 // if(Jpro < 1.f && Cpro > 12.f) Cpro=12.f;//reduce artifacts by "pseudo gamut control CIECAM" // else if(Jpro < 2.f && Cpro > 15.f) Cpro=15.f; @@ -1198,7 +1201,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // else if(Jpro < 7.f && Cpro > 50.f) Cpro=50.f; hpro = hpro + hue; - if ( hpro < 0.0f ) { + if (hpro < 0.0f) { hpro += 360.0f; //hue } } @@ -1210,15 +1213,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float Jold100 = (float) Jpro; float redu = 25.f; float reduc = 1.f; - const Lightcurve& userColCurveJ1 = static_cast (customColCurve1); - userColCurveJ1.Apply (Jj); + const Lightcurve& userColCurveJ1 = static_cast(customColCurve1); + userColCurveJ1.Apply(Jj); if (Jj > Jold) { if (Jj < 65535.f) { if (Jold < 327.68f * redu) { Jj = 0.3f * (Jj - Jold) + Jold; //divide sensibility } else { - reduc = LIM ((100.f - Jold100) / (100.f - redu), 0.f, 1.f); + reduc = LIM((100.f - Jold100) / (100.f - redu), 0.f, 1.f); Jj = 0.3f * reduc * (Jj - Jold) + Jold; //reduct sensibility in highlights } } @@ -1228,7 +1231,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw Jj = 0.90f * (Jj - Jold) + Jold; // not zero ==>artifacts } - Jpro = (float) (Jj / 327.68f); + Jpro = (float)(Jj / 327.68f); if (Jpro < 1.f) { Jpro = 1.f; @@ -1244,15 +1247,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float redu = 20.f; float reduc = 1.f; - const Brightcurve& userColCurveB1 = static_cast (customColCurve1); - userColCurveB1.Apply (Qq); + const Brightcurve& userColCurveB1 = static_cast(customColCurve1); + userColCurveB1.Apply(Qq); if (Qq > Qold) { if (Qq < 65535.f) { if (Qold < 327.68f * redu) { Qq = 0.25f * (Qq - Qold) + Qold; //divide sensibility } else { - reduc = LIM ((100.f - Qold100) / (100.f - redu), 0.f, 1.f); + reduc = LIM((100.f - Qold100) / (100.f - redu), 0.f, 1.f); Qq = 0.25f * reduc * (Qq - Qold) + Qold; //reduct sensibility in highlights } } @@ -1282,15 +1285,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float Jold100 = (float) Jpro; float redu = 25.f; float reduc = 1.f; - const Lightcurve& userColCurveJ2 = static_cast (customColCurve2); - userColCurveJ2.Apply (Jj); + const Lightcurve& userColCurveJ2 = static_cast(customColCurve2); + userColCurveJ2.Apply(Jj); if (Jj > Jold) { if (Jj < 65535.f) { if (Jold < 327.68f * redu) { Jj = 0.3f * (Jj - Jold) + Jold; //divide sensibility } else { - reduc = LIM ((100.f - Jold100) / (100.f - redu), 0.f, 1.f); + reduc = LIM((100.f - Jold100) / (100.f - redu), 0.f, 1.f); Jj = 0.3f * reduc * (Jj - Jold) + Jold; //reduct sensibility in highlights } } @@ -1308,7 +1311,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw } } - Jpro = (float) (Jj / 327.68f); + Jpro = (float)(Jj / 327.68f); if (Jpro < 1.f) { Jpro = 1.f; @@ -1325,15 +1328,15 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float redu = 20.f; float reduc = 1.f; - const Brightcurve& userColCurveB2 = static_cast (customColCurve2); - userColCurveB2.Apply (Qq); + const Brightcurve& userColCurveB2 = static_cast(customColCurve2); + userColCurveB2.Apply(Qq); if (Qq > Qold) { if (Qq < 65535.f) { if (Qold < 327.68f * redu) { Qq = 0.25f * (Qq - Qold) + Qold; //divide sensibility } else { - reduc = LIM ((100.f - Qold100) / (100.f - redu), 0.f, 1.f); + reduc = LIM((100.f - Qold100) / (100.f - redu), 0.f, 1.f); Qq = 0.25f * reduc * (Qq - Qold) + Qold; //reduct sensibility in highlights } } @@ -1354,10 +1357,10 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw coef = 2.f; //adapt Q to J approximation Qq = (float) Qpro * coef; Qold = Qq; - const Lightcurve& userColCurveJ1 = static_cast (customColCurve1); - userColCurveJ1.Apply (Qq); + const Lightcurve& userColCurveJ1 = static_cast(customColCurve1); + userColCurveJ1.Apply(Qq); Qq = 0.05f * (Qq - Qold) + Qold; //approximative adaptation - Qpro = (float) (Qq / coef); + Qpro = (float)(Qq / coef); Jpro = 100.f * (Qpro * Qpro) / ((4.0f / c) * (4.0f / c) * (aw + 4.0f) * (aw + 4.0f)); } @@ -1373,13 +1376,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float coef = 327.68f / parsat; float Cc = (float) Cpro * coef; float Ccold = Cc; - const Chromacurve& userColCurve = static_cast (customColCurve3); - userColCurve.Apply (Cc); + const Chromacurve& userColCurve = static_cast(customColCurve3); + userColCurve.Apply(Cc); float dred = 55.f; float protect_red = 30.0f; int sk = 1; float ko = 1.f / coef; - Color::skinredfloat (Jpro, hpro, Cc, Ccold, dred, protect_red, sk, rstprotection, ko, Cpro); + Color::skinredfloat(Jpro, hpro, Cc, Ccold, dred, protect_red, sk, rstprotection, ko, Cpro); /* if(Jpro < 1.f && Cpro > 12.f) { Cpro = 12.f; //reduce artifacts by "pseudo gamut control CIECAM" @@ -1396,32 +1399,32 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float coef = 327.68f / parsat; float Ss = (float) spro * coef; float Sold = Ss; - const Saturcurve& userColCurve = static_cast (customColCurve3); - userColCurve.Apply (Ss); + const Saturcurve& userColCurve = static_cast(customColCurve3); + userColCurve.Apply(Ss); Ss = 0.6f * (Ss - Sold) + Sold; //divide sensibility saturation float dred = 100.f; // in C mode float protect_red = 80.0f; // in C mode - dred = 100.0f * sqrtf ((dred * coe) / Qpro); - protect_red = 100.0f * sqrtf ((protect_red * coe) / Qpro); + dred = 100.0f * sqrtf((dred * coe) / Qpro); + protect_red = 100.0f * sqrtf((protect_red * coe) / Qpro); int sk = 0; float ko = 1.f / coef; - Color::skinredfloat (Jpro, hpro, Ss, Sold, dred, protect_red, sk, rstprotection, ko, spro); - Qpro = ( 4.0f / c ) * sqrtf ( Jpro / 100.0f ) * ( aw + 4.0f ) ; + Color::skinredfloat(Jpro, hpro, Ss, Sold, dred, protect_red, sk, rstprotection, ko, spro); + Qpro = (4.0f / c) * sqrtf(Jpro / 100.0f) * (aw + 4.0f) ; Cpro = (spro * spro * Qpro) / (10000.0f); } else if (curveMode3 == ColorAppearanceParams::CtcMode::COLORF) { // float parsat = 0.8f; //0.68; float coef = 327.68f / parsat; float Mm = (float) Mpro * coef; float Mold = Mm; - const Colorfcurve& userColCurve = static_cast (customColCurve3); - userColCurve.Apply (Mm); + const Colorfcurve& userColCurve = static_cast(customColCurve3); + userColCurve.Apply(Mm); float dred = 100.f; //in C mode float protect_red = 80.0f; // in C mode dred *= coe; //in M mode protect_red *= coe; int sk = 0; float ko = 1.f / coef; - Color::skinredfloat (Jpro, hpro, Mm, Mold, dred, protect_red, sk, rstprotection, ko, Mpro); + Color::skinredfloat(Jpro, hpro, Mm, Mold, dred, protect_red, sk, rstprotection, ko, Mpro); /* if(Jpro < 1.f && Mpro > 12.f * coe) { Mpro = 12.f * coe; //reduce artifacts by "pseudo gamut control CIECAM" @@ -1454,7 +1457,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw ncie->J_p[i][j] = (float)J + epsil; ncie->h_p[i][j] = (float)h; ncie->C_p[i][j] = (float)C + epsil; - ncie->sh_p[i][j] = (float) 3276.8f * (sqrtf ( J ) ) ; + ncie->sh_p[i][j] = (float) 3276.8f * (sqrtf(J)) ; if (epdEnabled) { if (ncie->Q_p[i][j] < minQThr) { @@ -1486,7 +1489,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw libr = J; //327 for J } - posl = (int) (libr * brli); + posl = (int)(libr * brli); hist16JCAM[posl]++; if (curveMode3 == ColorAppearanceParams::CtcMode::CHROMA) { @@ -1500,7 +1503,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw colch = M; } - posc = (int) (colch * chsacol); + posc = (int)(colch * chsacol); hist16_CCAM[posc]++; } @@ -1515,9 +1518,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float xx, yy, zz; //process normal==> viewing - Ciecam02::jch2xyz_ciecam02float ( xx, yy, zz, - J, C, h, - xw2, yw2, zw2, + Ciecam02::jch2xyz_ciecam02float(xx, yy, zz, + J, C, h, + xw2, yw2, zw2, c2, nc2, pow1n, nbbj, ncbj, flj, czj, dj, awj); float x, y, z; x = xx * 655.35f; @@ -1525,13 +1528,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw z = zz * 655.35f; float Ll, aa, bb; //convert xyz=>lab - Color::XYZ2Lab (x, y, z, Ll, aa, bb); + Color::XYZ2Lab(x, y, z, Ll, aa, bb); // gamut control in Lab mode; I must study how to do with cIECAM only if (gamu == 1) { float Lprov1, Chprov1; Lprov1 = Ll / 327.68f; - Chprov1 = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; + Chprov1 = sqrtf(SQR(aa) + SQR(bb)) / 327.68f; float2 sincosval; if (Chprov1 == 0.0f) { @@ -1543,16 +1546,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw } -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); -#endif - + Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); lab->L[i][j] = Lprov1 * 327.68f; lab->a[i][j] = 327.68f * Chprov1 * sincosval.y; lab->b[i][j] = 327.68f * Chprov1 * sincosval.x; @@ -1575,13 +1570,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float *zbuffer = sbuffer; for (k = 0; k < bufferLength; k += 4) { - Ciecam02::jch2xyz_ciecam02float ( x, y, z, - LVF (Jbuffer[k]), LVF (Cbuffer[k]), LVF (hbuffer[k]), - F2V (xw2), F2V (yw2), F2V (zw2), - F2V (nc2), F2V (pow1n), F2V (nbbj), F2V (ncbj), F2V (flj), F2V (dj), F2V (awj), F2V (reccmcz)); - STVF (xbuffer[k], x * c655d35); - STVF (ybuffer[k], y * c655d35); - STVF (zbuffer[k], z * c655d35); + Ciecam02::jch2xyz_ciecam02float(x, y, z, + LVF(Jbuffer[k]), LVF(Cbuffer[k]), LVF(hbuffer[k]), + F2V(xw2), F2V(yw2), F2V(zw2), + F2V(nc2), F2V(pow1n), F2V(nbbj), F2V(ncbj), F2V(flj), F2V(dj), F2V(awj), F2V(reccmcz)); + STVF(xbuffer[k], x * c655d35); + STVF(ybuffer[k], y * c655d35); + STVF(zbuffer[k], z * c655d35); } // XYZ2Lab uses a lookup table. The function behind that lut is a cube root. @@ -1589,13 +1584,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw for (int j = 0; j < width; j++) { float Ll, aa, bb; //convert xyz=>lab - Color::XYZ2Lab (xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); + Color::XYZ2Lab(xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); // gamut control in Lab mode; I must study how to do with cIECAM only if (gamu == 1) { float Lprov1, Chprov1; Lprov1 = Ll / 327.68f; - Chprov1 = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; + Chprov1 = sqrtf(SQR(aa) + SQR(bb)) / 327.68f; float2 sincosval; if (Chprov1 == 0.0f) { @@ -1606,15 +1601,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw sincosval.x = bb / (Chprov1 * 327.68f); } -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); -#endif + Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); lab->L[i][j] = Lprov1 * 327.68f; lab->a[i][j] = 327.68f * Chprov1 * sincosval.y; lab->b[i][j] = 327.68f * Chprov1 * sincosval.x; @@ -1647,26 +1635,16 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw if (ciedata) { //update histogram J - hist16JCAM.compressTo (histLCAM); + hist16JCAM.compressTo(histLCAM); //update histogram C - hist16_CCAM.compressTo (histCCAM); + hist16_CCAM.compressTo(histCCAM); } } -#ifdef _DEBUG - - if (settings->verbose) { - t2e.set(); - printf ("CIECAM02 performed in %d usec:\n", t2e.etime (t1e)); - // printf("minc=%f maxc=%f minj=%f maxj=%f\n",minc,maxc,minj,maxj); - } - -#endif - if (settings->autocielab) { if ((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) || (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab) - || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)) { + || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)) { @@ -1722,9 +1700,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // else if(params->dirpyrequalizer.algo=="LA") choice=1; if (rtt == 1) { - float b_l = static_cast (params->dirpyrequalizer.hueskin.getBottomLeft()) / 100.0f; - float t_l = static_cast (params->dirpyrequalizer.hueskin.getTopLeft()) / 100.0f; - float t_r = static_cast (params->dirpyrequalizer.hueskin.getTopRight()) / 100.0f; + float b_l = static_cast(params->dirpyrequalizer.hueskin.getBottomLeft()) / 100.0f; + float t_l = static_cast(params->dirpyrequalizer.hueskin.getTopLeft()) / 100.0f; + float t_r = static_cast(params->dirpyrequalizer.hueskin.getTopRight()) / 100.0f; lab->deleteLab(); dirpyr_equalizercam (ncie, ncie->sh_p, ncie->sh_p, ncie->W, ncie->H, ncie->h_p, ncie->C_p, params->dirpyrequalizer.mult, params->dirpyrequalizer.threshold, params->dirpyrequalizer.skinprotect, b_l, t_l, t_r, scale); //contrast by detail adapted to CIECAM lab->reallocLab(); @@ -1738,26 +1716,21 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw */ } - const float Qredi = ( 4.0f / c_) * ( a_w + 4.0f ); - const float co_e = (pow_F (f_l, 0.25f)); + const float Qredi = (4.0f / c_) * (a_w + 4.0f); + const float co_e = (pow_F(f_l, 0.25f)); -#ifndef _DEBUG #ifdef _OPENMP #pragma omp parallel -#endif #endif { -#ifndef _DEBUG #ifdef _OPENMP #pragma omp for schedule(dynamic, 10) #endif -#endif - for (int i = 0; i < height; i++) // update CieImages with new values after sharpening, defringe, contrast by detail level for (int j = 0; j < width; j++) { - float interm = fabsf (ncie->sh_p[i][j] / (32768.f)); - ncie->J_p[i][j] = 100.0f * SQR (interm); + float interm = fabsf(ncie->sh_p[i][j] / (32768.f)); + ncie->J_p[i][j] = 100.0f * SQR(interm); ncie->Q_p[i][j] = interm * Qredi; ncie->M_p[i][j] = ncie->C_p[i][j] * co_e; } @@ -1767,7 +1740,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw if ((params->colorappearance.tonecie && (epdEnabled)) || (params->sharpening.enabled && settings->autocielab && execsharp) || (params->dirpyrequalizer.enabled && settings->autocielab) || (params->defringe.enabled && settings->autocielab) || (params->sharpenMicro.enabled && settings->autocielab) - || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)) { + || (params->impulseDenoise.enabled && settings->autocielab) || (params->colorappearance.badpixsl > 0 && settings->autocielab)) { ciedata = (params->colorappearance.datacie && pW != 1); @@ -1781,12 +1754,10 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw constexpr float eps = 0.0001f; - const float co_e = (pow_F (f_l, 0.25f)) + eps; + const float co_e = (pow_F(f_l, 0.25f)) + eps; -#ifndef _DEBUG #ifdef _OPENMP #pragma omp parallel -#endif #endif { #ifdef __SSE2__ @@ -1799,10 +1770,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw float *zbuffer = hbuffer; // " #endif -#ifndef _DEBUG #ifdef _OPENMP #pragma omp for schedule(dynamic, 10) -#endif #endif for (int i = 0; i < height; i++) { // update CIECAM with new values after tone-mapping @@ -1810,7 +1779,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // if(epdEnabled) ncie->J_p[i][j]=(100.0f* ncie->Q_p[i][j]*ncie->Q_p[i][j])/(w_h*w_h); if (epdEnabled) { - ncie->J_p[i][j] = (100.0f * ncie->Q_p[i][j] * ncie->Q_p[i][j]) / SQR ((4.f / c) * (aw + 4.f)); + ncie->J_p[i][j] = (100.0f * ncie->Q_p[i][j] * ncie->Q_p[i][j]) / SQR((4.f / c) * (aw + 4.f)); } const float ncie_C_p = (ncie->M_p[i][j]) / co_e; @@ -1832,7 +1801,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw libr = ncie->J_p[i][j]; //327 for J } - posl = (int) (libr * brli); + posl = (int)(libr * brli); hist16JCAM[posl]++; if (curveMode3 == ColorAppearanceParams::CtcMode::CHROMA) { @@ -1840,13 +1809,13 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw colch = ncie_C_p; } else if (curveMode3 == ColorAppearanceParams::CtcMode::SATUR) { chsacol = 450.0f; - colch = 100.f * sqrtf (ncie_C_p / ncie->Q_p[i][j]); + colch = 100.f * sqrtf(ncie_C_p / ncie->Q_p[i][j]); } else { /*if(curveMode3 == ColorAppearanceParams::CTCMode::COLORF)*/ chsacol = 400.f;//327.0f; colch = ncie->M_p[i][j]; } - posc = (int) (colch * chsacol); + posc = (int)(colch * chsacol); hist16_CCAM[posc]++; } @@ -1858,21 +1827,21 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw hbuffer[j] = ncie->h_p[i][j]; #else float xx, yy, zz; - Ciecam02::jch2xyz_ciecam02float ( xx, yy, zz, - ncie->J_p[i][j], ncie_C_p, ncie->h_p[i][j], - xw2, yw2, zw2, + Ciecam02::jch2xyz_ciecam02float(xx, yy, zz, + ncie->J_p[i][j], ncie_C_p, ncie->h_p[i][j], + xw2, yw2, zw2, c2, nc2, pow1n, nbbj, ncbj, flj, czj, dj, awj); float x = (float)xx * 655.35f; float y = (float)yy * 655.35f; float z = (float)zz * 655.35f; float Ll, aa, bb; //convert xyz=>lab - Color::XYZ2Lab (x, y, z, Ll, aa, bb); + Color::XYZ2Lab(x, y, z, Ll, aa, bb); if (gamu == 1) { float Lprov1, Chprov1; Lprov1 = Ll / 327.68f; - Chprov1 = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; + Chprov1 = sqrtf(SQR(aa) + SQR(bb)) / 327.68f; float2 sincosval; if (Chprov1 == 0.0f) { @@ -1884,15 +1853,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw } -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); -#endif + Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); lab->L[i][j] = Lprov1 * 327.68f; lab->a[i][j] = 327.68f * Chprov1 * sincosval.y; @@ -1910,19 +1872,19 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // process line buffers int k; vfloat x, y, z; - vfloat c655d35 = F2V (655.35f); + vfloat c655d35 = F2V(655.35f); for (k = 0; k < bufferLength; k += 4) { - Ciecam02::jch2xyz_ciecam02float ( x, y, z, - LVF (Jbuffer[k]), LVF (Cbuffer[k]), LVF (hbuffer[k]), - F2V (xw2), F2V (yw2), F2V (zw2), - F2V (nc2), F2V (pow1n), F2V (nbbj), F2V (ncbj), F2V (flj), F2V (dj), F2V (awj), F2V (reccmcz)); + Ciecam02::jch2xyz_ciecam02float(x, y, z, + LVF(Jbuffer[k]), LVF(Cbuffer[k]), LVF(hbuffer[k]), + F2V(xw2), F2V(yw2), F2V(zw2), + F2V(nc2), F2V(pow1n), F2V(nbbj), F2V(ncbj), F2V(flj), F2V(dj), F2V(awj), F2V(reccmcz)); x *= c655d35; y *= c655d35; z *= c655d35; - STVF (xbuffer[k], x); - STVF (ybuffer[k], y); - STVF (zbuffer[k], z); + STVF(xbuffer[k], x); + STVF(ybuffer[k], y); + STVF(zbuffer[k], z); } // XYZ2Lab uses a lookup table. The function behind that lut is a cube root. @@ -1930,12 +1892,12 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw for (int j = 0; j < width; j++) { float Ll, aa, bb; //convert xyz=>lab - Color::XYZ2Lab (xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); + Color::XYZ2Lab(xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); if (gamu == 1) { float Lprov1, Chprov1; Lprov1 = Ll / 327.68f; - Chprov1 = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; + Chprov1 = sqrtf(SQR(aa) + SQR(bb)) / 327.68f; float2 sincosval; if (Chprov1 == 0.0f) { @@ -1946,15 +1908,8 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw sincosval.x = bb / (Chprov1 * 327.68f); } -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); -#endif + Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.96f); lab->L[i][j] = Lprov1 * 327.68f; lab->a[i][j] = 327.68f * Chprov1 * sincosval.y; lab->b[i][j] = 327.68f * Chprov1 * sincosval.x; @@ -1975,17 +1930,17 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw if (ciedata) { //update histogram J and Q //update histogram J - hist16JCAM.compressTo (histLCAM); + hist16JCAM.compressTo(histLCAM); //update color histogram M,s,C - hist16_CCAM.compressTo (histCCAM); + hist16_CCAM.compressTo(histCCAM); } } } } //end CIECAM -void ImProcFunctions::moyeqt (Imagefloat* working, float &moyS, float &eqty) +void ImProcFunctions::moyeqt(Imagefloat* working, float &moyS, float &eqty) { BENCHFUN @@ -2013,7 +1968,7 @@ void ImProcFunctions::moyeqt (Imagefloat* working, float &moyS, float &eqty) } static inline void -filmlike_clip_rgb_tone (float *r, float *g, float *b, const float L) +filmlike_clip_rgb_tone(float *r, float *g, float *b, const float L) { float r_ = *r > L ? L : *r; float b_ = *b > L ? L : *b; @@ -2024,18 +1979,18 @@ filmlike_clip_rgb_tone (float *r, float *g, float *b, const float L) } /*static*/ void -filmlike_clip (float *r, float *g, float *b) +filmlike_clip(float *r, float *g, float *b) { // This is Adobe's hue-stable film-like curve with a diagonal, ie only used for clipping. Can probably be further optimized. const float L = 65535.0; if (*r >= *g) { if (*g > *b) { // Case 1: r >= g > b - filmlike_clip_rgb_tone (r, g, b, L); + filmlike_clip_rgb_tone(r, g, b, L); } else if (*b > *r) { // Case 2: b > r >= g - filmlike_clip_rgb_tone (b, r, g, L); + filmlike_clip_rgb_tone(b, r, g, L); } else if (*b > *g) { // Case 3: r >= b > g - filmlike_clip_rgb_tone (r, b, g, L); + filmlike_clip_rgb_tone(r, b, g, L); } else { // Case 4: r >= g == b *r = *r > L ? L : *r; *g = *g > L ? L : *g; @@ -2043,11 +1998,11 @@ filmlike_clip (float *r, float *g, float *b) } } else { if (*r >= *b) { // Case 5: g > r >= b - filmlike_clip_rgb_tone (g, r, b, L); + filmlike_clip_rgb_tone(g, r, b, L); } else if (*b > *g) { // Case 6: b > g > r - filmlike_clip_rgb_tone (b, g, r, L); + filmlike_clip_rgb_tone(b, g, r, L); } else { // Case 7: g >= b > r - filmlike_clip_rgb_tone (g, b, r, L); + filmlike_clip_rgb_tone(g, b, r, L); } } } @@ -2087,7 +2042,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer EditUniqueID editID = pipetteBuffer ? pipetteBuffer->getEditID() : EUID_None; if (editID != EUID_None) { - switch (pipetteBuffer->getDataProvider()->getCurrSubscriber()->getPipetteBufferType()) { + switch (pipetteBuffer->getDataProvider()->getCurrSubscriber()->getPipetteBufferType()) { case (BT_IMAGEFLOAT): editImgFloat = pipetteBuffer->getImgFloatBuffer(); break; @@ -2119,7 +2074,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer static_cast(wprof[2][2] / static_cast(Color::D50z)) } }; - float maxFactorToxyz = max (toxyz[1][0], toxyz[1][1], toxyz[1][2]); + float maxFactorToxyz = max(toxyz[1][0], toxyz[1][1], toxyz[1][2]); float equalR = maxFactorToxyz / toxyz[1][0]; float equalG = maxFactorToxyz / toxyz[1][1]; float equalB = maxFactorToxyz / toxyz[1][2]; @@ -2138,7 +2093,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer }; bool mixchannels = params->chmixer.enabled && - (params->chmixer.red[0] != 100 || params->chmixer.red[1] != 0 || params->chmixer.red[2] != 0 || + (params->chmixer.red[0] != 100 || params->chmixer.red[1] != 0 || params->chmixer.red[2] != 0 || params->chmixer.green[0] != 0 || params->chmixer.green[1] != 100 || params->chmixer.green[2] != 0 || params->chmixer.blue[0] != 0 || params->chmixer.blue[1] != 0 || params->chmixer.blue[2] != 100); @@ -2147,10 +2102,10 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer FlatCurve* vCurve = nullptr; FlatCurve* bwlCurve = nullptr; - FlatCurveType hCurveType = (FlatCurveType)params->hsvequalizer.hcurve.at (0); - FlatCurveType sCurveType = (FlatCurveType)params->hsvequalizer.scurve.at (0); - FlatCurveType vCurveType = (FlatCurveType)params->hsvequalizer.vcurve.at (0); - FlatCurveType bwlCurveType = (FlatCurveType)params->blackwhite.luminanceCurve.at (0); + FlatCurveType hCurveType = (FlatCurveType)params->hsvequalizer.hcurve.at(0); + FlatCurveType sCurveType = (FlatCurveType)params->hsvequalizer.scurve.at(0); + FlatCurveType vCurveType = (FlatCurveType)params->hsvequalizer.vcurve.at(0); + FlatCurveType bwlCurveType = (FlatCurveType)params->blackwhite.luminanceCurve.at(0); bool hCurveEnabled = params->hsvequalizer.enabled && hCurveType > FCT_Linear; bool sCurveEnabled = params->hsvequalizer.enabled && sCurveType > FCT_Linear; bool vCurveEnabled = params->hsvequalizer.enabled && vCurveType > FCT_Linear; @@ -2158,7 +2113,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer // TODO: We should create a 'skip' value like for CurveFactory::complexsgnCurve (rtengine/curves.cc) if (hCurveEnabled) { - hCurve = new FlatCurve (params->hsvequalizer.hcurve); + hCurve = new FlatCurve(params->hsvequalizer.hcurve); if (hCurve->isIdentity()) { delete hCurve; @@ -2168,7 +2123,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } if (sCurveEnabled) { - sCurve = new FlatCurve (params->hsvequalizer.scurve); + sCurve = new FlatCurve(params->hsvequalizer.scurve); if (sCurve->isIdentity()) { delete sCurve; @@ -2178,7 +2133,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } if (vCurveEnabled) { - vCurve = new FlatCurve (params->hsvequalizer.vcurve); + vCurve = new FlatCurve(params->hsvequalizer.vcurve); if (vCurve->isIdentity()) { delete vCurve; @@ -2188,7 +2143,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } if (bwlCurveEnabled) { - bwlCurve = new FlatCurve (params->blackwhite.luminanceCurve); + bwlCurve = new FlatCurve(params->blackwhite.luminanceCurve); if (bwlCurve->isIdentity()) { delete bwlCurve; @@ -2207,24 +2162,24 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer vfloat v_xyz2work[3][3] ALIGNED16; #endif - if ( params->filmSimulation.enabled && !params->filmSimulation.clutFilename.empty() ) { - hald_clut = CLUTStore::getInstance().getClut ( params->filmSimulation.clutFilename ); + if (params->filmSimulation.enabled && !params->filmSimulation.clutFilename.empty()) { + hald_clut = CLUTStore::getInstance().getClut(params->filmSimulation.clutFilename); - if ( hald_clut ) { + if (hald_clut) { clutAndWorkingProfilesAreSame = hald_clut->getProfile() == params->icm.workingProfile; - if ( !clutAndWorkingProfilesAreSame ) { - xyz2clut = ICCStore::getInstance()->workingSpaceInverseMatrix ( hald_clut->getProfile() ); - clut2xyz = ICCStore::getInstance()->workingSpaceMatrix ( hald_clut->getProfile() ); + if (!clutAndWorkingProfilesAreSame) { + xyz2clut = ICCStore::getInstance()->workingSpaceInverseMatrix(hald_clut->getProfile()); + clut2xyz = ICCStore::getInstance()->workingSpaceMatrix(hald_clut->getProfile()); #ifdef __SSE2__ for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { - v_work2xyz[i][j] = F2V (wprof[i][j]); - v_xyz2clut[i][j] = F2V (xyz2clut[i][j]); - v_xyz2work[i][j] = F2V (wiprof[i][j]); - v_clut2xyz[i][j] = F2V (clut2xyz[i][j]); + v_work2xyz[i][j] = F2V(wprof[i][j]); + v_xyz2clut[i][j] = F2V(xyz2clut[i][j]); + v_xyz2work[i][j] = F2V(wiprof[i][j]); + v_clut2xyz[i][j] = F2V(clut2xyz[i][j]); } } @@ -2234,7 +2189,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } - const float film_simulation_strength = static_cast (params->filmSimulation.strength) / 100.0f; + const float film_simulation_strength = static_cast(params->filmSimulation.strength) / 100.0f; const float exp_scale = pow(2.0, expcomp); const float comp = (max(0.0, expcomp) + 1.0) * hlcompr / 100.0; @@ -2340,7 +2295,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float mixerPurple = params->blackwhite.mixerPurple; int algm = 0; - if (params->blackwhite.method == "Desaturation") { + if (params->blackwhite.method == "Desaturation") { algm = 0; } else if (params->blackwhite.method == "LumEqualizer") { algm = 1; @@ -2379,7 +2334,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bool hasgammabw = gammabwr != 1.f || gammabwg != 1.f || gammabwb != 1.f; if (hasColorToning || blackwhite || (params->dirpyrequalizer.cbdlMethod == "bef" && params->dirpyrequalizer.enabled)) { - tmpImage = new Imagefloat (working->getWidth(), working->getHeight()); + tmpImage = new Imagefloat(working->getWidth(), working->getHeight()); } // For tonecurve histogram @@ -2388,11 +2343,11 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (toneCurveHistSize > 0) { histToneCurve.clear(); - histToneCurveCompression = log2 (65536 / toneCurveHistSize); + histToneCurveCompression = log2(65536 / toneCurveHistSize); } // For tonecurve histogram - const float lumimulf[3] = {static_cast (lumimul[0]), static_cast (lumimul[1]), static_cast (lumimul[2])}; + const float lumimulf[3] = {static_cast(lumimul[0]), static_cast(lumimul[1]), static_cast(lumimul[2])}; #define TS 112 @@ -2401,11 +2356,10 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer #pragma omp parallel if (multiThread) #endif { - size_t perChannelSizeBytes = padToAlignment(sizeof (float) * TS * TS + 4 * 64); + size_t perChannelSizeBytes = padToAlignment(sizeof(float) * TS * TS + 4 * 64); AlignedBuffer buffer(3 * perChannelSizeBytes); char *editIFloatBuffer = nullptr; char *editWhateverBuffer = nullptr; - float *rtemp = buffer.data; float *gtemp = &rtemp[perChannelSizeBytes / sizeof(float)]; float *btemp = >emp[perChannelSizeBytes / sizeof(float)]; @@ -2421,17 +2375,17 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float *editIFloatTmpR = nullptr, *editIFloatTmpG = nullptr, *editIFloatTmpB = nullptr, *editWhateverTmp = nullptr; if (editImgFloat) { - editIFloatBuffer = (char *) malloc (3 * sizeof (float) * TS * TS + 20 * 64 + 63); - char *data = (char*) ( ( uintptr_t (editIFloatBuffer) + uintptr_t (63)) / 64 * 64); + editIFloatBuffer = (char *) malloc(3 * sizeof(float) * TS * TS + 20 * 64 + 63); + char *data = (char*)((uintptr_t (editIFloatBuffer) + uintptr_t (63)) / 64 * 64); editIFloatTmpR = (float (*))data; - editIFloatTmpG = (float (*)) ((char*)editIFloatTmpR + sizeof (float) * TS * TS + 4 * 64); - editIFloatTmpB = (float (*)) ((char*)editIFloatTmpG + sizeof (float) * TS * TS + 8 * 64); + editIFloatTmpG = (float (*))((char*)editIFloatTmpR + sizeof(float) * TS * TS + 4 * 64); + editIFloatTmpB = (float (*))((char*)editIFloatTmpG + sizeof(float) * TS * TS + 8 * 64); } if (editWhatever) { - editWhateverBuffer = (char *) malloc (sizeof (float) * TS * TS + 20 * 64 + 63); - char *data = (char*) ( ( uintptr_t (editWhateverBuffer) + uintptr_t (63)) / 64 * 64); + editWhateverBuffer = (char *) malloc(sizeof(float) * TS * TS + 20 * 64 + 63); + char *data = (char*)((uintptr_t (editWhateverBuffer) + uintptr_t (63)) / 64 * 64); editWhateverTmp = (float (*))data; } @@ -2444,7 +2398,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer LUTu histToneCurveThr; if (toneCurveHistSize > 0) { - histToneCurveThr (toneCurveHistSize); + histToneCurveThr(toneCurveHistSize); histToneCurveThr.clear(); } @@ -2456,15 +2410,15 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int jj = 0; jj < working->getWidth(); jj += TS) { istart = ii; jstart = jj; - tH = min (ii + TS, working->getHeight()); - tW = min (jj + TS, working->getWidth()); + tH = min(ii + TS, working->getHeight()); + tW = min(jj + TS, working->getWidth()); for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - rtemp[ti * TS + tj] = working->r (i, j); - gtemp[ti * TS + tj] = working->g (i, j); - btemp[ti * TS + tj] = working->b (i, j); + rtemp[ti * TS + tj] = working->r(i, j); + gtemp[ti * TS + tj] = working->g(i, j); + btemp[ti * TS + tj] = working->b(i, j); } } @@ -2493,7 +2447,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } if (dcpProf) { - dcpProf->step2ApplyTile (rtemp, gtemp, btemp, tW - jstart, tH - istart, TS, asIn); + dcpProf->step2ApplyTile(rtemp, gtemp, btemp, tW - jstart, tH - istart, TS, asIn); } if (params->toneCurve.clampOOG) { @@ -2507,11 +2461,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (OOG(r) || OOG(g) || OOG(b)) { filmlike_clip(&r, &g, &b); } + rtemp[ti * TS + tj] = r; gtemp[ti * TS + tj] = g; btemp[ti * TS + tj] = b; } } + } if (histToneCurveThr) { @@ -2542,11 +2498,14 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer STVF(tmpr[0], tonecurve(LVF(rtemp[ti * TS + tj]))); STVF(tmpg[0], tonecurve(LVF(gtemp[ti * TS + tj]))); STVF(tmpb[0], tonecurve(LVF(btemp[ti * TS + tj]))); + for (int k = 0; k < 4; ++k) { setUnlessOOG(rtemp[ti * TS + tj + k], gtemp[ti * TS + tj + k], btemp[ti * TS + tj + k], tmpr[k], tmpg[k], tmpb[k]); } } + #endif + for (; j < tW; j++, tj++) { //brightness/contrast setUnlessOOG(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], tonecurve[rtemp[ti * TS + tj]], tonecurve[gtemp[ti * TS + tj]], tonecurve[btemp[ti * TS + tj]]); @@ -2625,9 +2584,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float y = toxyz[1][0] * r + toxyz[1][1] * g + toxyz[1][2] * b; float z = toxyz[2][0] * r + toxyz[2][1] * g + toxyz[2][2] * b; - float fx = x < MAXVALF ? Color::cachef[x] : 327.68f * std::cbrt (x / MAXVALF); - float fy = y < MAXVALF ? Color::cachef[y] : 327.68f * std::cbrt (y / MAXVALF); - float fz = z < MAXVALF ? Color::cachef[z] : 327.68f * std::cbrt (z / MAXVALF); + float fx = x < MAXVALF ? Color::cachef[x] : 327.68f * std::cbrt(x / MAXVALF); + float fy = y < MAXVALF ? Color::cachef[y] : 327.68f * std::cbrt(y / MAXVALF); + float fz = z < MAXVALF ? Color::cachef[z] : 327.68f * std::cbrt(z / MAXVALF); float a_1 = 500.0f * (fx - fy); float b_1 = 200.0f * (fy - fz); @@ -2656,7 +2615,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer //gamut control if (settings->rgbcurveslumamode_gamut) { float Lpro = L_2 / 327.68f; - float Chpro = sqrtf (SQR (a_1) + SQR (b_1)) / 327.68f; + float Chpro = sqrtf(SQR(a_1) + SQR(b_1)) / 327.68f; float HH = NAN; // we set HH to NAN, because then it will be calculated in Color::gamutLchonly only if needed // float HH = xatan2f(b_1, a_1); // According to mathematical laws we can get the sin and cos of HH by simple operations even if we don't calculate HH @@ -2670,21 +2629,14 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer sincosval.x = b_1 / (Chpro * 327.68f); } -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, Lpro, Chpro, r, g, b, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, Lpro, Chpro, r, g, b, wip, highlight, 0.15f, 0.96f); -#endif + Color::gamutLchonly(HH, sincosval, Lpro, Chpro, r, g, b, wip, highlight, 0.15f, 0.96f); //end of gamut control } else { float x_, y_, z_; //calculate RGB with L_2 and old value of a and b - Color::Lab2XYZ (L_2, a_1, b_1, x_, y_, z_) ; - Color::xyz2rgb (x_, y_, z_, r, g, b, wip); + Color::Lab2XYZ(L_2, a_1, b_1, x_, y_, z_) ; + Color::xyz2rgb(x_, y_, z_, r, g, b, wip); } setUnlessOOG(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], r, g, b); @@ -2697,7 +2649,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { float h, s, v; - Color::rgb2hsv (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], h, s, v); + Color::rgb2hsv(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], h, s, v); editWhateverTmp[ti * TS + tj] = h; } } @@ -2705,11 +2657,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (sat != 0 || hCurveEnabled || sCurveEnabled || vCurveEnabled) { const float satby100 = sat / 100.f; + for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { float h, s, v; Color::rgb2hsvtc(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], h, s, v); h /= 6.f; + if (sat > 0) { s = std::max(0.f, intp(satby100, 1.f - SQR(SQR(1.f - std::min(s, 1.0f))), s)); } else { /*if (sat < 0)*/ @@ -2729,10 +2683,10 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (sCurveEnabled) { //shift saturation - float satparam = (sCurve->getVal (double (h)) - 0.5) * 2; + float satparam = (sCurve->getVal(double (h)) - 0.5) * 2; if (satparam > 0.00001f) { - s = (1.f - satparam) * s + satparam * (1.f - SQR (1.f - min (s, 1.0f))); + s = (1.f - satparam) * s + satparam * (1.f - SQR(1.f - min(s, 1.0f))); if (s < 0.f) { s = 0.f; @@ -2750,10 +2704,10 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer //shift value float valparam = vCurve->getVal(h) - 0.5; - valparam *= (1.f - SQR (SQR (1.f - min (s, 1.0f)))); + valparam *= (1.f - SQR(SQR(1.f - min(s, 1.0f)))); if (valparam > 0.00001f) { - v = (1.f - valparam) * v + valparam * (1.f - SQR (1.f - min (v, 1.0f))); // SQR (SQR to increase action and avoid artifacts + v = (1.f - valparam) * v + valparam * (1.f - SQR(1.f - min(v, 1.0f))); // SQR (SQR to increase action and avoid artifacts if (v < 0) { v = 0; @@ -2793,21 +2747,22 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer const float iplow = ctColorCurve.low; const float iphigh = ctColorCurve.high; //2 colours - ctColorCurve.getVal (iphigh, xh, yh, zh); - ctColorCurve.getVal (iplow, xl, yl, zl); + ctColorCurve.getVal(iphigh, xh, yh, zh); + ctColorCurve.getVal(iplow, xl, yl, zl); - Color::xyz2rgb (xh, yh, zh, rh, gh, bh, wip); - Color::xyz2rgb (xl, yl, zl, rl, gl, bl, wip); + Color::xyz2rgb(xh, yh, zh, rh, gh, bh, wip); + Color::xyz2rgb(xl, yl, zl, rl, gl, bl, wip); //reteave rgb value with s and l =1 - retreavergb (rl, gl, bl); + retreavergb(rl, gl, bl); const float krl = rl / (rl + gl + bl); const float kgl = gl / (rl + gl + bl); const float kbl = bl / (rl + gl + bl); - retreavergb (rh, gh, bh); + retreavergb(rh, gh, bh); const float krh = rh / (rh + gh + bh); const float kgh = gh / (rh + gh + bh); const float kbh = bh / (rh + gh + bh); constexpr int mode = 0; + for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { toning2col(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], iplow, iphigh, krl, kgl, kbl, krh, kgh, kbh, SatLow, SatHigh, balanS, balanH, reducac, mode, preser, strProtect); @@ -2847,13 +2802,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bool twocol = true;//true=500 color false=2 color int metchrom = 0; - if (params->colorToning.twocolor == "Std" ) { + if (params->colorToning.twocolor == "Std") { metchrom = 0; - } else if (params->colorToning.twocolor == "All" ) { + } else if (params->colorToning.twocolor == "All") { metchrom = 1; } else if (params->colorToning.twocolor == "Separ") { metchrom = 2; - } else if (params->colorToning.twocolor == "Two" ) { + } else if (params->colorToning.twocolor == "Two") { metchrom = 3; } @@ -2876,7 +2831,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer twoc = 1; // 500 colours } - if (params->colorToning.method == "Lab") { + if (params->colorToning.method == "Lab") { algm = 1; } else if (params->colorToning.method == "Lch") { algm = 2; //in case of @@ -2889,12 +2844,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float g = gtemp[ti * TS + tj]; float b = btemp[ti * TS + tj]; float ro, go, bo; - labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); + labtoning(r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); setUnlessOOG(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], ro, go, bo); } } } - } else if (params->colorToning.method.substr (0, 3) == "RGB" && opautili) { + } else if (params->colorToning.method.substr(0, 3) == "RGB" && opautili) { // color toning for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { @@ -2905,7 +2860,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer // Luminance = (0.299f*r + 0.587f*g + 0.114f*b) float s, l; - Color::rgb2slfloat (r, g, b, s, l); + Color::rgb2slfloat(r, g, b, s, l); float l_ = Color::gammatab_srgb1[l * 65535.f]; @@ -2917,12 +2872,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } float r2, g2, b2; - ctColorCurve.getVal (l_, r2, g2, b2); // get the color from the color curve + ctColorCurve.getVal(l_, r2, g2, b2); // get the color from the color curve float h2, s2, l2; - Color::rgb2hslfloat (r2, g2, b2, h2, s2, l2); // transform this new color to hsl + Color::rgb2hslfloat(r2, g2, b2, h2, s2, l2); // transform this new color to hsl - Color::hsl2rgbfloat (h2, s + ((1.f - s) * (1.f - l) * 0.7f), l, r2, g2, b2); + Color::hsl2rgbfloat(h2, s + ((1.f - s) * (1.f - l) * 0.7f), l, r2, g2, b2); rtemp[ti * TS + tj] = r + (r2 - r) * opacity; // merge the color to the old color, depending on the opacity gtemp[ti * TS + tj] = g + (g2 - g) * opacity; @@ -2940,13 +2895,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = jstart, tj = 0; j < tW; j++, tj++) { float X, Y, Z, L, aa, bb; //rgb=>lab - Color::rgbxyz (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], X, Y, Z, wp); + Color::rgbxyz(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], X, Y, Z, wp); //convert Lab - Color::XYZ2Lab (X, Y, Z, L, aa, bb); + Color::XYZ2Lab(X, Y, Z, L, aa, bb); //end rgb=>lab - float HH = xatan2f (bb, aa); // HH hue in -3.141 +3.141 + float HH = xatan2f(bb, aa); // HH hue in -3.141 +3.141 - editWhateverTmp[ti * TS + tj] = float (Color::huelab_to_huehsv2 (HH)); + editWhateverTmp[ti * TS + tj] = float (Color::huelab_to_huehsv2(HH)); } } } @@ -2957,36 +2912,36 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (beforeCurveMode == BlackWhiteParams::TcMode::STD_BW) { // Standard for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - const StandardToneCurve& userToneCurvebw = static_cast (customToneCurvebw1); - userToneCurvebw.Apply (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); + const StandardToneCurve& userToneCurvebw = static_cast(customToneCurvebw1); + userToneCurvebw.Apply(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); } } } else if (beforeCurveMode == BlackWhiteParams::TcMode::FILMLIKE_BW) { // Adobe like for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - const AdobeToneCurve& userToneCurvebw = static_cast (customToneCurvebw1); - userToneCurvebw.Apply (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); + const AdobeToneCurve& userToneCurvebw = static_cast(customToneCurvebw1); + userToneCurvebw.Apply(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); } } } else if (beforeCurveMode == BlackWhiteParams::TcMode::SATANDVALBLENDING_BW) { // apply the curve on the saturation and value channels for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - const SatAndValueBlendingToneCurve& userToneCurvebw = static_cast (customToneCurvebw1); + const SatAndValueBlendingToneCurve& userToneCurvebw = static_cast(customToneCurvebw1); // rtemp[ti * TS + tj] = CLIP (rtemp[ti * TS + tj]); // gtemp[ti * TS + tj] = CLIP (gtemp[ti * TS + tj]); // btemp[ti * TS + tj] = CLIP (btemp[ti * TS + tj]); - userToneCurvebw.Apply (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); + userToneCurvebw.Apply(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); } } } else if (beforeCurveMode == BlackWhiteParams::TcMode::WEIGHTEDSTD_BW) { // apply the curve to the rgb channels, weighted for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - const WeightedStdToneCurve& userToneCurvebw = static_cast (customToneCurvebw1); + const WeightedStdToneCurve& userToneCurvebw = static_cast(customToneCurvebw1); // rtemp[ti * TS + tj] = CLIP (rtemp[ti * TS + tj]); // gtemp[ti * TS + tj] = CLIP (gtemp[ti * TS + tj]); // btemp[ti * TS + tj] = CLIP (btemp[ti * TS + tj]); - userToneCurvebw.Apply (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); + userToneCurvebw.Apply(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); } } } @@ -3018,7 +2973,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer //gamma correction: pseudo TRC curve if (hasgammabw) { - Color::trcGammaBW (r, g, b, gammabwr, gammabwg, gammabwb); + Color::trcGammaBW(r, g, b, gammabwr, gammabwg, gammabwb); } #endif @@ -3031,7 +2986,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (hasgammabw) { //gamma correction: pseudo TRC curve - Color::trcGammaBWRow (&rtemp[ti * TS], >emp[ti * TS], &btemp[ti * TS], tW - jstart, gammabwr, gammabwg, gammabwb); + Color::trcGammaBWRow(&rtemp[ti * TS], >emp[ti * TS], &btemp[ti * TS], tW - jstart, gammabwr, gammabwg, gammabwb); } #endif @@ -3042,12 +2997,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = jstart, tj = 0; j < tW; j++, tj++) { //rgb => xyz float X, Y, Z; - Color::rgbxyz (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], X, Y, Z, wp); + Color::rgbxyz(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], X, Y, Z, wp); //xyz => Lab float L, aa, bb; - Color::XYZ2Lab (X, Y, Z, L, aa, bb); - float CC = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; //CC chromaticity in 0..180 or more - float HH = xatan2f (bb, aa); // HH hue in -3.141 +3.141 + Color::XYZ2Lab(X, Y, Z, L, aa, bb); + float CC = sqrtf(SQR(aa) + SQR(bb)) / 327.68f; //CC chromaticity in 0..180 or more + float HH = xatan2f(bb, aa); // HH hue in -3.141 +3.141 float2 sincosval; if (CC == 0.0f) { @@ -3060,14 +3015,14 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (bwlCurveEnabled) { L /= 32768.f; - double hr = Color::huelab_to_huehsv2 (HH); + double hr = Color::huelab_to_huehsv2(HH); float valparam = (bwlCurve->getVal(hr) - 0.5) * 2.0; //get l_r=f(H) float kcc = (CC / 70.f); //take Chroma into account...70 "middle" of chromaticity (arbitrary and simple), one can imagine other algorithme //reduct action for low chroma and increase action for high chroma valparam *= kcc; if (valparam > 0.f) { - L = (1.f - valparam) * L + valparam * (1.f - SQR (SQR (SQR (SQR (1.f - min (L, 1.0f)))))); // SQR (SQR((SQR) to increase action in low light + L = (1.f - valparam) * L + valparam * (1.f - SQR(SQR(SQR(SQR(1.f - min(L, 1.0f)))))); // SQR (SQR((SQR) to increase action in low light } else { L *= (1.f + valparam); //for negative } @@ -3077,26 +3032,19 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float RR, GG, BB; L /= 327.68f; -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, L, CC, RR, GG, BB, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, L, CC, RR, GG, BB, wip, highlight, 0.15f, 0.96f); -#endif + Color::gamutLchonly(HH, sincosval, L, CC, RR, GG, BB, wip, highlight, 0.15f, 0.96f); L *= 327.68f; //convert l => rgb - Color::L2XYZ (L, X, Y, Z); + Color::L2XYZ(L, X, Y, Z); float newRed; // We use the red channel for bw - Color::xyz2r (X, Y, Z, newRed, wip); + Color::xyz2r(X, Y, Z, newRed, wip); rtemp[ti * TS + tj] = gtemp[ti * TS + tj] = btemp[ti * TS + tj] = newRed; #ifndef __SSE2__ if (hasgammabw) { //gamma correction: pseudo TRC curve - Color::trcGammaBW (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], gammabwr, gammabwg, gammabwb); + Color::trcGammaBW(rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], gammabwr, gammabwg, gammabwb); } #endif @@ -3106,7 +3054,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (hasgammabw) { //gamma correction: pseudo TRC curve - Color::trcGammaBWRow (&rtemp[ti * TS], >emp[ti * TS], &btemp[ti * TS], tW - jstart, gammabwr, gammabwg, gammabwb); + Color::trcGammaBWRow(&rtemp[ti * TS], >emp[ti * TS], &btemp[ti * TS], tW - jstart, gammabwr, gammabwg, gammabwb); } #endif @@ -3127,19 +3075,19 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer #ifdef __SSE2__ for (; j < tW - 3; j += 4, tj += 4) { - vfloat sourceR = LVF (rtemp[ti * TS + tj]); - vfloat sourceG = LVF (gtemp[ti * TS + tj]); - vfloat sourceB = LVF (btemp[ti * TS + tj]); + vfloat sourceR = LVF(rtemp[ti * TS + tj]); + vfloat sourceG = LVF(gtemp[ti * TS + tj]); + vfloat sourceB = LVF(btemp[ti * TS + tj]); vfloat x; vfloat y; vfloat z; - Color::rgbxyz (sourceR, sourceG, sourceB, x, y, z, v_work2xyz); - Color::xyz2rgb (x, y, z, sourceR, sourceG, sourceB, v_xyz2clut); + Color::rgbxyz(sourceR, sourceG, sourceB, x, y, z, v_work2xyz); + Color::xyz2rgb(x, y, z, sourceR, sourceG, sourceB, v_xyz2clut); - STVF (clutr[tj], sourceR); - STVF (clutg[tj], sourceG); - STVF (clutb[tj], sourceB); + STVF(clutr[tj], sourceR); + STVF(clutg[tj], sourceG); + STVF(clutb[tj], sourceB); } #endif @@ -3150,8 +3098,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float sourceB = btemp[ti * TS + tj]; float x, y, z; - Color::rgbxyz ( sourceR, sourceG, sourceB, x, y, z, wprof ); - Color::xyz2rgb (x, y, z, clutr[tj], clutg[tj], clutb[tj], xyz2clut); + Color::rgbxyz(sourceR, sourceG, sourceB, x, y, z, wprof); + Color::xyz2rgb(x, y, z, clutr[tj], clutg[tj], clutb[tj], xyz2clut); } } else { memcpy(clutr, &rtemp[ti * TS], sizeof(float) * TS); @@ -3165,14 +3113,14 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float &sourceB = clutb[tj]; // Apply gamma sRGB (default RT) - sourceR = Color::gamma_srgbclipped (sourceR); - sourceG = Color::gamma_srgbclipped (sourceG); - sourceB = Color::gamma_srgbclipped (sourceB); + sourceR = Color::gamma_srgbclipped(sourceR); + sourceG = Color::gamma_srgbclipped(sourceG); + sourceB = Color::gamma_srgbclipped(sourceB); } - hald_clut->getRGB ( + hald_clut->getRGB( film_simulation_strength, - std::min (TS, tW - jstart), + std::min(TS, tW - jstart), clutr, clutg, clutb, @@ -3185,9 +3133,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float &sourceB = clutb[tj]; // Apply inverse gamma sRGB - sourceR = Color::igamma_srgb (out_rgbx[tj * 4 + 0]); - sourceG = Color::igamma_srgb (out_rgbx[tj * 4 + 1]); - sourceB = Color::igamma_srgb (out_rgbx[tj * 4 + 2]); + sourceR = Color::igamma_srgb(out_rgbx[tj * 4 + 0]); + sourceG = Color::igamma_srgb(out_rgbx[tj * 4 + 1]); + sourceB = Color::igamma_srgb(out_rgbx[tj * 4 + 2]); } if (!clutAndWorkingProfilesAreSame) { @@ -3198,19 +3146,19 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer #ifdef __SSE2__ for (; j < tW - 3; j += 4, tj += 4) { - vfloat sourceR = LVF (clutr[tj]); - vfloat sourceG = LVF (clutg[tj]); - vfloat sourceB = LVF (clutb[tj]); + vfloat sourceR = LVF(clutr[tj]); + vfloat sourceG = LVF(clutg[tj]); + vfloat sourceB = LVF(clutb[tj]); vfloat x; vfloat y; vfloat z; - Color::rgbxyz (sourceR, sourceG, sourceB, x, y, z, v_clut2xyz); - Color::xyz2rgb (x, y, z, sourceR, sourceG, sourceB, v_xyz2work); + Color::rgbxyz(sourceR, sourceG, sourceB, x, y, z, v_clut2xyz); + Color::xyz2rgb(x, y, z, sourceR, sourceG, sourceB, v_xyz2work); - STVF (clutr[tj], sourceR); - STVF (clutg[tj], sourceG); - STVF (clutb[tj], sourceB); + STVF(clutr[tj], sourceR); + STVF(clutg[tj], sourceG); + STVF(clutb[tj], sourceB); } #endif @@ -3221,8 +3169,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float &sourceB = clutb[tj]; float x, y, z; - Color::rgbxyz (sourceR, sourceG, sourceB, x, y, z, clut2xyz); - Color::xyz2rgb ( x, y, z, sourceR, sourceG, sourceB, wiprof ); + Color::rgbxyz(sourceR, sourceG, sourceB, x, y, z, clut2xyz); + Color::xyz2rgb(x, y, z, sourceR, sourceG, sourceB, wiprof); } } @@ -3241,19 +3189,21 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer // filling the pipette buffer by the content of the temp pipette buffers if (editImgFloat) { - editImgFloat->r (i, j) = editIFloatTmpR[ti * TS + tj]; - editImgFloat->g (i, j) = editIFloatTmpG[ti * TS + tj]; - editImgFloat->b (i, j) = editIFloatTmpB[ti * TS + tj]; + editImgFloat->r(i, j) = editIFloatTmpR[ti * TS + tj]; + editImgFloat->g(i, j) = editIFloatTmpG[ti * TS + tj]; + editImgFloat->b(i, j) = editIFloatTmpB[ti * TS + tj]; } else if (editWhatever) { - editWhatever->v (i, j) = editWhateverTmp[ti * TS + tj]; + editWhatever->v(i, j) = editWhateverTmp[ti * TS + tj]; } } } } + // ready, fill lab for (int i = istart, ti = 0; i < tH; i++, ti++) { Color::RGB2Lab(&rtemp[ti * TS], >emp[ti * TS], &btemp[ti * TS], &(lab->L[i][jstart]), &(lab->a[i][jstart]), &(lab->b[i][jstart]), toxyz, tW - jstart); } + if (hasColorToningLabGrid) { colorToningLabGrid(lab, jstart, tW, istart, tH, false); } @@ -3263,27 +3213,27 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = jstart, tj = 0; j < tW; j++, tj++) { // filling the pipette buffer by the content of the temp pipette buffers if (editImgFloat) { - editImgFloat->r (i, j) = editIFloatTmpR[ti * TS + tj]; - editImgFloat->g (i, j) = editIFloatTmpG[ti * TS + tj]; - editImgFloat->b (i, j) = editIFloatTmpB[ti * TS + tj]; + editImgFloat->r(i, j) = editIFloatTmpR[ti * TS + tj]; + editImgFloat->g(i, j) = editIFloatTmpG[ti * TS + tj]; + editImgFloat->b(i, j) = editIFloatTmpB[ti * TS + tj]; } else if (editWhatever) { - editWhatever->v (i, j) = editWhateverTmp[ti * TS + tj]; + editWhatever->v(i, j) = editWhateverTmp[ti * TS + tj]; } - tmpImage->r (i, j) = rtemp[ti * TS + tj]; - tmpImage->g (i, j) = gtemp[ti * TS + tj]; - tmpImage->b (i, j) = btemp[ti * TS + tj]; + tmpImage->r(i, j) = rtemp[ti * TS + tj]; + tmpImage->g(i, j) = gtemp[ti * TS + tj]; + tmpImage->b(i, j) = btemp[ti * TS + tj]; } } } } if (editIFloatBuffer) { - free (editIFloatBuffer); + free(editIFloatBuffer); } if (editWhateverBuffer) { - free (editWhateverBuffer); + free(editWhateverBuffer); } #ifdef _OPENMP @@ -3326,9 +3276,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer double kng = srgb / ng; double knb = srgb / nb; double sk = knr + kng + knb; - autor = (float) (100.0 * knr / sk); - autog = (float) (100.0 * kng / sk); - autob = (float) (100.0 * knb / sk); + autor = (float)(100.0 * knr / sk); + autog = (float)(100.0 * kng / sk); + autob = (float)(100.0 * knb / sk); } @@ -3345,9 +3295,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } float filcor; - Color::computeBWMixerConstants (params->blackwhite.setting, params->blackwhite.filter, params->blackwhite.algo, filcor, - bwr, bwg, bwb, mixerOrange, mixerYellow, mixerCyan, mixerPurple, mixerMagenta, - params->blackwhite.autoc, complem, kcorec, rrm, ggm, bbm); + Color::computeBWMixerConstants(params->blackwhite.setting, params->blackwhite.filter, params->blackwhite.algo, filcor, + bwr, bwg, bwb, mixerOrange, mixerYellow, mixerCyan, mixerPurple, mixerMagenta, + params->blackwhite.autoc, complem, kcorec, rrm, ggm, bbm); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) @@ -3357,13 +3307,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = 0; j < tW; j++) { //mix channel - tmpImage->r (i, j) = tmpImage->g (i, j) = tmpImage->b (i, j) = /*CLIP*/ ((bwr * tmpImage->r (i, j) + bwg * tmpImage->g (i, j) + bwb * tmpImage->b (i, j)) * kcorec); + tmpImage->r(i, j) = tmpImage->g(i, j) = tmpImage->b(i, j) = /*CLIP*/ ((bwr * tmpImage->r(i, j) + bwg * tmpImage->g(i, j) + bwb * tmpImage->b(i, j)) * kcorec); #ifndef __SSE2__ //gamma correction: pseudo TRC curve if (hasgammabw) { - Color::trcGammaBW (tmpImage->r (i, j), tmpImage->g (i, j), tmpImage->b (i, j), gammabwr, gammabwg, gammabwb); + Color::trcGammaBW(tmpImage->r(i, j), tmpImage->g(i, j), tmpImage->b(i, j), gammabwr, gammabwg, gammabwb); } #endif @@ -3373,7 +3323,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (hasgammabw) { //gamma correction: pseudo TRC curve - Color::trcGammaBWRow (tmpImage->r (i), tmpImage->g (i), tmpImage->b (i), tW, gammabwr, gammabwg, gammabwb); + Color::trcGammaBWRow(tmpImage->r(i), tmpImage->g(i), tmpImage->b(i), tW, gammabwr, gammabwg, gammabwb); } #endif @@ -3387,7 +3337,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = 0; i < tH; i++) { for (int j = 0; j < tW; j++) { - editWhatever->v (i, j) = Color::gamma2curve[tmpImage->r (i, j)] / 65535.f; // assuming that r=g=b + editWhatever->v(i, j) = Color::gamma2curve[tmpImage->r(i, j)] / 65535.f; // assuming that r=g=b } } } @@ -3401,8 +3351,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = 0; i < tH; i++) { for (int j = 0; j < tW; j++) { - const StandardToneCurve& userToneCurve = static_cast (customToneCurvebw2); - userToneCurve.Apply (tmpImage->r (i, j), tmpImage->g (i, j), tmpImage->b (i, j)); + const StandardToneCurve& userToneCurve = static_cast(customToneCurvebw2); + userToneCurve.Apply(tmpImage->r(i, j), tmpImage->g(i, j), tmpImage->b(i, j)); } } } else if (afterCurveMode == BlackWhiteParams::TcMode::WEIGHTEDSTD_BW) { // apply the curve to the rgb channels, weighted @@ -3412,13 +3362,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = 0; i < tH; i++) { //for ulterior usage if bw data modified for (int j = 0; j < tW; j++) { - const WeightedStdToneCurve& userToneCurve = static_cast (customToneCurvebw2); + const WeightedStdToneCurve& userToneCurve = static_cast(customToneCurvebw2); // tmpImage->r (i, j) = CLIP (tmpImage->r (i, j)); // tmpImage->g (i, j) = CLIP (tmpImage->g (i, j)); // tmpImage->b (i, j) = CLIP (tmpImage->b (i, j)); - userToneCurve.Apply (tmpImage->r (i, j), tmpImage->g (i, j), tmpImage->b (i, j)); + userToneCurve.Apply(tmpImage->r(i, j), tmpImage->g(i, j), tmpImage->b(i, j)); } } } @@ -3435,9 +3385,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = 0; i < tH; i++) { for (int j = 0; j < tW; j++) { - const float r = tmpImage->r (i, j); - const float g = tmpImage->g (i, j); - const float b = tmpImage->b (i, j); + const float r = tmpImage->r(i, j); + const float g = tmpImage->g(i, j); + const float b = tmpImage->b(i, j); const float lumbefore = 0.299f * r + 0.587f * g + 0.114f * b; @@ -3479,19 +3429,19 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer const float iphigh = ctColorCurve.high; //2 colours - ctColorCurve.getVal (iphigh, xh, yh, zh); - ctColorCurve.getVal (iplow, xl, yl, zl); + ctColorCurve.getVal(iphigh, xh, yh, zh); + ctColorCurve.getVal(iplow, xl, yl, zl); - Color::xyz2rgb (xh, yh, zh, rh, gh, bh, wip); - Color::xyz2rgb (xl, yl, zl, rl, gl, bl, wip); + Color::xyz2rgb(xh, yh, zh, rh, gh, bh, wip); + Color::xyz2rgb(xl, yl, zl, rl, gl, bl, wip); //retrieve rgb value with s and l =1 - retreavergb (rl, gl, bl); + retreavergb(rl, gl, bl); const float krl = rl / (rl + gl + bl); const float kgl = gl / (rl + gl + bl); const float kbl = bl / (rl + gl + bl); - retreavergb (rh, gh, bh); + retreavergb(rh, gh, bh); const float krh = rh / (rh + gh + bh); const float kgh = gh / (rh + gh + bh); const float kbh = bh / (rh + gh + bh); @@ -3513,13 +3463,13 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bool twocol = true; int metchrom = 0; - if (params->colorToning.twocolor == "Std" ) { + if (params->colorToning.twocolor == "Std") { metchrom = 0; - } else if (params->colorToning.twocolor == "All" ) { + } else if (params->colorToning.twocolor == "All") { metchrom = 1; } else if (params->colorToning.twocolor == "Separ") { metchrom = 2; - } else if (params->colorToning.twocolor == "Two" ) { + } else if (params->colorToning.twocolor == "Two") { metchrom = 3; } @@ -3543,7 +3493,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer twoc = 1; // 500 colours } - if (params->colorToning.method == "Lab") { + if (params->colorToning.method == "Lab") { algm = 1; } else if (params->colorToning.method == "Lch") { algm = 2; //in case of @@ -3556,18 +3506,18 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = 0; i < tH; i++) { for (int j = 0; j < tW; j++) { - float r = tmpImage->r (i, j); - float g = tmpImage->g (i, j); - float b = tmpImage->b (i, j); + float r = tmpImage->r(i, j); + float g = tmpImage->g(i, j); + float b = tmpImage->b(i, j); float ro, bo, go; - labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); + labtoning(r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); setUnlessOOG(tmpImage->r(i, j), tmpImage->g(i, j), tmpImage->b(i, j), ro, go, bo); } } } } - else if (params->colorToning.method.substr (0, 3) == "RGB" && opautili) { + else if (params->colorToning.method.substr(0, 3) == "RGB" && opautili) { // color toning #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 5) @@ -3634,6 +3584,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = 0; i < tH; i++) { Color::RGB2Lab(tmpImage->r(i), tmpImage->g(i), tmpImage->b(i), lab->L[i], lab->a[i], lab->b[i], toxyz, tW); + if (hasColorToningLabGrid) { colorToningLabGrid(lab, 0, tW, i, i + 1, false); } @@ -3663,7 +3614,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer if (params->localContrast.enabled) { // Alberto's local contrast - localContrast(lab); + localContrast(lab, lab->L, params->localContrast, false, scale); } } @@ -3673,10 +3624,10 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer * @param g green input and in exit new g * @param b blue input and in exit new b **/ -void ImProcFunctions::retreavergb (float &r, float &g, float &b) +void ImProcFunctions::retreavergb(float &r, float &g, float &b) { - float mini = min (r, g, b); - float maxi = max (r, g, b); + float mini = min(r, g, b); + float maxi = max(r, g, b); float kkm = 65535.f / maxi; if (b == mini && r == maxi) { @@ -3714,7 +3665,7 @@ void ImProcFunctions::retreavergb (float &r, float &g, float &b) * @param bb first degree parameter * @param cc third parameter **/ -void ImProcFunctions::secondeg_end (float reducac, float vinf, float &aa, float &bb, float &cc) +void ImProcFunctions::secondeg_end(float reducac, float vinf, float &aa, float &bb, float &cc) { float zrd = reducac; //value at me linear =0.5 float v0 = vinf; //max shadows @@ -3735,7 +3686,7 @@ void ImProcFunctions::secondeg_end (float reducac, float vinf, float &aa, float * @param aa second degree parameter * @param bb first degree parameter **/ -void ImProcFunctions::secondeg_begin (float reducac, float vend, float &aam, float &bbm) +void ImProcFunctions::secondeg_begin(float reducac, float vend, float &aam, float &bbm) { aam = (2.f - 4.f * reducac) / (vend * vend); bbm = 1.f / vend - aam * vend; @@ -3780,17 +3731,18 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, if (v > v0) { float aa, bb, cc; - secondeg_end (reducac, v0, aa, bb, cc); + secondeg_end(reducac, v0, aa, bb, cc); kl = aa * v * v + bb * v + cc; //verified ==> exact } else { float aab, bbb; - secondeg_begin (0.7f, v0, aab, bbb); + secondeg_begin(0.7f, v0, aab, bbb); kl = aab * v * v + bbb * v; } } else { //bw coefficient to preserve same results as before for satlimtopacity = 0.5 (default) rlo = strProtect * 0.8f; //0.4 rlm = strProtect * 2.2f; //1.1 rlh = strProtect * 2.4f; //1.2 + if (v > 0.15f) { kl = (-1.f / 0.85f) * v + 1.f / 0.85f; //Low light ==> decrease action after v=0.15 } @@ -3849,12 +3801,12 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, if (v < v0m) { float aam, bbm; float vend = v0m; - secondeg_begin (reducac, vend, aam, bbm); + secondeg_begin(reducac, vend, aam, bbm); km = aam * v * v + bbm * v; //verification = good } else { float v0mm = 0.5f; //max float aamm, bbmm, ccmm; - secondeg_end (reducac, v0mm, aamm, bbmm, ccmm); + secondeg_end(reducac, v0mm, aamm, bbmm, ccmm); km = aamm * v * v + bbmm * v + ccmm; //verification good } @@ -3870,6 +3822,7 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g -= 20000.f * RedM; b -= 20000.f * RedM; } + // r = CLIP(r); // g = CLIP(g); // b = CLIP(b); @@ -3887,6 +3840,7 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g += 10000.f * GreenM; b -= 20000.f * GreenM; } + // r = CLIP(r); // g = CLIP(g); // b = CLIP(b); @@ -3904,6 +3858,7 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g -= 20000.f * BlueM; b += 10000.f * BlueM; } + // r = CLIP(r); // g = CLIP(g); // b = CLIP(b); @@ -3912,9 +3867,10 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, //high tones constexpr float v00 = 0.8f; //max action float aa0, bb0; - secondeg_begin (reducac, v00, aa0, bb0); + secondeg_begin(reducac, v00, aa0, bb0); float kh; + if (v > v00) { //max action kh = (1.f - v) / (1.f - v00); //High tones } else { @@ -3983,7 +3939,7 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, * @param balanH [0..1] balance for highlights (same slider than for balanS) * @param reducac value of the reduction in the middle of the range for second degree, increase or decrease action **/ -void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &go, float &bo, float iplow, float iphigh, float krl, float kgl, float kbl, float krh, float kgh, float kbh, float SatLow, float SatHigh, float balanS, float balanH, float reducac, int mode, int preser, float strProtect) +void ImProcFunctions::toning2col(float r, float g, float b, float &ro, float &go, float &bo, float iplow, float iphigh, float krl, float kgl, float kbl, float krh, float kgh, float kbh, float SatLow, float SatHigh, float balanS, float balanH, float reducac, int mode, int preser, float strProtect) { const float lumbefore = 0.299f * r + 0.587f * g + 0.114f * b; const float v = max(r, g, b) / 65535.f; @@ -3995,22 +3951,25 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g //second degree float aa, bb, cc; //fixed value of reducac =0.4; - secondeg_end (reducac, iplow, aa, bb, cc); + secondeg_end(reducac, iplow, aa, bb, cc); float aab, bbb; - secondeg_begin (0.7f, iplow, aab, bbb); + secondeg_begin(0.7f, iplow, aab, bbb); if (SatLow > 0.f) { float kl = 1.f; + if (v > iplow) { kl = aa * v * v + bb * v + cc; } else if (mode == 0) { kl = aab * v * v + bbb * v; } + const float kmgb = min(r, g, b); + if (kmgb < 20000.f) { //I have tested ...0.85 compromise... - kl *= pow_F ((kmgb / 20000.f), 0.85f); + kl *= pow_F((kmgb / 20000.f), 0.85f); } const float factor = 20000.f * SatLow * kl * rlo * balanS; @@ -4043,10 +4002,11 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g //high tones float aa0, bb0; //fixed value of reducac ==0.4; - secondeg_begin (reducac, iphigh, aa0, bb0); + secondeg_begin(reducac, iphigh, aa0, bb0); if (SatHigh > 0.f) { float kh = 1.f; + if (v > iphigh) { kh = (1.f - v) / (1.f - iphigh); //Low light ==> decrease action after iplow } else { @@ -4054,11 +4014,13 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g } const float kmgb = max(r, g, b); + if (kmgb > 45535.f) { constexpr float cora = 1.f / (45535.f - 65535.f); constexpr float corb = 1.f - cora * 45535.f; kh *= kmgb * cora + corb; } + const float factor = 20000.f * SatHigh * kh * rlh * balanH; r += factor * (krh > 0.f ? krh : 0.f); g += factor * (kgh > 0.f ? kgh : 0.f); @@ -4070,6 +4032,7 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g } float preserv = 1.f; + if (preser == 1) { float lumafter = 0.299f * r + 0.587f * g + 0.114f * b; preserv = lumbefore / lumafter; @@ -4098,7 +4061,7 @@ void ImProcFunctions::labtoning (float r, float g, float b, float &ro, float &go float realL; float h, s, l; - Color::rgb2hsl (ro, go, bo, h, s, l); + Color::rgb2hsl(ro, go, bo, h, s, l); float x2, y2, z2; float xl, yl, zl; @@ -4109,10 +4072,10 @@ void ImProcFunctions::labtoning (float r, float g, float b, float &ro, float &go } if (twoc == 1) { - ctColorCurve.getVal (l, x2, y2, z2); + ctColorCurve.getVal(l, x2, y2, z2); } else { - ctColorCurve.getVal (iphigh, x2, y2, z2); - ctColorCurve.getVal (iplow, xl, yl, zl); + ctColorCurve.getVal(iphigh, x2, y2, z2); + ctColorCurve.getVal(iplow, xl, yl, zl); } realL = l; @@ -4142,9 +4105,9 @@ void ImProcFunctions::labtoning (float r, float g, float b, float &ro, float &go } if (algm == 1) { - Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); + Color::interpolateRGBColor(realL, iplow, iphigh, algm, opacity, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); } else { - Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity2, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); + Color::interpolateRGBColor(realL, iplow, iphigh, algm, opacity2, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); } } @@ -4171,7 +4134,6 @@ void ImProcFunctions::luminanceCurve (LabImage* lold, LabImage* lnew, const LUTf void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW, LabImage* lold, LabImage* lnew, const LUTf& acurve, const LUTf& bcurve, const LUTf& satcurve, const LUTf& lhskcurve, const LUTf& clcurve, LUTf & curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histLCurve) { - int W = lold->W; int H = lold->H; @@ -4184,7 +4146,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW if (editID != EUID_None) { - switch (pipetteBuffer->getDataProvider()->getCurrSubscriber()->getPipetteBufferType()) { + switch (pipetteBuffer->getDataProvider()->getCurrSubscriber()->getPipetteBufferType()) { case (BT_IMAGEFLOAT): break; @@ -4258,7 +4220,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW bool chutili = false; if (params->labCurve.chromaticity > -100) { - chCurve = new FlatCurve (params->labCurve.chcurve); + chCurve = new FlatCurve(params->labCurve.chcurve); if (chCurve->isIdentity()) { delete chCurve; @@ -4273,7 +4235,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW bool lhutili = false; if (params->labCurve.chromaticity > -100) { - lhCurve = new FlatCurve (params->labCurve.lhcurve); + lhCurve = new FlatCurve(params->labCurve.lhcurve); if (lhCurve->isIdentity()) { delete lhCurve; @@ -4288,7 +4250,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW bool hhutili = false; if (params->labCurve.chromaticity > -100) { - hhCurve = new FlatCurve (params->labCurve.hhcurve); + hhCurve = new FlatCurve(params->labCurve.hhcurve); if (hhCurve->isIdentity()) { delete hhCurve; @@ -4303,13 +4265,6 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW const float histLFactor = pW != 1 ? histLCurve.getSize() / 100.f : 1.f; const float histCFactor = pW != 1 ? histCCurve.getSize() / 48000.f : 1.f; -#ifdef _DEBUG - MyTime t1e, t2e; - t1e.set(); - // init variables to display Munsell corrections - MunsellDebugInfo* MunsDebugInfo = new MunsellDebugInfo(); -#endif - float adjustr = 1.0f; // if(params->labCurve.avoidclip ){ @@ -4408,11 +4363,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW }; #ifdef _OPENMP -#ifdef _DEBUG - #pragma omp parallel default(shared) firstprivate(lold, lnew, MunsDebugInfo, pW) if (multiThread) -#else #pragma omp parallel if (multiThread) -#endif #endif { #ifdef __SSE2__ @@ -4428,27 +4379,27 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW // only if user activate Lab adjustments if (autili || butili || ccutili || cclutili || chutili || lhutili || hhutili || clcutili || utili || chromaticity) { - Color::LabGamutMunsell (lold->L[i], lold->a[i], lold->b[i], W, /*corMunsell*/true, /*lumaMuns*/false, params->toneCurve.hrenabled, /*gamut*/true, wip); + Color::LabGamutMunsell(lold->L[i], lold->a[i], lold->b[i], W, /*corMunsell*/true, /*lumaMuns*/false, params->toneCurve.hrenabled, /*gamut*/true, wip); } #ifdef __SSE2__ // precalculate some values using SSE if (bwToning || (!autili && !butili)) { - __m128 c327d68v = _mm_set1_ps (327.68f); + __m128 c327d68v = _mm_set1_ps(327.68f); __m128 av, bv; int k; for (k = 0; k < W - 3; k += 4) { - av = LVFU (lold->a[i][k]); - bv = LVFU (lold->b[i][k]); - STVF (HHBuffer[k], xatan2f (bv, av)); + av = LVFU(lold->a[i][k]); + bv = LVFU(lold->b[i][k]); + STVF(HHBuffer[k], xatan2f(bv, av)); STVF (CCBuffer[k], vsqrtf (SQRV (av) + SQRV (bv)) / c327d68v); } for (; k < W; k++) { - HHBuffer[k] = xatan2f (lold->b[i][k], lold->a[i][k]); - CCBuffer[k] = sqrt (SQR (lold->a[i][k]) + SQR (lold->b[i][k])) / 327.68f; + HHBuffer[k] = xatan2f(lold->b[i][k], lold->a[i][k]); + CCBuffer[k] = sqrt(SQR(lold->a[i][k]) + SQR(lold->b[i][k])) / 327.68f; } } @@ -4470,8 +4421,8 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW HH = HHBuffer[j]; CC = CCBuffer[j]; #else - HH = xatan2f (lold->b[i][j], lold->a[i][j]); - CC = sqrt (SQR (lold->a[i][j]) + SQR (lold->b[i][j])) / 327.68f; + HH = xatan2f(lold->b[i][j], lold->a[i][j]); + CC = sqrt(SQR(lold->a[i][j]) + SQR(lold->b[i][j])) / 327.68f; #endif // According to mathematical laws we can get the sin and cos of HH by simple operations @@ -4489,7 +4440,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW } if (editPipette && editID == EUID_Lab_LCurve) { - editWhatever->v (i, j) = LIM01 (Lin / 32768.0f); // Lab L pipette + editWhatever->v(i, j) = LIM01 (Lin / 32768.0f); // Lab L pipette } lnew->L[i][j] = curve[Lin]; @@ -4499,10 +4450,10 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW if (editPipette) { if (editID == EUID_Lab_aCurve) { // Lab a pipette float chromapipa = lold->a[i][j] + (32768.f * 1.28f); - editWhatever->v (i, j) = LIM01 ((chromapipa) / (65536.f * 1.28f)); + editWhatever->v(i, j) = LIM01 ((chromapipa) / (65536.f * 1.28f)); } else if (editID == EUID_Lab_bCurve) { //Lab b pipette float chromapipb = lold->b[i][j] + (32768.f * 1.28f); - editWhatever->v (i, j) = LIM01 ((chromapipb) / (65536.f * 1.28f)); + editWhatever->v(i, j) = LIM01 ((chromapipb) / (65536.f * 1.28f)); } } @@ -4527,13 +4478,13 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW HH = HHBuffer[j]; CC = CCBuffer[j]; } else { - CC = sqrt (SQR (atmp) + SQR (btmp)) / 327.68f; - HH = xatan2f (btmp, atmp); + CC = sqrt(SQR(atmp) + SQR(btmp)) / 327.68f; + HH = xatan2f(btmp, atmp); } #else - CC = sqrt (SQR (atmp) + SQR (btmp)) / 327.68f; - HH = xatan2f (btmp, atmp); + CC = sqrt(SQR(atmp) + SQR(btmp)) / 327.68f; + HH = xatan2f(btmp, atmp); #endif // According to mathematical laws we can get the sin and cos of HH by simple operations @@ -4553,8 +4504,8 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW if (editPipette) if (editID == EUID_Lab_LHCurve || editID == EUID_Lab_CHCurve || editID == EUID_Lab_HHCurve) {//H pipette - float valpar = Color::huelab_to_huehsv2 (HH); - editWhatever->v (i, j) = valpar; + float valpar = Color::huelab_to_huehsv2(HH); + editWhatever->v(i, j) = valpar; } if (lhutili) { // L=f(H) @@ -4563,6 +4514,8 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW l_r = Lprov1 / 100.f; { float valparam = lhCurve->getVal(Color::huelab_to_huehsv2(HH)) - 0.5; //get l_r=f(H) + //float valparam = float ((lhCurve->getVal (hr - 0.5f)*2));//get l_r=f(H) + float valparamneg; valparamneg = valparam; float kcc = (CC / amountchroma); //take Chroma into account...40 "middle low" of chromaticity (arbitrary and simple), one can imagine other algorithme @@ -4571,7 +4524,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW valparamneg *= kcc; //slightly different for negative if (valparam > 0.f) { - l_r = (1.f - valparam) * l_r + valparam * (1.f - SQR (((SQR (1.f - min (l_r, 1.0f)))))); + l_r = (1.f - valparam) * l_r + valparam * (1.f - SQR(((SQR(1.f - min(l_r, 1.0f)))))); } else //for negative { @@ -4582,7 +4535,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW Lprov1 = l_r * 100.f; - float Chprov2 = sqrt (SQR (atmp) + SQR (btmp)) / 327.68f; + float Chprov2 = sqrt(SQR(atmp) + SQR(btmp)) / 327.68f; //Gamut control especially for negative values slightly different from gamutlchonly bool inRGB; @@ -4591,7 +4544,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW float aprov1 = Chprov2 * sincosval.y; float bprov1 = Chprov2 * sincosval.x; - float fy = (Color::c1By116 * Lprov1 ) + Color::c16By116; + float fy = (Color::c1By116 * Lprov1) + Color::c16By116; float fx = (0.002f * aprov1) + fy; float fz = fy - (0.005f * bprov1); @@ -4599,7 +4552,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW float z_ = 65535.f * Color::f2xyz (fz) * Color::D50z; float y_ = Lprov1 > Color::epskapf ? 65535.f * fy * fy * fy : 65535.f * Lprov1 / Color::kappaf; float R, G, B; - Color::xyz2rgb (x_, y_, z_, R, G, B, wip); + Color::xyz2rgb(x_, y_, z_, R, G, B, wip); if (R < 0.0f || G < 0.0f || B < 0.0f) { if (Lprov1 < 0.1f) { @@ -4624,8 +4577,9 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW // calculate C=f(H) if (chutili) { - double hr = Color::huelab_to_huehsv2 (HH); + double hr = Color::huelab_to_huehsv2(HH); float chparam = (chCurve->getVal(hr) - 0.5) * 2.0; //get C=f(H) + float chromaChfactor = 1.0f + chparam; atmp *= chromaChfactor;//apply C=f(H) btmp *= chromaChfactor; @@ -4644,7 +4598,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW if (chromapro > 1.f) { float scale = scaleConst;//reduction in normal zone float scaleext = 1.f;//reduction in transition zone - Color::scalered ( rstprotection, chromapro, 0.0, HH, protect_redh, scale, scaleext);//1.0 + Color::scalered(rstprotection, chromapro, 0.0, HH, protect_redh, scale, scaleext); //1.0 float interm = (chromapro - 1.f); factorskin = 1.f + (interm * scale); factorskinext = 1.f + (interm * scaleext); @@ -4658,7 +4612,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW //simulate very approximative gamut f(L) : with pyramid transition float dred /*=55.f*/;//C red value limit - if (Lprov1 < 25.f) { + if (Lprov1 < 25.f) { dred = 40.f; } else if (Lprov1 < 30.f) { dred = 3.f * Lprov1 - 35.f; @@ -4673,12 +4627,12 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW // end pyramid // Test if chroma is in the normal range first - Color::transitred ( HH, Chprov1, dred, factorskin, protect_red, factorskinext, protect_redh, factorsat, factorsat); + Color::transitred(HH, Chprov1, dred, factorskin, protect_red, factorskinext, protect_redh, factorsat, factorsat); atmp *= factorsat; btmp *= factorsat; if (editPipette && editID == EUID_Lab_CLCurve) { - editWhatever->v (i, j) = LIM01 (LL / 100.f); // Lab C=f(L) pipette + editWhatever->v(i, j) = LIM01 (LL / 100.f); // Lab C=f(L) pipette } if (clut && LL > 0.f) { // begin C=f(L) @@ -4712,7 +4666,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW deltaHH = protect_redhcur; //transition hue if (chromaCfactor > 0.f) { - Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext); //1.0 + Color::scalered(rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext); //1.0 } if (chromaCfactor > 1.f) { @@ -4726,7 +4680,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW factorsat = chromaCfactor; factor = factorsat; - Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); + Color::transitred(HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); atmp = LIM(atmp * factor, min(-42000.f, atmp), max(42000.f, atmp)); btmp = LIM(btmp * factor, min(-42000.f, btmp), max(42000.f, btmp)); } @@ -4737,13 +4691,13 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW // I have placed C=f(C) after all C treatments to assure maximum amplitude of "C" if (editPipette && editID == EUID_Lab_CCurve) { - float chromapip = sqrt (SQR (atmp) + SQR (btmp) + 0.001f); - editWhatever->v (i, j) = LIM01 ((chromapip) / (48000.f)); + float chromapip = sqrt(SQR(atmp) + SQR(btmp) + 0.001f); + editWhatever->v(i, j) = LIM01 ((chromapip) / (48000.f)); }//Lab C=f(C) pipette if (ccut) { float factorskin, factorsat, factor, factorskinext; - float chroma = sqrt (SQR (atmp) + SQR (btmp) + 0.001f); + float chroma = sqrt(SQR(atmp) + SQR(btmp) + 0.001f); float chromaCfactor = (satcurve[chroma * adjustr]) / (chroma * adjustr); //apply C=f(C) float curf = 0.7f; //empirical coeff because curve is more progressive float scale = 100.0f / 100.1f; //reduction in normal zone for curve CC @@ -4773,7 +4727,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW deltaHH = protect_redhcur; //transition hue if (chromaCfactor > 0.f) { - Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext); //1.0 + Color::scalered(rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext); //1.0 } if (chromaCfactor > 1.f) { @@ -4790,7 +4744,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW factorsat = chromaCfactor; factor = factorsat; - Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); + Color::transitred(HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); atmp *= factor; btmp *= factor; } @@ -4804,8 +4758,8 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW } if (editPipette && editID == EUID_Lab_LCCurve) { - float chromapiplc = sqrt (SQR (atmp) + SQR (btmp) + 0.001f); - editWhatever->v (i, j) = LIM01 ((chromapiplc) / (48000.f)); + float chromapiplc = sqrt(SQR(atmp) + SQR(btmp) + 0.001f); + editWhatever->v(i, j) = LIM01 ((chromapiplc) / (48000.f)); }//Lab L=f(C) pipette @@ -4824,7 +4778,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW float yy = 0.0f; if (Chprov1 < chrmin) { - yy = SQR (Chprov1 / chrmin) * xx; + yy = SQR(Chprov1 / chrmin) * xx; } else { yy = xx; //avoid artifact for low C } @@ -4835,7 +4789,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW skdeltaHH = 0.001f; } - if (HH > skbeg && HH < skend ) { + if (HH > skbeg && HH < skend) { zz = yy; } else if (HH > skbeg - skdeltaHH && HH <= skbeg) { //transition aa = yy / skdeltaHH; @@ -4847,7 +4801,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW zz = aa * HH + bb; } - float chroma = sqrt (SQR (atmp) + SQR (btmp) + 0.001f); + float chroma = sqrt(SQR(atmp) + SQR(btmp) + 0.001f); float Lc = (lhskcurve[chroma * adjustr]) / (chroma * adjustr); //apply L=f(C) Lc = (Lc - 1.0f) * zz + 1.0f; //reduct action Lprov1 *= Lc; //adjust luminance @@ -4858,7 +4812,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW histLCurve[Lprov1 * histLFactor]++; } - Chprov1 = sqrt (SQR (atmp) + SQR (btmp)) / 327.68f; + Chprov1 = sqrt(SQR(atmp) + SQR(btmp)) / 327.68f; // labCurve.bwtoning option allows to decouple modulation of a & b curves by saturation // with bwtoning enabled the net effect of a & b curves is visible @@ -4871,27 +4825,18 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW //gamutmap Lch ==> preserve Hue,but a little slower than gamutbdy for high values...and little faster for low values if (gamutLch) { float R, G, B; - -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else - //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f); -#endif + Color::gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f); lnew->L[i][j] = Lprov1 * 327.68f; -// float2 sincosval = xsincosf(HH); lnew->a[i][j] = 327.68f * Chprov1 * sincosval.y; lnew->b[i][j] = 327.68f * Chprov1 * sincosval.x; } else { //use gamutbdy //Luv limiter float Y, u, v; - Color::Lab2Yuv (lnew->L[i][j], atmp, btmp, Y, u, v); + Color::Lab2Yuv(lnew->L[i][j], atmp, btmp, Y, u, v); //Yuv2Lab includes gamut restriction map - Color::Yuv2Lab (Y, u, v, lnew->L[i][j], lnew->a[i][j], lnew->b[i][j], wp); + Color::Yuv2Lab(Y, u, v, lnew->L[i][j], lnew->a[i][j], lnew->b[i][j], wp); } if (utili || autili || butili || ccut || clut || cclutili || chutili || lhutili || hhutili || clcutili || chromaticity) { @@ -4899,16 +4844,11 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW float correctlum = 0.f; Lprov1 = lnew->L[i][j] / 327.68f; - Chprov = sqrt (SQR (lnew->a[i][j]) + SQR (lnew->b[i][j])) / 327.68f; - -#ifdef _DEBUG - Color::AllMunsellLch (/*lumaMuns*/true, Lprov1, LL, HH, Chprov, memChprov, correctionHue, correctlum, MunsDebugInfo); -#else - Color::AllMunsellLch (/*lumaMuns*/true, Lprov1, LL, HH, Chprov, memChprov, correctionHue, correctlum); -#endif + Chprov = sqrt(SQR(lnew->a[i][j]) + SQR(lnew->b[i][j])) / 327.68f; + Color::AllMunsellLch(/*lumaMuns*/true, Lprov1, LL, HH, Chprov, memChprov, correctionHue, correctlum); if (correctionHue != 0.f || correctlum != 0.f) { - if (fabs (correctionHue) < 0.015f) { + if (fabs(correctionHue) < 0.015f) { HH += correctlum; // correct only if correct Munsell chroma very little. } @@ -4918,7 +4858,7 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW else if(fabs(correctionHue) < 0.1f) HH+=0.35f*correctlum; else if(fabs(correctionHue) < 0.015f) HH+=correctlum; // correct only if correct Munsell chroma very little. */ - sincosval = xsincosf (HH + correctionHue); + sincosval = xsincosf(HH + correctionHue); } lnew->a[i][j] = 327.68f * Chprov * sincosval.y; // apply Munsell @@ -4942,18 +4882,6 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW } } // end of parallelization -#ifdef _DEBUG - - if (settings->verbose) { - t2e.set(); - printf ("Color::AllMunsellLch (correction performed in %d usec):\n", t2e.etime (t1e)); - printf (" Munsell chrominance: MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad dep=%u\n", MunsDebugInfo->maxdhue[0], MunsDebugInfo->maxdhue[1], MunsDebugInfo->maxdhue[2], MunsDebugInfo->maxdhue[3], MunsDebugInfo->depass); - printf (" Munsell luminance : MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad dep=%u\n", MunsDebugInfo->maxdhuelum[0], MunsDebugInfo->maxdhuelum[1], MunsDebugInfo->maxdhuelum[2], MunsDebugInfo->maxdhuelum[3], MunsDebugInfo->depassLum); - } - - delete MunsDebugInfo; -#endif - if (chCurve) { delete chCurve; } @@ -4976,84 +4904,84 @@ void ImProcFunctions::chromiLuminanceCurve (PipetteBuffer *pipetteBuffer, int pW //void ImProcFunctions::colorCurve (LabImage* lold, LabImage* lnew) //{ - /* LUT cmultiplier(181021); +/* LUT cmultiplier(181021); - double boost_a = ((float)params->colorBoost.amount + 100.0) / 100.0; - double boost_b = ((float)params->colorBoost.amount + 100.0) / 100.0; + double boost_a = ((float)params->colorBoost.amount + 100.0) / 100.0; + double boost_b = ((float)params->colorBoost.amount + 100.0) / 100.0; - double c, amul = 1.0, bmul = 1.0; - if (boost_a > boost_b) { - c = boost_a; - if (boost_a > 0) - bmul = boost_b / boost_a; - } - else { - c = boost_b; - if (boost_b > 0) - amul = boost_a / boost_b; + double c, amul = 1.0, bmul = 1.0; + if (boost_a > boost_b) { + c = boost_a; + if (boost_a > 0) + bmul = boost_b / boost_a; + } + else { + c = boost_b; + if (boost_b > 0) + amul = boost_a / boost_b; + } + + if (params->colorBoost.enable_saturationlimiter && c>1.0) { + // re-generate color multiplier lookup table + double d = params->colorBoost.saturationlimit / 3.0; + double alpha = 0.5; + double threshold1 = alpha * d; + double threshold2 = c*d*(alpha+1.0) - d; + for (int i=0; i<=181020; i++) { // lookup table stores multipliers with a 0.25 chrominance resolution + double chrominance = (double)i/4.0; + if (chrominance < threshold1) + cmultiplier[i] = c; + else if (chrominance < d) + cmultiplier[i] = (c / (2.0*d*(alpha-1.0)) * (chrominance-d)*(chrominance-d) + c*d/2.0 * (alpha+1.0) ) / chrominance; + else if (chrominance < threshold2) + cmultiplier[i] = (1.0 / (2.0*d*(c*(alpha+1.0)-2.0)) * (chrominance-d)*(chrominance-d) + c*d/2.0 * (alpha+1.0) ) / chrominance; + else + cmultiplier[i] = 1.0; } + } - if (params->colorBoost.enable_saturationlimiter && c>1.0) { - // re-generate color multiplier lookup table - double d = params->colorBoost.saturationlimit / 3.0; - double alpha = 0.5; - double threshold1 = alpha * d; - double threshold2 = c*d*(alpha+1.0) - d; - for (int i=0; i<=181020; i++) { // lookup table stores multipliers with a 0.25 chrominance resolution - double chrominance = (double)i/4.0; - if (chrominance < threshold1) - cmultiplier[i] = c; - else if (chrominance < d) - cmultiplier[i] = (c / (2.0*d*(alpha-1.0)) * (chrominance-d)*(chrominance-d) + c*d/2.0 * (alpha+1.0) ) / chrominance; - else if (chrominance < threshold2) - cmultiplier[i] = (1.0 / (2.0*d*(c*(alpha+1.0)-2.0)) * (chrominance-d)*(chrominance-d) + c*d/2.0 * (alpha+1.0) ) / chrominance; - else - cmultiplier[i] = 1.0; + float eps = 0.001; + double shift_a = params->colorShift.a + eps, shift_b = params->colorShift.b + eps; + + float** oa = lold->a; + float** ob = lold->b; + + #pragma omp parallel for if (multiThread) + for (int i=0; iH; i++) + for (int j=0; jW; j++) { + + double wanted_c = c; + if (params->colorBoost.enable_saturationlimiter && c>1) { + float chroma = (float)(4.0 * sqrt((oa[i][j]+shift_a)*(oa[i][j]+shift_a) + (ob[i][j]+shift_b)*(ob[i][j]+shift_b))); + wanted_c = cmultiplier [chroma]; } - } - float eps = 0.001; - double shift_a = params->colorShift.a + eps, shift_b = params->colorShift.b + eps; - - float** oa = lold->a; - float** ob = lold->b; - - #pragma omp parallel for if (multiThread) - for (int i=0; iH; i++) - for (int j=0; jW; j++) { - - double wanted_c = c; - if (params->colorBoost.enable_saturationlimiter && c>1) { - float chroma = (float)(4.0 * sqrt((oa[i][j]+shift_a)*(oa[i][j]+shift_a) + (ob[i][j]+shift_b)*(ob[i][j]+shift_b))); - wanted_c = cmultiplier [chroma]; + double real_c = wanted_c; + if (wanted_c >= 1.0 && params->colorBoost.avoidclip) { + double cclip = 100000.0; + double cr = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, 3.079935, -1.5371515, -0.54278342); + double cg = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, -0.92123418, 1.87599, 0.04524418); + double cb = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, 0.052889682, -0.20404134, 1.15115166); + if (cr>1.0 && cr1.0 && cg1.0 && cb= 1.0 && params->colorBoost.avoidclip) { - double cclip = 100000.0; - double cr = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, 3.079935, -1.5371515, -0.54278342); - double cg = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, -0.92123418, 1.87599, 0.04524418); - double cb = tightestroot ((double)lnew->L[i][j]/655.35, (double)(oa[i][j]+shift_a)*amul, (double)(ob[i][j]+shift_b)*bmul, 0.052889682, -0.20404134, 1.15115166); - if (cr>1.0 && cr1.0 && cg1.0 && cba[i][j] = LIM(nna,-32000.0f,32000.0f); - lnew->b[i][j] = LIM(nnb,-32000.0f,32000.0f); } - */ - //delete [] cmultiplier; + + float nna = ((oa[i][j]+shift_a) * real_c * amul); + float nnb = ((ob[i][j]+shift_b) * real_c * bmul); + lnew->a[i][j] = LIM(nna,-32000.0f,32000.0f); + lnew->b[i][j] = LIM(nnb,-32000.0f,32000.0f); + } +*/ +//delete [] cmultiplier; //} -void ImProcFunctions::impulsedenoise (LabImage* lab) +void ImProcFunctions::impulsedenoise(LabImage* lab) { if (params->impulseDenoise.enabled && lab->W >= 8 && lab->H >= 8) @@ -5063,7 +4991,7 @@ void ImProcFunctions::impulsedenoise (LabImage* lab) } } -void ImProcFunctions::impulsedenoisecam (CieImage* ncie, float **buffers[3]) +void ImProcFunctions::impulsedenoisecam(CieImage* ncie, float **buffers[3]) { if (params->impulseDenoise.enabled && ncie->W >= 8 && ncie->H >= 8) @@ -5073,57 +5001,57 @@ void ImProcFunctions::impulsedenoisecam (CieImage* ncie, float **buffers[3]) } } -void ImProcFunctions::defringe (LabImage* lab) +void ImProcFunctions::defringe(LabImage* lab) { if (params->defringe.enabled && lab->W >= 8 && lab->H >= 8) { - PF_correct_RT (lab, params->defringe.radius, params->defringe.threshold); + PF_correct_RT(lab, params->defringe.radius, params->defringe.threshold); } } -void ImProcFunctions::defringecam (CieImage* ncie) +void ImProcFunctions::defringecam(CieImage* ncie) { if (params->defringe.enabled && ncie->W >= 8 && ncie->H >= 8) { - PF_correct_RTcam (ncie, params->defringe.radius, params->defringe.threshold); + PF_correct_RTcam(ncie, params->defringe.radius, params->defringe.threshold); } } -void ImProcFunctions::badpixcam (CieImage* ncie, double rad, int thr, int mode, float chrom, bool hotbad) +void ImProcFunctions::badpixcam(CieImage* ncie, double rad, int thr, int mode, float chrom, bool hotbad) { if (ncie->W >= 8 && ncie->H >= 8) { - Badpixelscam (ncie, rad, thr, mode, chrom, hotbad); + Badpixelscam(ncie, rad, thr, mode, chrom, hotbad); } } -void ImProcFunctions::badpixlab (LabImage* lab, double rad, int thr, float chrom) +void ImProcFunctions::badpixlab(LabImage* lab, double rad, int thr, float chrom) { if (lab->W >= 8 && lab->H >= 8) { - BadpixelsLab (lab, rad, thr, chrom); + BadpixelsLab(lab, rad, thr, chrom); } } -void ImProcFunctions::dirpyrequalizer (LabImage* lab, int scale) +void ImProcFunctions::dirpyrequalizer(LabImage* lab, int scale) { if (params->dirpyrequalizer.enabled && lab->W >= 8 && lab->H >= 8) { - float b_l = static_cast (params->dirpyrequalizer.hueskin.getBottomLeft()) / 100.f; - float t_l = static_cast (params->dirpyrequalizer.hueskin.getTopLeft()) / 100.f; - float t_r = static_cast (params->dirpyrequalizer.hueskin.getTopRight()) / 100.f; + float b_l = static_cast(params->dirpyrequalizer.hueskin.getBottomLeft()) / 100.f; + float t_l = static_cast(params->dirpyrequalizer.hueskin.getTopLeft()) / 100.f; + float t_r = static_cast(params->dirpyrequalizer.hueskin.getTopRight()) / 100.f; // if (params->dirpyrequalizer.algo=="FI") choice=0; // else if(params->dirpyrequalizer.algo=="LA") choice=1; if (params->dirpyrequalizer.gamutlab && params->dirpyrequalizer.skinprotect != 0) { constexpr float artifact = 4.f; constexpr float chrom = 50.f; - ImProcFunctions::badpixlab (lab, artifact / scale, 5, chrom); //for artifacts + ImProcFunctions::badpixlab(lab, artifact / scale, 5, chrom); //for artifacts } //dirpyrLab_equalizer(lab, lab, params->dirpyrequalizer.mult); - dirpyr_equalizer (lab->L, lab->L, lab->W, lab->H, lab->a, lab->b, params->dirpyrequalizer.mult, params->dirpyrequalizer.threshold, params->dirpyrequalizer.skinprotect, b_l, t_l, t_r, scale); + dirpyr_equalizer(lab->L, lab->L, lab->W, lab->H, lab->a, lab->b, params->dirpyrequalizer.mult, params->dirpyrequalizer.threshold, params->dirpyrequalizer.skinprotect, b_l, t_l, t_r, scale); } } -void ImProcFunctions::EPDToneMapCIE (CieImage *ncie, float a_w, float c_, int Wid, int Hei, float minQ, float maxQ, unsigned int Iterates, int skip) +void ImProcFunctions::EPDToneMapCIE(CieImage *ncie, float a_w, float c_, int Wid, int Hei, float minQ, float maxQ, unsigned int Iterates, int skip) { if (!params->epd.enabled) { @@ -5135,7 +5063,7 @@ void ImProcFunctions::EPDToneMapCIE (CieImage *ncie, float a_w, float c_, int Wi } */ float stren = params->epd.strength; - float edgest = params->epd.edgeStopping; + const float edgest = std::min(params->epd.edgeStopping, params->localContrast.enabled ? 3.0 : 4.0); float sca = params->epd.scale; float gamm = params->epd.gamma; float rew = params->epd.reweightingIterates; @@ -5150,7 +5078,7 @@ void ImProcFunctions::EPDToneMapCIE (CieImage *ncie, float a_w, float c_, int Wi Qpro = maxQ; } - EdgePreservingDecomposition epd (Wid, Hei); + EdgePreservingDecomposition epd(Wid, Hei); #ifdef _OPENMP #pragma omp parallel for @@ -5161,7 +5089,7 @@ void ImProcFunctions::EPDToneMapCIE (CieImage *ncie, float a_w, float c_, int Wi ncie->Q_p[i][j] = gamm * ncie->Q_p[i][j] / (Qpro); } - float Compression = expf (-stren); //This modification turns numbers symmetric around 0 into exponents. + float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. float DetailBoost = stren; if (stren < 0.0f) { @@ -5175,16 +5103,13 @@ void ImProcFunctions::EPDToneMapCIE (CieImage *ncie, float a_w, float c_, int Wi //Jacques Desmis : always Iterates=5 for compatibility images between preview and output - epd.CompressDynamicRange (Qpr, sca / (float)skip, edgest, Compression, DetailBoost, Iterates, rew); + epd.CompressDynamicRange(Qpr, sca / (float)skip, edgest, Compression, DetailBoost, Iterates, rew); //Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping. - float s = (1.0f + 38.7889f) * powf (Compression, 1.5856f) / (1.0f + 38.7889f * powf (Compression, 1.5856f)); -#ifndef _DEBUG + float s = (1.0f + 38.7889f) * powf(Compression, 1.5856f) / (1.0f + 38.7889f * powf(Compression, 1.5856f)); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,10) #endif -#endif - for (int i = 0; i < Hei; i++) for (int j = 0; j < Wid; j++) { ncie->Q_p[i][j] = (ncie->Q_p[i][j] * Qpro) / gamm; @@ -5230,71 +5155,35 @@ void ImProcFunctions::EPDToneMapCIE (CieImage *ncie, float a_w, float c_, int Wi */ } - -//Map tones by way of edge preserving decomposition. Is this the right way to include source? -//#include "EdgePreservingDecomposition.cc" -void ImProcFunctions::EPDToneMap (LabImage *lab, unsigned int Iterates, int skip) +void ImProcFunctions::EPDToneMaplocal(int sp, LabImage *lab, LabImage *tmp1, unsigned int Iterates, int skip) { - //Hasten access to the parameters. -// EPDParams *p = (EPDParams *)(¶ms->epd); - //Enabled? Leave now if not. -// if(!p->enabled) return; - if (!params->epd.enabled) { - return; - } -/* - if (params->wavelet.enabled && params->wavelet.tmrs != 0) { - return; - } -*/ - float stren = params->epd.strength; - float edgest = params->epd.edgeStopping; - float sca = params->epd.scale; - float gamm = params->epd.gamma; - float rew = params->epd.reweightingIterates; + float stren = ((float)params->locallab.spots.at(sp).stren); + const float edgest = std::min(params->locallab.spots.at(sp).estop, params->localContrast.enabled ? 3.0 : 4.0); + + float sca = ((float)params->locallab.spots.at(sp).scaltm); + float gamm = ((float)params->locallab.spots.at(sp).gamma); + float satur = ((float)params->locallab.spots.at(sp).satur) / 100.f; + float rew = ((float)params->locallab.spots.at(sp).rewei); //Pointers to whole data and size of it. float *L = lab->L[0]; float *a = lab->a[0]; float *b = lab->b[0]; - size_t N = lab->W * lab->H; - EdgePreservingDecomposition epd (lab->W, lab->H); + unsigned int i, N = lab->W * lab->H; + int WW = lab->W ; +// int HH = lab->H ; + EdgePreservingDecomposition epd(lab->W, lab->H); //Due to the taking of logarithms, L must be nonnegative. Further, scale to 0 to 1 using nominal range of L, 0 to 15 bit. - float minL = FLT_MAX; - float maxL = 0.f; -#ifdef _OPENMP - #pragma omp parallel -#endif - { - float lminL = FLT_MAX; - float lmaxL = 0.f; -#ifdef _OPENMP - #pragma omp for -#endif - - for (size_t i = 0; i < N; i++) { - if (L[i] < lminL) { - lminL = L[i]; - } - - if (L[i] > lmaxL) { - lmaxL = L[i]; - } - } + float minL = L[0]; + float maxL = minL; #ifdef _OPENMP - #pragma omp critical + #pragma omp parallel for reduction(max:maxL) reduction(min:minL) schedule(dynamic,16) #endif - { - if (lminL < minL) { - minL = lminL; - } - - if (lmaxL > maxL) { - maxL = lmaxL; - } - } + for (i = 0; i < N; i++) { + minL = rtengine::min(minL, L[i]); + maxL = rtengine::max(maxL, L[i]); } if (minL > 0.0f) { @@ -5304,20 +5193,18 @@ void ImProcFunctions::EPDToneMap (LabImage *lab, unsigned int Iterates, int skip if (maxL == 0.f) { // avoid division by zero maxL = 1.f; } - + #ifdef _OPENMP #pragma omp parallel for #endif - - for (size_t i = 0; i < N; ++i) - //{L[i] = (L[i] - minL)/32767.0f; + for (i = 0; i < N; i++) { L[i] = (L[i] - minL) / maxL; L[i] *= gamm; } //Some interpretations. - float Compression = expf (-stren); //This modification turns numbers symmetric around 0 into exponents. + float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. float DetailBoost = stren; if (stren < 0.0f) { @@ -5326,7 +5213,7 @@ void ImProcFunctions::EPDToneMap (LabImage *lab, unsigned int Iterates, int skip //Auto select number of iterates. Note that p->EdgeStopping = 0 makes a Gaussian blur. if (Iterates == 0) { - Iterates = (unsigned int) (edgest * 15.0f); + Iterates = (unsigned int)(edgest * 15.0f); } /* Debuggery. Saves L for toying with outside of RT. @@ -5336,24 +5223,104 @@ void ImProcFunctions::EPDToneMap (LabImage *lab, unsigned int Iterates, int skip fwrite(L, N, sizeof(float), f); fclose(f);*/ - epd.CompressDynamicRange (L, sca / float (skip), edgest, Compression, DetailBoost, Iterates, rew); + epd.CompressDynamicRange(L, sca / float (skip), edgest, Compression, DetailBoost, Iterates, rew); //Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping. - float s = (1.0f + 38.7889f) * powf (Compression, 1.5856f) / (1.0f + 38.7889f * powf (Compression, 1.5856f)); + float s = (1.0f + 38.7889f) * powf(Compression, 1.5856f) / (1.0f + 38.7889f * powf(Compression, 1.5856f)); + float sat = s + 0.3f * s * satur; + //printf("s=%f sat=%f \n", s, sat); + if(sat == 1.f) sat = 1.001f; #ifdef _OPENMP #pragma omp parallel for // removed schedule(dynamic,10) #endif - for (size_t ii = 0; ii < N; ++ii) { - a[ii] *= s; - b[ii] *= s; - L[ii] = L[ii] * maxL * (1.f / gamm) + minL; + for (unsigned int i = 0; i < N; i++) { + int x = i / WW; + int y = i - x * WW; + + tmp1->L[x][y] = L[i] * maxL * (1.f / gamm) + minL; + tmp1->a[x][y] = sat * a[i]; + tmp1->b[x][y] = sat * b[i]; + } } -void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double clip, - double& expcomp, int& bright, int& contr, int& black, int& hlcompr, int& hlcomprthresh) + + + +//Map tones by way of edge preserving decomposition. +void ImProcFunctions::EPDToneMap(LabImage *lab, unsigned int Iterates, int skip) +{ + + if (!params->epd.enabled) { + return; + } + + const float stren = params->epd.strength; + const float edgest = std::min(params->epd.edgeStopping, params->localContrast.enabled ? 3.0 : 4.0); + const float sca = params->epd.scale; + const float gamm = params->epd.gamma; + const float rew = params->epd.reweightingIterates; + //Pointers to whole data and size of it. + float *L = lab->L[0]; + float *a = lab->a[0]; + float *b = lab->b[0]; + const size_t N = lab->W * lab->H; + + EdgePreservingDecomposition epd(lab->W, lab->H); + + //Due to the taking of logarithms, L must be nonnegative. Further, scale to 0 to 1 using nominal range of L, 0 to 15 bit. + float minL = L[0]; + float maxL = L[0]; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minL) reduction(max:maxL) +#endif + for (size_t i = 1; i < N; i++) { + minL = std::min(minL, L[i]); + maxL = std::max(maxL, L[i]); + } + + if (maxL == 0.f) { // black input => do nothing + return; + } + + minL = std::min(minL, 0.f); //Disable the shift if there are no negative numbers. I wish there were just no negative numbers to begin with. + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (size_t i = 0; i < N; ++i) { + L[i] = (L[i] - minL) * (gamm / maxL); + } + + //Some interpretations. + const float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. + const float DetailBoost = std::max(stren, 0.f); //Go with effect of exponent only if uncompressing. + + //Auto select number of iterates. Note that p->EdgeStopping = 0 makes a Gaussian blur. + if (Iterates == 0) { + Iterates = edgest * 15.f; + } + + epd.CompressDynamicRange (L, sca / skip, edgest, Compression, DetailBoost, Iterates, rew); + + //Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping. + const float s = (1.f + 38.7889f) * std::pow(Compression, 1.5856f) / (1.f + 38.7889f * std::pow(Compression, 1.5856f)); + + maxL /= gamm; +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (size_t ii = 0; ii < N; ++ii) { + a[ii] *= s; + b[ii] *= s; + L[ii] = L[ii] * maxL + minL; + } +} + + +void ImProcFunctions::getAutoExp(const LUTu &histogram, int histcompr, double clip, double& expcomp, int& bright, int& contr, int& black, int& hlcompr, int& hlcomprthresh) { float scale = 65536.0f; @@ -5365,7 +5332,7 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double float ave = 0.f; //find average luminance - histogram.getSumAndAverage (sum, ave); + histogram.getSumAndAverage(sum, ave); //find median of luminance size_t median = 0, count = histogram[0]; @@ -5432,12 +5399,12 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double // lodev = (lodev / (log(2.f) * losum)); // hidev = (hidev / (log(2.f) * hisum)); - if (octile[6] > log1p ((float)imax) / log2 (2.f)) { //if very overxposed image + if (octile[6] > log1p((float)imax) / log2(2.f)) { //if very overxposed image octile[6] = 1.5f * octile[5] - 0.5f * octile[4]; overex = 2; } - if (octile[7] > log1p ((float)imax) / log2 (2.f)) { //if overexposed + if (octile[7] > log1p((float)imax) / log2(2.f)) { //if overexposed octile[7] = 1.5f * octile[6] - 0.5f * octile[5]; overex = 1; } @@ -5459,7 +5426,7 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double // compute weighted average separation of octiles // for future use in contrast setting for (int i = 1; i < 6; i++) { - ospread += (octile[i + 1] - octile[i]) / max (0.5f, (i > 2 ? (octile[i + 1] - octile[3]) : (octile[3] - octile[i]))); + ospread += (octile[i + 1] - octile[i]) / max(0.5f, (i > 2 ? (octile[i + 1] - octile[3]) : (octile[3] - octile[i]))); } ospread /= 5.f; @@ -5519,24 +5486,24 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double //sets the mean or median at middle gray, and the amount that sets the estimated top //of the histogram at or near clipping. //float expcomp1 = (log(/*(median/ave)*//*(hidev/lodev)*/midgray*scale/(ave-shc+midgray*shc))+log((hidev/lodev)))/log(2.f); - float expcomp1 = (log (/*(median/ave)*//*(hidev/lodev)*/midgray * scale / (ave - shc + midgray * shc))) / log (2.f); + float expcomp1 = (log(/*(median/ave)*//*(hidev/lodev)*/midgray * scale / (ave - shc + midgray * shc))) / log(2.f); float expcomp2; if (overex == 0) { // image is not overexposed - expcomp2 = 0.5f * ( (15.5f - histcompr - (2.f * oct7 - oct6)) + log (scale / rawmax) / log (2.f) ); + expcomp2 = 0.5f * ((15.5f - histcompr - (2.f * oct7 - oct6)) + log(scale / rawmax) / log(2.f)); } else { - expcomp2 = 0.5f * ( (15.5f - histcompr - (2.f * octile[7] - octile[6])) + log (scale / rawmax) / log (2.f) ); + expcomp2 = 0.5f * ((15.5f - histcompr - (2.f * octile[7] - octile[6])) + log(scale / rawmax) / log(2.f)); } - if (fabs (expcomp1) - fabs (expcomp2) > 1.f) { //for great expcomp - expcomp = (expcomp1 * fabs (expcomp2) + expcomp2 * fabs (expcomp1)) / (fabs (expcomp1) + fabs (expcomp2)); + if (fabs(expcomp1) - fabs(expcomp2) > 1.f) { //for great expcomp + expcomp = (expcomp1 * fabs(expcomp2) + expcomp2 * fabs(expcomp1)) / (fabs(expcomp1) + fabs(expcomp2)); } else { expcomp = 0.5 * (double)expcomp1 + 0.5 * (double) expcomp2; //for small expcomp } - float gain = exp ((float)expcomp * log (2.f)); + float gain = exp((float)expcomp * log(2.f)); - float corr = sqrt (gain * scale / rawmax); + float corr = sqrt(gain * scale / rawmax); black = (int) shc * corr; @@ -5546,11 +5513,11 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double //which is a transcendental equation double comp = (gain * whiteclip / scale - 1.f) * 2.3f; // 2.3 instead of 2 to increase slightly comp hlcompr = 100.0 * comp / (max(0.0, expcomp) + 1.0); - hlcompr = max (0, min (100, hlcompr)); + hlcompr = max(0, min(100, hlcompr)); //now find brightness if gain didn't bring ave to midgray using //the envelope of the actual 'control cage' brightness curve for simplicity - float midtmp = gain * sqrt (median * ave) / scale; + float midtmp = gain * sqrt(median * ave) / scale; if (midtmp < 0.1f) { bright = (midgray - midtmp) * 15.f / (midtmp); @@ -5558,11 +5525,11 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double bright = (midgray - midtmp) * 15.f / (0.10833f - 0.0833f * midtmp); } - bright = 0.25 */*(median/ave)*(hidev/lodev)*/max (0, bright); + bright = 0.25 */*(median/ave)*(hidev/lodev)*/max(0, bright); //compute contrast that spreads the average spacing of octiles contr = (int) 50.0f * (1.1f - ospread); - contr = max (0, min (100, contr)); + contr = max(0, min(100, contr)); //take gamma into account double whiteclipg = (int) (CurveFactory::gamma2(whiteclip * static_cast(corr) / 65536.0) * 65536.0); @@ -5589,7 +5556,7 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double whiteclipg = CurveFactory::igamma2(whiteclipg / 65535.0) * 65535.0; //need to inverse gamma transform to get correct exposure compensation parameter //correction with gamma - black = (int) ((65535 * black) / whiteclipg); + black = (int)((65535 * black) / whiteclipg); //expcomp = log(65535.0 / (whiteclipg)) / log(2.0); //diagnostics @@ -5645,14 +5612,14 @@ void ImProcFunctions::getAutoExp (const LUTu &histogram, int histcompr, double expcomp = 12.0; } - bright = max (-100, min (bright, 100)); + bright = max(-100, min(bright, 100)); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_size) +double ImProcFunctions::getAutoDistor(const Glib::ustring &fname, int thumb_size) { if (!fname.empty()) { rtengine::RawMetaDataLocation ri; @@ -5660,13 +5627,13 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si int w_thumb = -1, h_thumb = thumb_size; eSensorType sensorType = rtengine::ST_NONE; - Thumbnail* thumb = rtengine::Thumbnail::loadQuickFromRaw (fname, ri, sensorType, w_thumb, h_thumb, 1, FALSE); + Thumbnail* thumb = rtengine::Thumbnail::loadQuickFromRaw(fname, ri, sensorType, w_thumb, h_thumb, 1, FALSE); if (!thumb) { return 0.0; } - Thumbnail* raw = rtengine::Thumbnail::loadFromRaw (fname, ri, sensorType, w_raw, h_raw, 1, 1.0, FALSE); + Thumbnail* raw = rtengine::Thumbnail::loadFromRaw(fname, ri, sensorType, w_raw, h_raw, 1, 1.0, FALSE); if (!raw) { delete thumb; @@ -5689,8 +5656,8 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si unsigned char* thumbGray; unsigned char* rawGray; - thumbGray = thumb->getGrayscaleHistEQ (width); - rawGray = raw->getGrayscaleHistEQ (width); + thumbGray = thumb->getGrayscaleHistEQ(width); + rawGray = raw->getGrayscaleHistEQ(width); if (!thumbGray || !rawGray) { if (thumbGray) { @@ -5707,10 +5674,10 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si } double dist_amount; - int dist_result = calcDistortion (thumbGray, rawGray, width, h_thumb, 1, dist_amount); + int dist_result = calcDistortion(thumbGray, rawGray, width, h_thumb, 1, dist_amount); if (dist_result == -1) { // not enough features found, try increasing max. number of features by factor 4 - calcDistortion (thumbGray, rawGray, width, h_thumb, 4, dist_amount); + calcDistortion(thumbGray, rawGray, width, h_thumb, 4, dist_amount); } delete thumbGray; @@ -5723,13 +5690,13 @@ double ImProcFunctions::getAutoDistor (const Glib::ustring &fname, int thumb_si } } -void ImProcFunctions::rgb2lab (const Imagefloat &src, LabImage &dst, const Glib::ustring &workingSpace) +void ImProcFunctions::rgb2lab(const Imagefloat &src, LabImage &dst, const Glib::ustring &workingSpace) { - TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix ( workingSpace ); + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(workingSpace); const float wp[3][3] = { - {static_cast (wprof[0][0]), static_cast (wprof[0][1]), static_cast (wprof[0][2])}, - {static_cast (wprof[1][0]), static_cast (wprof[1][1]), static_cast (wprof[1][2])}, - {static_cast (wprof[2][0]), static_cast (wprof[2][1]), static_cast (wprof[2][2])} + {static_cast(wprof[0][0]), static_cast(wprof[0][1]), static_cast(wprof[0][2])}, + {static_cast(wprof[1][0]), static_cast(wprof[1][1]), static_cast(wprof[1][2])}, + {static_cast(wprof[2][0]), static_cast(wprof[2][1]), static_cast(wprof[2][2])} }; const int W = src.getWidth(); @@ -5742,20 +5709,20 @@ void ImProcFunctions::rgb2lab (const Imagefloat &src, LabImage &dst, const Glib: for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) { float X, Y, Z; - Color::rgbxyz (src.r (i, j), src.g (i, j), src.b (i, j), X, Y, Z, wp); + Color::rgbxyz(src.r(i, j), src.g(i, j), src.b(i, j), X, Y, Z, wp); //convert Lab - Color::XYZ2Lab (X, Y, Z, dst.L[i][j], dst.a[i][j], dst.b[i][j]); + Color::XYZ2Lab(X, Y, Z, dst.L[i][j], dst.a[i][j], dst.b[i][j]); } } } -void ImProcFunctions::lab2rgb (const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace) +void ImProcFunctions::lab2rgb(const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace) { - TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix ( workingSpace ); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(workingSpace); const float wip[3][3] = { - {static_cast (wiprof[0][0]), static_cast (wiprof[0][1]), static_cast (wiprof[0][2])}, - {static_cast (wiprof[1][0]), static_cast (wiprof[1][1]), static_cast (wiprof[1][2])}, - {static_cast (wiprof[2][0]), static_cast (wiprof[2][1]), static_cast (wiprof[2][2])} + {static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])}, + {static_cast(wiprof[1][0]), static_cast(wiprof[1][1]), static_cast(wiprof[1][2])}, + {static_cast(wiprof[2][0]), static_cast(wiprof[2][1]), static_cast(wiprof[2][2])} }; const int W = dst.getWidth(); @@ -5765,7 +5732,7 @@ void ImProcFunctions::lab2rgb (const LabImage &src, Imagefloat &dst, const Glib: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - wipv[i][j] = F2V (wiprof[i][j]); + wipv[i][j] = F2V(wiprof[i][j]); } } @@ -5782,19 +5749,19 @@ void ImProcFunctions::lab2rgb (const LabImage &src, Imagefloat &dst, const Glib: for (; j < W - 3; j += 4) { vfloat X, Y, Z; vfloat R, G, B; - Color::Lab2XYZ (LVFU (src.L[i][j]), LVFU (src.a[i][j]), LVFU (src.b[i][j]), X, Y, Z); - Color::xyz2rgb (X, Y, Z, R, G, B, wipv); - STVFU (dst.r (i, j), R); - STVFU (dst.g (i, j), G); - STVFU (dst.b (i, j), B); + Color::Lab2XYZ(LVFU(src.L[i][j]), LVFU(src.a[i][j]), LVFU(src.b[i][j]), X, Y, Z); + Color::xyz2rgb(X, Y, Z, R, G, B, wipv); + STVFU(dst.r(i, j), R); + STVFU(dst.g(i, j), G); + STVFU(dst.b(i, j), B); } #endif for (; j < W; j++) { float X, Y, Z; - Color::Lab2XYZ (src.L[i][j], src.a[i][j], src.b[i][j], X, Y, Z); - Color::xyz2rgb (X, Y, Z, dst.r (i, j), dst.g (i, j), dst.b (i, j), wip); + Color::Lab2XYZ(src.L[i][j], src.a[i][j], src.b[i][j], X, Y, Z); + Color::xyz2rgb(X, Y, Z, dst.r(i, j), dst.g(i, j), dst.b(i, j), wip); } } } @@ -5828,9 +5795,10 @@ void ImProcFunctions::colorToningLabGrid(LabImage *lab, int xstart, int xend, in float b_scale = (params->colorToning.labgridBHigh - params->colorToning.labgridBLow) / factor / scaling; float b_base = params->colorToning.labgridBLow / scaling; -#ifdef _OPENMP + #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif + for (int y = ystart; y < yend; ++y) { for (int x = xstart; x < xend; ++x) { lab->a[y][x] += lab->L[y][x] * a_scale + a_base; diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index b7d7c41e5..355b5d435 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -23,6 +23,11 @@ #include "coord2d.h" #include "gamutwarning.h" +#include "jaggedarray.h" +#include "pipettebuffer.h" +#include "array2D.h" +#include "imagesource.h" +#include namespace Glib { @@ -48,6 +53,14 @@ class DCPProfileApplyState; class FlatCurve; class FramesMetaData; class LensCorrection; +class LocCCmaskCurve; +class LocLLmaskCurve; +class LocHHmaskCurve; +class LocwavCurve; +class LocretigainCurve; +class LocretitransCurve; +class LocLHCurve; +class LocHHCurve; class NoiseCurve; class OpacityCurve; class PipetteBuffer; @@ -71,9 +84,15 @@ namespace procparams class ProcParams; +struct DehazeParams; +struct FattalToneMappingParams; struct ColorManagementParams; struct DirPyrDenoiseParams; +struct LocalContrastParams; +struct LocallabParams; struct SharpeningParams; +struct SoftLightParams; +struct VibranceParams; struct VignettingParams; struct WaveletParams; @@ -85,11 +104,18 @@ class ImProcFunctions { cmsHTRANSFORM monitorTransform; std::unique_ptr gamutWarning; + Cairo::RefPtr locImage; const procparams::ProcParams* params; double scale; bool multiThread; + bool lastcutpast; + int lastcxbuf; + int lastcybuf; + int lastcount; + LabImage *spotbuffer; + void calcVignettingParams(int oW, int oH, const procparams::VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul); void transformLuminanceOnly(Imagefloat* original, Imagefloat* transformed, int cx, int cy, int oW, int oH, int fW, int fH); @@ -106,7 +132,6 @@ class ImProcFunctions bool needsLensfun() const; // static cmsUInt8Number* Mempro = NULL; - public: enum class Median { TYPE_3X3_SOFT, @@ -130,7 +155,7 @@ public: bool needsTransform(int oW, int oH, int rawRotationDeg, const FramesMetaData *metadata) const; bool needsPCVignetting() const; - + float calcGradientFactor (const struct grad_params& gp, int x, int y); void firstAnalysis(const Imagefloat* const working, const procparams::ProcParams ¶ms, LUTu & vhist16); void updateColorProfiles(const Glib::ustring& monitorProfile, RenderingIntent monitorIntent, bool softProof, bool gamutCheck); void rgbProc(Imagefloat* working, LabImage* lab, PipetteBuffer *pipetteBuffer, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve, @@ -156,12 +181,16 @@ public: void moyeqt(Imagefloat* working, float &moyS, float &eqty); void luminanceCurve(LabImage* lold, LabImage* lnew, const LUTf &curve); + void ciecamloc_02float(int sp, LabImage* lab); + void ciecam_02float(CieImage* ncie, float adap, int pW, int pwb, LabImage* lab, const procparams::ProcParams* params, const ColorAppearance & customColCurve1, const ColorAppearance & customColCurve, const ColorAppearance & customColCurve3, LUTu &histLCAM, LUTu &histCCAM, LUTf & CAMBrightCurveJ, LUTf & CAMBrightCurveQ, float &mean, int Iterates, int scale, bool execsharp, float &d, float &dj, float &yb, int rtt, bool showSharpMask = false); void chromiLuminanceCurve(PipetteBuffer *pipetteBuffer, int pW, LabImage* lold, LabImage* lnew, const LUTf& acurve, const LUTf& bcurve, const LUTf& satcurve, const LUTf& satclcurve, const LUTf& clcurve, LUTf &curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histLurve); - void vibrance(LabImage* lab); //Jacques' vibrance + void vibrance(LabImage* lab, const procparams::VibranceParams &vibranceParams, bool highlight, const Glib::ustring &workingProfile); //Jacques' vibrance + void softprocess(const LabImage* bufcolorig, array2D &buflight, /* float ** bufchro, float ** buf_a, float ** buf_b, */ float rad, int bfh, int bfw, double epsilmax, double epsilmin, float thres, int sk, bool multiThread); + void softproc(const LabImage* bufcolorig, const LabImage* bufcolfin, float rad, int bfh, int bfw, float epsilmax, float epsilmin, float thres, int sk, bool multiThread, int flag); // void colorCurve (LabImage* lold, LabImage* lnew); void sharpening(LabImage* lab, const procparams::SharpeningParams &sharpenParam, bool showMask = false); void sharpeningcam(CieImage* ncie, float** buffer, bool showMask = false); @@ -173,6 +202,8 @@ public: void Lanczos(const Imagefloat* src, Imagefloat* dst, float scale); void deconvsharpening(float** luminance, float** buffer, const float* const * blend, int W, int H, const procparams::SharpeningParams &sharpenParam, double Scale); + void deconvsharpeningloc(float** luminance, float** buffer, int W, int H, float** loctemp, int damp, double radi, int ite, int amo, int contrast, double blurrad, int sk); + void MLsharpen(LabImage* lab); // Manuel's clarity / sharpening void MLmicrocontrast(float** luminance, int W, int H); //Manuel's microcontrast void MLmicrocontrast(LabImage* lab); //Manuel's microcontrast @@ -187,46 +218,179 @@ public: void dirpyrequalizer(LabImage* lab, int scale); //Emil's wavelet - void EPDToneMapResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, struct cont_params& cp, int W_L, int H_L, float max0, float min0); + void EPDToneMapResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, const struct cont_params& cp, int W_L, int H_L, float max0); void CompressDR(float *Source, int W_L, int H_L, float Compression, float DetailBoost); - void ContrastResid(float * WavCoeffs_L0, struct cont_params &cp, int W_L, int H_L, float max0, float min0); + void Compresslevels(float **Source, int W_L, int H_L, float compression, float detailattenuator, float thres, float mean, float maxp, float meanN, float maxN, float madL); + void ContrastResid(float * WavCoeffs_L0, const struct cont_params &cp, int W_L, int H_L, float max0); void EPDToneMap(LabImage *lab, unsigned int Iterates = 0, int skip = 1); + void EPDToneMaplocal(int sp, LabImage *lab, LabImage *tmp1, unsigned int Iterates, int skip); void EPDToneMapCIE(CieImage *ncie, float a_w, float c_, int Wid, int Hei, float minQ, float maxQ, unsigned int Iterates = 0, int skip = 1); // pyramid denoise // procparams::DirPyrDenoiseParams dnparams; void dirpyr(LabImage* data_fine, LabImage* data_coarse, int level, LUTf &rangefn_L, LUTf &rangefn_ab, - int pitch, int scale, const int luma, int chroma); + int pitch, int scale, const int luma, int chroma); void idirpyr(LabImage* data_coarse, LabImage* data_fine, int level, LUTf &rangefn_L, LUTf & nrwt_l, LUTf & nrwt_ab, int pitch, int scale, const int luma, const int chroma/*, LUTf & Lcurve, LUTf & abcurve*/); + //locallab Local adjustments + void maskcalccol(bool invmask, bool pde, int bfw, int bfh, int xstart, int ystart, int sk, int cx, int cy, LabImage* bufcolorig, LabImage* bufmaskblurcol, LabImage* originalmaskcol, LabImage* original, LabImage* reserved, int inv, struct local_params & lp, + float strumask, bool astool, + const LocCCmaskCurve & locccmasCurve, bool lcmasutili, + const LocLLmaskCurve & locllmasCurve, bool llmasutili, + const LocHHmaskCurve & lochhmasCurve, bool lhmasutili, const LocHHmaskCurve & lochhhmasCurve, bool lhhmasutili, + bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, int shado, float amountcd, float anchorcd, + const LUTf& lmasklocalcurve, bool localmaskutili, + const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, + int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope); + + void deltaEforMask(float **rdE, int bfw, int bfh, LabImage* bufcolorig, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh); + void discrete_laplacian_threshold(float * data_out, const float * data_in, size_t nx, size_t ny, float t); + void rex_poisson_dct(float * data, size_t nx, size_t ny, double m); + void mean_dt(const float * data, size_t size, double& mean_p, double& dt_p); + float *cos_table(size_t size); + void normalize_mean_dt(float *data, const float *ref, size_t size, float mod, float sigm); + void retinex_pde(const float *datain, float * dataout, int bfw, int bfh, float thresh, float multy, float *dE, int show, int dEenable, int normalize); + void exposure_pde(float *dataor, float *datain, float * dataout, int bfw, int bfh, float thresh, float mod); + void fftw_convol_blur(float *input, float *output, int bfw, int bfh, float radius, int fftkern, int algo); + void fftw_convol_blur2(float **input2, float **output2, int bfw, int bfh, float radius, int fftkern, int algo); + void fftw_tile_blur(int GW, int GH, int tilssize , int max_numblox_W, int min_numblox_W, float **tmp1, int numThreads, double radius); + + void maskforretinex(int sp, int before, float ** luminance, float ** out, int W_L, int H_L, int skip, + const LocCCmaskCurve & locccmasretiCurve, bool &lcmasretiutili, const LocLLmaskCurve & locllmasretiCurve, bool &llmasretiutili, const LocHHmaskCurve & lochhmasretiCurve, bool & lhmasretiutili, + int llretiMask, bool retiMasktmap, bool retiMask, float rad, float lap, bool pde, float gamm, float slop, float chro, float blend, + LUTf & lmaskretilocalcurve, bool & localmaskretiutili, + LabImage * bufreti, LabImage * bufmask, LabImage * buforig, LabImage * buforigmas, bool multiThread, + bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh, float lumask); + + //3 functions from Alberto Griggio, adapted J.Desmis 2019 + void filmGrain(Imagefloat *rgb, int isogr, int strengr, int scalegr, int bfw, int bfh); + void log_encode(Imagefloat *rgb, const struct local_params & lp, bool multiThread, int bfw, int bfh); + void getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg, float *blackev, float *whiteev, bool *Autogr, int fw, int fh, float xsta, float xend, float ysta, float yend, int SCALE); + + void MSRLocal(int call, int sp, bool fftw, int lum, float** reducDE, LabImage * bufreti, LabImage * bufmask, LabImage * buforig, LabImage * buforigmas, float** luminance, const float* const *originalLuminance, + const int width, const int height, int bfwr, int bfhr, const procparams::LocallabParams &loc, const int skip, const LocretigainCurve &locRETgainCcurve, const LocretitransCurve &locRETtransCcurve, + const int chrome, const int scall, const float krad, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, + const LocCCmaskCurve & locccmasretiCurve, bool &lcmasretiutili, const LocLLmaskCurve & locllmasretiCurve, bool &llmasretiutili, const LocHHmaskCurve & lochhmasretiCurve, bool & lhmasretiutili, int llretiMask, + LUTf & lmaskretilocalcurve, bool & localmaskretiutili, + LabImage * transformed, bool retiMasktmap, bool retiMask, + bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh, float lumask); + + + void calc_ref(int sp, LabImage* original, LabImage* transformed, int cx, int cy, int oW, int oH, int sk, double &huerefblur, double &chromarefblur, double &lumarefblur, double &hueref, double &chromaref, double &lumaref, double &sobelref, float &avg, const LocwavCurve & locwavCurveden, bool locwavdenutili); + void copy_ref(LabImage* spotbuffer, LabImage* original, LabImage* transformed, int cx, int cy, int sk, const struct local_params & lp, double &huerefspot, double &chromarefspot, double &lumarefspot); + void paste_ref(LabImage* spotbuffer, LabImage* transformed, int cx, int cy, int sk, const struct local_params & lp); + void Lab_Local(int call, int sp, float** shbuffer, LabImage* original, LabImage* transformed, LabImage* reserved, LabImage* lastorig, int cx, int cy, int oW, int oH, int sk, const LocretigainCurve& locRETgainCcurve, const LocretitransCurve &locRETtransCcurve, + const LUTf& lllocalcurve, bool locallutili, + const LUTf& cllocalcurve, bool localclutili, + const LUTf& lclocalcurve, bool locallcutili, + const LocLHCurve& loclhCurve, const LocHHCurve& lochhCurve, + const LUTf& lmasklocalcurve, bool localmaskutili, + const LUTf& lmaskexplocalcurve, bool localmaskexputili, + const LUTf& lmaskSHlocalcurve, bool localmaskSHutili, + const LUTf& lmaskviblocalcurve, bool localmaskvibutili, + const LUTf& lmasktmlocalcurve, bool localmasktmutili, + LUTf& lmaskretilocalcurve, bool localmaskretiutili, + const LUTf& lmaskcblocalcurve, bool localmaskcbutili, + const LUTf& lmaskbllocalcurve, bool localmaskblutili, + const LUTf& lmasklclocalcurve, bool localmasklcutili, + const LocCCmaskCurve& locccmasCurve, bool lcmasutili, const LocLLmaskCurve& locllmasCurve, bool llmasutili, const LocHHmaskCurve& lochhmasCurve, bool lhmasutili, const LocHHmaskCurve& lochhhmasCurve, bool lhhmasutili, + const LocCCmaskCurve& locccmasexpCurve, bool lcmasexputili, const LocLLmaskCurve& locllmasexpCurve, bool llmasexputili, const LocHHmaskCurve& lochhmasexpCurve, bool lhmasexputili, + const LocCCmaskCurve& locccmasSHCurve, bool lcmasSHutili, const LocLLmaskCurve& locllmasSHCurve, bool llmasSHutili, const LocHHmaskCurve& lochhmasSHCurve, bool lhmasSHutili, + const LocCCmaskCurve& locccmasvibCurve, bool lcmasvibutili, const LocLLmaskCurve& locllmasvibCurve, bool llmasvibutili, const LocHHmaskCurve& lochhmasvibCurve, bool lhmasvibutili, + const LocCCmaskCurve& locccmascbCurve, bool lcmascbutili, const LocLLmaskCurve& locllmascbCurve, bool llmascbutili, const LocHHmaskCurve& lochhmascbCurve, bool lhmascbutili, + const LocCCmaskCurve& locccmasretiCurve, bool lcmasretiutili, const LocLLmaskCurve& locllmasretiCurve, bool llmasretiutili, const LocHHmaskCurve& lochhmasretiCurve, bool lhmasretiutili, + const LocCCmaskCurve& locccmastmCurve, bool lcmastmutili, const LocLLmaskCurve& locllmastmCurve, bool llmastmutili, const LocHHmaskCurve& lochhmastmCurve, bool lhmastmutili, + const LocCCmaskCurve& locccmasblCurve, bool lcmasblutili, const LocLLmaskCurve& locllmasblCurve, bool llmasblutili, const LocHHmaskCurve& lochhmasblCurve, bool lhmasblutili, + const LocCCmaskCurve& locccmaslcCurve, bool lcmaslcutili, const LocLLmaskCurve& locllmaslcCurve, bool llmaslcutili, const LocHHmaskCurve& lochhmaslcCurve, bool lhmaslcutili, + const LocwavCurve& loclmasCurveblwav, bool lmasutiliblwav, + const LocwavCurve& loclmasCurvecolwav, bool lmasutilicolwav, + const LocwavCurve& locwavCurve, bool locwavutili, + const LocwavCurve& loclevwavCurve, bool loclevwavutili, + const LocwavCurve& locconwavCurve, bool locconwavutili, + const LocwavCurve& loccompwavCurve, bool loccompwavutili, + const LocwavCurve& loccomprewavCurve, bool loccomprewavutili, + const LocwavCurve& locwavCurveden, bool locwavdenutili, + const LocwavCurve& locedgwavCurve, bool locedgwavutili, + bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, + double& huerefblur, double &chromarefblur, double& lumarefblur, double &hueref, double &chromaref, double &lumaref, double &sobelref, int &lastsav, + bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, + float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax); + + void addGaNoise(LabImage *lab, LabImage *dst, const float mean, const float variance, const int sk); + void BlurNoise_Localold(int call, const struct local_params& lp, LabImage* original, LabImage* transformed, const LabImage* const tmp1, int cx, int cy); + void InverseBlurNoise_Local(LabImage * originalmask, float **bufchro, const struct local_params& lp, const float hueref, const float chromaref, const float lumaref, LabImage* original, LabImage* transformed, const LabImage* const tmp1, int cx, int cy, int sk); + void InverseReti_Local(const struct local_params& lp, const float hueref, const float chromaref, const float lumaref, LabImage* original, LabImage* transformed, const LabImage* const tmp1, int cx, int cy, int chro, int sk); + void BlurNoise_Local(LabImage* tmp1, LabImage * originalmask, float **bufchro, const float hueref, const float chromaref, const float lumaref, local_params& lp, LabImage* original, LabImage* transformed, int cx, int cy, int sk); + static void strcurv_data(std::string retistr, int *s_datc, int &siz); + void blendstruc(int bfw, int bfh, LabImage* bufcolorig, float radius, float stru, array2D & blend2, int sk, bool multiThread); + + void wavcontrast4(struct local_params& lp, float ** tmp, float ** tmpa, float ** tmpb, float contrast, float radblur, float radlevblur, int bfw, int bfh, int level_bl, int level_hl, int level_br, int level_hr, int sk, int numThreads, const LocwavCurve & locwavCurve, bool locwavutili, bool wavcurve, + const LocwavCurve & loclevwavCurve, bool loclevwavutili, bool wavcurvelev, + const LocwavCurve & locconwavCurve, bool locconwavutili, bool wavcurvecon, + const LocwavCurve & loccompwavCurve, bool loccompwavutili, bool wavcurvecomp, + const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, bool wavcurvecompre, + const LocwavCurve & locedgwavCurve, bool locedgwavutili, + float sigm, float offs,int & maxlvl, float fatdet, float fatanch, float chromalev, float chromablu, bool blurlc, bool blurena, bool levelena, bool comprena, bool compreena, float compress, float thres); + + void wavcont(const struct local_params& lp, float ** tmp, wavelet_decomposition &wdspot, int level_bl, int maxlvl, + const LocwavCurve & loclevwavCurve, bool loclevwavutili, + const LocwavCurve & loccompwavCurve, bool loccompwavutili, + const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, + float radlevblur, int process, float chromablu, float thres, float sigmadc, float deltad); + + void wavcbd(wavelet_decomposition &wdspot, int level_bl, int maxlvl, + const LocwavCurve& locconwavCurve, bool locconwavutili, float sigm, float offs, float chromalev, int sk); + + void transit_shapedetect2(int call, int senstype, const LabImage * bufexporig, const LabImage * bufexpfin, LabImage * originalmask, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk); + + void transit_shapedetect_retinex(int call, int senstype, LabImage * bufexporig, LabImage * bufmask, LabImage * buforigmas, float **buflight, float **bufchro, const float hueref, const float chromaref, const float lumaref, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk); + void transit_shapedetect(int senstype, const LabImage *bufexporig, LabImage * originalmask, float **bufchro, bool HHutili, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk); + void exlabLocal(local_params& lp, int bfh, int bfw, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve); + void Exclude_Local(float **deltaso, float hueref, float chromaref, float lumaref, float sobelref, float meansobel, const struct local_params & lp, const LabImage * original, LabImage * transformed, const LabImage * rsv, const LabImage * reserv, int cx, int cy, int sk); + + void DeNoise_Local(int call, const struct local_params& lp, LabImage* originalmask, int levred, float hueref, float lumaref, float chromaref, LabImage* original, LabImage* transformed, const LabImage &tmp1, int cx, int cy, int sk); + void DeNoise(int call, int del, float * slidL, float * slida, float * slidb, int aut, bool noiscfactiv, const struct local_params& lp, LabImage* originalmaskbl, int levred, float huerefblur, float lumarefblur, float chromarefblur, LabImage* original, LabImage* transformed, int cx, int cy, int sk); + + + void fftw_denoise(int GW, int GH, int max_numblox_W, int min_numblox_W, float **tmp1, array2D *Lin, int numThreads, const struct local_params & lp, int chrom); + + void ColorLight_Local(float moddE, float powdE, int call, LabImage * bufcolorig, LabImage * originalmask, float **buflight, float **bufchro, float **bufchroslid, float ** bufhh, float ** buflightslid, bool &LHutili, bool &HHutili, const float hueplus, const float huemoins, const float hueref, const float dhue, const float chromaref, const float lumaref, float sobelref, float ** blend2, LUTf & lllocalcurve, const LocLHCurve & loclhCurve, const LocHHCurve & lochhCurve, LUTf & lightCurveloc, const local_params& lp, LabImage* original, LabImage* transformed, int cx, int cy, int sk); + void InverseColorLight_Local(bool tonequ, bool tonecurv, int sp, int senstype, struct local_params& lp, LabImage * originalmask, const LUTf& lightCurveloc, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& exlocalcurve, const LUTf& cclocalcurve, float adjustr, bool localcutili, const LUTf& lllocalcurve, bool locallutili, LabImage* original, LabImage* transformed, int cx, int cy, const float hueref, const float chromaref, const float lumaref, int sk); + void Sharp_Local(int call, float **loctemp, int senstype, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk); + + void InverseSharp_Local(float **loctemp, const float hueref, const float lumaref, const float chromaref, local_params& lp, LabImage* original, LabImage* transformed, int cx, int cy, int sk); + +//Wavelet and denoise void Tile_calc(int tilesize, int overlap, int kall, int imwidth, int imheight, int &numtiles_W, int &numtiles_H, int &tilewidth, int &tileheight, int &tileWskip, int &tileHskip); void ip_wavelet(LabImage * lab, LabImage * dst, int kall, const procparams::WaveletParams & waparams, const WavCurve & wavCLVCcurve, const Wavblcurve & wavblcurve, const WavOpacityCurveRG & waOpacityCurveRG, const WavOpacityCurveSH & waOpacityCurveSH, const WavOpacityCurveBY & waOpacityCurveBY, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveWL & waOpacityCurveWL, const LUTf &wavclCurve, int skip); - void WaveletcontAllL(LabImage * lab, float **varhue, float **varchrom, const wavelet_decomposition &WaveletCoeffs_L, const Wavblcurve & wavblcurve, + void WaveletcontAllL(LabImage * lab, float **varhue, float **varchrom, wavelet_decomposition& WaveletCoeffs_L, const Wavblcurve & wavblcurve, struct cont_params &cp, int skip, float *mean, float *sigma, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveSH & waOpacityCurveSH, FlatCurve* ChCurve, bool Chutili); - void WaveletcontAllLfinal(const wavelet_decomposition &WaveletCoeffs_L, const cont_params &cp, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL); - void WaveletcontAllAB(LabImage * lab, float **varhue, float **varchrom, const wavelet_decomposition &WaveletCoeffs_a, const Wavblcurve & wavblcurve, const WavOpacityCurveW & waOpacityCurveW, + void WaveletcontAllLfinal(wavelet_decomposition& WaveletCoeffs_L, const cont_params &cp, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL); + void WaveletcontAllAB(LabImage * lab, float **varhue, float **varchrom, wavelet_decomposition& WaveletCoeffs_a, const Wavblcurve & wavblcurve, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp, const bool useChannelA, int skip, float *meanab, float *sigmaab); - void WaveletAandBAllAB(const wavelet_decomposition &WaveletCoeffs_a, const wavelet_decomposition &WaveletCoeffs_b, + void WaveletAandBAllAB(wavelet_decomposition& WaveletCoeffs_a, wavelet_decomposition& WaveletCoeffs_b, const cont_params &cp, FlatCurve* hhcurve, bool hhutili); - void ContAllL(float **koeLi, float *maxkoeLi, bool lipschitz, int maxlvl, LabImage * lab, float **varhue, float **varchrom, float ** WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, struct cont_params &cp, + void ContAllL(float** koeLi, float maxkoeLi, bool lipschitz, int maxlvl, LabImage * lab, const float* const* varhue, const float* const* varchrom, float* const* WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, struct cont_params &cp, int W_L, int H_L, int skip, float *mean, float *sigma, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveSH & waOpacityCurveSH, FlatCurve* ChCurve, bool Chutili); - void finalContAllL(float ** WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, const cont_params &cp, + void finalContAllL(float* const* WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, const cont_params &cp, int W_L, int H_L, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL); - void ContAllAB(LabImage * lab, int maxlvl, float **varhue, float **varchrom, float ** WavCoeffs_a, float * WavCoeffs_a0, int level, int dir, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp, + void ContAllAB(LabImage * lab, int maxlvl, float **varhue, float **varchrom, float* const* WavCoeffs_a, float * WavCoeffs_a0, int level, int dir, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp, int W_ab, int H_ab, const bool useChannelA, float *meanab, float *sigmaab); - void Evaluate2(const wavelet_decomposition &WaveletCoeffs_L, - float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN); - void Eval2(float ** WavCoeffs_L, int level, - int W_L, int H_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN); + void Evaluate2(const wavelet_decomposition &WaveletCoeffs_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, int numThreads); + void Eval2(const float* const* WavCoeffs_L, int level, int W_L, int H_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, int numThreads); void calceffect(int level, float *mean, float *sigma, float *mea, float effect, float offs); - void Aver(float * HH_Coeffs, int datalen, float &averagePlus, float &averageNeg, float &max, float &min); - void Sigma(float * HH_Coeffs, int datalen, float averagePlus, float averageNeg, float &sigmaPlus, float &sigmaNeg); - void calckoe(float ** WavCoeffs_LL, const cont_params& cp, float ** koeLi, int level, int dir, int W_L, int H_L, float edd, float *maxkoeLi, float **tmC = nullptr); + void Aver(const float* HH_Coeffs, int datalen, float &averagePlus, float &averageNeg, float &max, float &min, int numThreads); + void Sigma(const float* HH_Coeffs, int datalen, float averagePlus, float averageNeg, float &sigmaPlus, float &sigmaNeg, int numThreads); + void calckoe(const float* const* WavCoeffs_LL, float gradw, float tloww, float ** koeLi, int level, int dir, int W_L, int H_L, float edd, float &maxkoeLi, float **tmC = nullptr); void Median_Denoise(float **src, float **dst, int width, int height, Median medianType, int iterations, int numThreads, float **buffer = nullptr); void Median_Denoise(float **src, float **dst, float upperBound, int width, int height, Median medianType, int iterations, int numThreads, float **buffer = nullptr); @@ -240,18 +404,18 @@ public: const wavelet_decomposition &WaveletCoeffs_b, float **noisevarlum, float **noisevarchrom, float **noisevarhue, float &chaut, int &Nb, float &redaut, float &blueaut, float &maxredaut, float &maxblueaut, float &minredaut, float & minblueaut, int schoice, float &chromina, float &sigma, float &lumema, float &sigma_L, float &redyel, float &skinc, float &nsknc, float &maxchred, float &maxchblue, float &minchred, float &minchblue, int &nb, float &chau, float &chred, float &chblue, bool denoiseMethodRgb); - bool WaveletDenoiseAllL(const wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels); - bool WaveletDenoiseAllAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels); + bool WaveletDenoiseAllL(wavelet_decomposition& WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels); + bool WaveletDenoiseAllAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels); - bool WaveletDenoiseAll_BiShrinkL(const wavelet_decomposition &WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels); - bool WaveletDenoiseAll_BiShrinkAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels); + bool WaveletDenoiseAll_BiShrinkL(wavelet_decomposition& WaveletCoeffs_L, float *noisevarlum, float madL[8][3], float * vari, int edge, int denoiseNestedLevels); + bool WaveletDenoiseAll_BiShrinkAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float *noisevarchrom, float madL[8][3], float *variC, int local, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, int denoiseNestedLevels); - void ShrinkAllL(const wavelet_decomposition &WaveletCoeffs_L, float **buffer, int level, int dir, float *noisevarlum, float * madL, float * vari, int edge); - void ShrinkAllAB(const wavelet_decomposition &WaveletCoeffs_L, const wavelet_decomposition &WaveletCoeffs_ab, float **buffer, int level, int dir, + void ShrinkAllL(wavelet_decomposition& WaveletCoeffs_L, float **buffer, int level, int dir, float *noisevarlum, float * madL, float * vari, int edge); + void ShrinkAllAB(wavelet_decomposition& WaveletCoeffs_L, wavelet_decomposition& WaveletCoeffs_ab, float **buffer, int level, int dir, float *noisevarchrom, float noisevar_ab, const bool useNoiseCCurve, bool autoch, bool denoiseMethodRgb, float * madL, float * variC, int local, float * madaab = nullptr, bool madCalculated = false); - void ShrinkAll_info(float ** WavCoeffs_a, float ** WavCoeffs_b, + void ShrinkAll_info(const float* const* WavCoeffs_a, const float* const* WavCoeffs_b, int W_ab, int H_ab, float **noisevarlum, float **noisevarchrom, float **noisevarhue, float &chaut, int &Nb, float &redaut, float &blueaut, float &maxredaut, float &maxblueaut, float &minredaut, float &minblueaut, int schoice, int lvl, float &chromina, float &sigma, float &lumema, float &sigma_L, float &redyel, float &skinc, float &nsknc, float &maxchred, float &maxchblue, float &minchred, float &minchblue, int &nb, float &chau, float &chred, float &chblue, bool denoiseMethodRgb); void Noise_residualAB(const wavelet_decomposition &WaveletCoeffs_ab, float &chresid, float &chmaxresid, bool denoiseMethodRgb); @@ -260,6 +424,7 @@ public: float MadRgb(const float * DataList, int datalen); // pyramid wavelet + void cbdl_local_temp(float ** src, float ** loctemp, int srcwidth, int srcheight, const float * mult, float kchro, const double dirpyrThreshold, const float mergeL, const float contres, const float blurcb, const double skinprot, const bool gamutlab, float b_l, float t_l, float t_r, float b_r, int choice, int scale, bool multiThread); void dirpyr_equalizer(const float * const * src, float ** dst, int srcwidth, int srcheight, const float * const * l_a, const float * const * l_b, const double * mult, double dirpyrThreshold, double skinprot, float b_l, float t_l, float t_r, int scale); //Emil's directional pyramid wavelet void dirpyr_equalizercam(const CieImage* ncie, float ** src, float ** dst, int srcwidth, int srcheight, const float * const * h_p, const float * const * C_p, const double * mult, const double dirpyrThreshold, const double skinprot, float b_l, float t_l, float t_r, int scale); //Emil's directional pyramid wavelet void defringe(LabImage* lab); @@ -272,13 +437,15 @@ public: void Badpixelscam(CieImage * ncie, double radius, int thresh, int mode, float chrom, bool hotbad); void BadpixelsLab(LabImage * lab, double radius, int thresh, float chrom); - void dehaze(Imagefloat *rgb); - void ToneMapFattal02(Imagefloat *rgb); - void localContrast(LabImage *lab); + void dehaze(Imagefloat *rgb, const procparams::DehazeParams &dehazeParams); + void dehazeloc(Imagefloat *rgb, const procparams::DehazeParams &dehazeParams); + void ToneMapFattal02(Imagefloat *rgb, const procparams::FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo); + void localContrast(LabImage *lab, float **destination, const procparams::LocalContrastParams &localContrastParams, bool fftwlc, double scale); void colorToningLabGrid(LabImage *lab, int xstart, int xend, int ystart, int yend, bool MultiThread); //void shadowsHighlights(LabImage *lab); void shadowsHighlights(LabImage *lab, bool ena, int labmode, int hightli, int shado, int rad, int scal, int hltonal, int shtonal); - void softLight(LabImage *lab); + + void softLight(LabImage *lab, const procparams::SoftLightParams &softLightParams); void labColorCorrectionRegions(LabImage *lab); Image8* lab2rgb(LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool consider_histogram_settings = true); diff --git a/rtengine/ipdehaze.cc b/rtengine/ipdehaze.cc index 53939b5c0..7d715a9b0 100644 --- a/rtengine/ipdehaze.cc +++ b/rtengine/ipdehaze.cc @@ -41,6 +41,8 @@ #include "procparams.h" #include "rescale.h" #include "rt_math.h" +//#define BENCHMARK +#include "StopWatch.h" #include "../rtgui/options.h" @@ -58,15 +60,18 @@ float normalize(Imagefloat *rgb, bool multithread) #ifdef _OPENMP #pragma omp parallel for reduction(max:maxval) schedule(dynamic, 16) if (multithread) #endif + for (int y = 0; y < H; ++y) { for (int x = 0; x < W; ++x) { maxval = max(maxval, rgb->r(y, x), rgb->g(y, x), rgb->b(y, x)); } } + maxval = max(maxval * 2.f, 65535.f); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 16) if (multithread) #endif + for (int y = 0; y < H; ++y) { for (int x = 0; x < W; ++x) { rgb->r(y, x) /= maxval; @@ -74,6 +79,7 @@ float normalize(Imagefloat *rgb, bool multithread) rgb->b(y, x) /= maxval; } } + return maxval; } @@ -81,10 +87,12 @@ void restore(Imagefloat *rgb, float maxval, bool multithread) { const int W = rgb->getWidth(); const int H = rgb->getHeight(); + if (maxval > 0.f && maxval != 1.f) { #ifdef _OPENMP -# pragma omp parallel for if (multithread) + # pragma omp parallel for if (multithread) #endif + for (int y = 0; y < H; ++y) { for (int x = 0; x < W; ++x) { rgb->r(y, x) *= maxval; @@ -103,8 +111,10 @@ int get_dark_channel(const array2D &R, const array2D &G, const arr #ifdef _OPENMP #pragma omp parallel for if (multithread) #endif + for (int y = 0; y < H; y += patchsize) { const int pH = min(y + patchsize, H); + for (int x = 0; x < W; x += patchsize) { float minR = RT_INFINITY_F; float minG = RT_INFINITY_F; @@ -115,21 +125,26 @@ int get_dark_channel(const array2D &R, const array2D &G, const arr vfloat minBv = F2V(minB); #endif const int pW = min(x + patchsize, W); + for (int yy = y; yy < pH; ++yy) { int xx = x; #ifdef __SSE2__ + for (; xx < pW - 3; xx += 4) { minRv = vminf(minRv, LVFU(R[yy][xx])); minGv = vminf(minGv, LVFU(G[yy][xx])); minBv = vminf(minBv, LVFU(B[yy][xx])); } + #endif + for (; xx < pW; ++xx) { minR = min(minR, R[yy][xx]); minG = min(minG, G[yy][xx]); minB = min(minB, B[yy][xx]); } } + #ifdef __SSE2__ minR = min(minR, vhmin(minRv)); minG = min(minG, vhmin(minGv)); @@ -137,13 +152,14 @@ int get_dark_channel(const array2D &R, const array2D &G, const arr #endif float val = min(minR / ambient[0], minG / ambient[1], minB / ambient[2]); val = 1.f - strength * LIM01(val); + for (int yy = y; yy < pH; ++yy) { std::fill(dst[yy] + x, dst[yy] + pW, val); } } } - return (W / patchsize + ((W % patchsize) > 0)) * (H / patchsize + ((H % patchsize) > 0)); + return (W / patchsize + ((W % patchsize) > 0)) * (H / patchsize + ((H % patchsize) > 0)); } int get_dark_channel_downsized(const array2D &R, const array2D &G, const array2D &B, const array2D &dst, int patchsize, bool multithread) @@ -154,23 +170,27 @@ int get_dark_channel_downsized(const array2D &R, const array2D &G, #ifdef _OPENMP #pragma omp parallel for if (multithread) #endif + for (int y = 0; y < H; y += patchsize) { const int pH = min(y + patchsize, H); + for (int x = 0; x < W; x += patchsize) { float val = RT_INFINITY_F; const int pW = min(x + patchsize, W); + for (int xx = x; xx < pW; ++xx) { for (int yy = y; yy < pH; ++yy) { val = min(val, R[yy][xx], G[yy][xx], B[yy][xx]); } } + for (int yy = y; yy < pH; ++yy) { std::fill(dst[yy] + x, dst[yy] + pW, val); } } } - return (W / patchsize + ((W % patchsize) > 0)) * (H / patchsize + ((H % patchsize) > 0)); + return (W / patchsize + ((W % patchsize) > 0)) * (H / patchsize + ((H % patchsize) > 0)); } float estimate_ambient_light(const array2D &R, const array2D &G, const array2D &B, const array2D &dark, int patchsize, int npatches, float ambient[3]) @@ -181,6 +201,7 @@ float estimate_ambient_light(const array2D &R, const array2D &G, c float darklim = RT_INFINITY_F; { std::vector p; + for (int y = 0; y < H; y += patchsize) { for (int x = 0; x < W; x += patchsize) { if (!OOG(dark[y][x], 1.f - 1e-5f)) { @@ -188,6 +209,7 @@ float estimate_ambient_light(const array2D &R, const array2D &G, c } } } + const int pos = p.size() * 0.95; std::nth_element(p.begin(), p.begin() + pos, p.end()); darklim = p[pos]; @@ -213,17 +235,18 @@ float estimate_ambient_light(const array2D &R, const array2D &G, c { std::vector l; l.reserve(patches.size() * patchsize * patchsize); - + for (auto &p : patches) { - const int pW = min(p.first+patchsize, W); - const int pH = min(p.second+patchsize, H); - + const int pW = min(p.first + patchsize, W); + const int pH = min(p.second + patchsize, H); + for (int y = p.second; y < pH; ++y) { for (int x = p.first; x < pW; ++x) { l.push_back(R[y][x] + G[y][x] + B[y][x]); } } } + const int pos = l.size() * 0.95; std::nth_element(l.begin(), l.begin() + pos, l.end()); bright_lim = l[pos]; @@ -231,15 +254,17 @@ float estimate_ambient_light(const array2D &R, const array2D &G, c double rr = 0, gg = 0, bb = 0; int n = 0; + for (auto &p : patches) { - const int pW = min(p.first+patchsize, W); - const int pH = min(p.second+patchsize, H); - + const int pW = min(p.first + patchsize, W); + const int pH = min(p.second + patchsize, H); + for (int y = p.second; y < pH; ++y) { for (int x = p.first; x < pW; ++x) { float r = R[y][x]; float g = G[y][x]; float b = B[y][x]; + if (r + g + b >= bright_lim) { rr += static_cast(r); gg += static_cast(g); @@ -249,6 +274,7 @@ float estimate_ambient_light(const array2D &R, const array2D &G, c } } } + n = std::max(n, 1); ambient[0] = rr / n; ambient[1] = gg / n; @@ -275,9 +301,9 @@ void extract_channels(Imagefloat *img, array2D &r, array2D &g, arr } // namespace -void ImProcFunctions::dehaze(Imagefloat *img) +void ImProcFunctions::dehaze(Imagefloat *img, const DehazeParams &dehazeParams) { - if (!params->dehaze.enabled || params->dehaze.strength == 0.0) { + if (!dehazeParams.enabled || dehazeParams.strength == 0.0) { return; } @@ -285,7 +311,7 @@ void ImProcFunctions::dehaze(Imagefloat *img) const int W = img->getWidth(); const int H = img->getHeight(); - const float strength = LIM01(float(params->dehaze.strength) / 100.f * 0.9f); + const float strength = LIM01(float(dehazeParams.strength) / 100.f * 0.9f); if (settings->verbose) { std::cout << "dehaze: strength = " << strength << std::endl; @@ -356,11 +382,11 @@ void ImProcFunctions::dehaze(Imagefloat *img) std::cout << "dehaze: max distance is " << maxDistance << std::endl; } - const float depth = -float(params->dehaze.depth) / 100.f; + const float depth = -float(dehazeParams.depth) / 100.f; const float t0 = max(1e-3f, std::exp(depth * maxDistance)); const float teps = 1e-3f; - const bool luminance = params->dehaze.luminance; + const bool luminance = dehazeParams.luminance; const TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); #ifdef __SSE2__ const vfloat wsv[3] = {F2V(ws[1][0]), F2V(ws[1][1]),F2V(ws[1][2])}; @@ -389,7 +415,7 @@ void ImProcFunctions::dehaze(Imagefloat *img) // ... t >= tl to avoid negative values const vfloat tlv = onev - vminf(r / ambient0v, vminf(g / ambient1v, b / ambient2v)); const vfloat mtv = vmaxf(LVFU(dark[y][x]), vmaxf(tlv + tepsv, t0v)); - if (params->dehaze.showDepthMap) { + if (dehazeParams.showDepthMap) { const vfloat valv = vclampf(onev - mtv, ZEROV, onev) * cmaxChannelv; STVFU(img->r(y, x), valv); STVFU(img->g(y, x), valv); @@ -416,7 +442,7 @@ void ImProcFunctions::dehaze(Imagefloat *img) // ... t >= tl to avoid negative values const float tl = 1.f - min(r / ambient[0], g / ambient[1], b / ambient[2]); const float mt = max(dark[y][x], t0, tl + teps); - if (params->dehaze.showDepthMap) { + if (dehazeParams.showDepthMap) { img->r(y, x) = img->g(y, x) = img->b(y, x) = LIM01(1.f - mt) * maxChannel; } else if (luminance) { const float Y = Color::rgbLuminance(img->r(y, x), img->g(y, x), img->b(y, x), ws); @@ -434,4 +460,167 @@ void ImProcFunctions::dehaze(Imagefloat *img) } } + + +void ImProcFunctions::dehazeloc(Imagefloat *img, const DehazeParams &dehazeParams) +{ + //J.Desmis 12 2019 - this version derived from ART, is slower than the main from maximum 10% - probably use of SSE + //Probably Ingo could solved this problem in some times + BENCHFUN + if (!dehazeParams.enabled || dehazeParams.strength == 0.0) { + return; + } + + + + const float maxChannel = normalize(img, multiThread); + + const int W = img->getWidth(); + const int H = img->getHeight(); + const float strength = LIM01(float(std::abs(dehazeParams.strength)) / 100.f * 0.9f); + const bool add_haze = dehazeParams.strength < 0; + + if (settings->verbose) { + std::cout << "dehaze: strength = " << strength << std::endl; + } + + array2D dark(W, H); + + int patchsize = max(int(5 / scale), 2); + float ambient[3]; + float maxDistance = 0.f; + + { + array2D& R = dark; // R and dark can safely use the same buffer, which is faster and reduces memory allocations/deallocations + array2D G(W, H); + array2D B(W, H); + extract_channels(img, R, G, B, patchsize, 1e-1, multiThread); + + { + constexpr int sizecap = 200; + const float r = static_cast(W) / static_cast(H); + const int hh = r >= 1.f ? sizecap : sizecap / r; + const int ww = r >= 1.f ? sizecap * r : sizecap; + + if (W <= ww && H <= hh) { + // don't rescale small thumbs + array2D D(W, H); + const int npatches = get_dark_channel_downsized(R, G, B, D, 2, multiThread); + maxDistance = estimate_ambient_light(R, G, B, D, patchsize, npatches, ambient); + } else { + array2D RR(ww, hh); + array2D GG(ww, hh); + array2D BB(ww, hh); + rescaleNearest(R, RR, multiThread); + rescaleNearest(G, GG, multiThread); + rescaleNearest(B, BB, multiThread); + array2D D(ww, hh); + + const int npatches = get_dark_channel_downsized(RR, GG, BB, D, 2, multiThread); + maxDistance = estimate_ambient_light(RR, GG, BB, D, patchsize, npatches, ambient); + } + } + + if (min(ambient[0], ambient[1], ambient[2]) < 0.01f) { + if (settings->verbose) { + std::cout << "dehaze: no haze detected" << std::endl; + } + + restore(img, maxChannel, multiThread); + return; // probably no haze at all + } + + patchsize = max(max(W, H) / 600, 2); + + if (settings->verbose) { + std::cout << "dehaze: ambient light is " + << ambient[0] << ", " << ambient[1] << ", " << ambient[2] + << std::endl; + } + + get_dark_channel(R, G, B, dark, patchsize, ambient, true, multiThread, strength); + } + + + const int radius = patchsize * 4; + constexpr float epsilon = 1e-5f; + + array2D guideB(W, H, img->b.ptrs, ARRAY2D_BYREFERENCE); + guidedFilter(guideB, dark, dark, radius, epsilon, multiThread); + + if (settings->verbose) { + std::cout << "dehaze: max distance is " << maxDistance << std::endl; + } + + const float depth = -float(dehazeParams.depth) / 100.f; + const float teps = 1e-6f; + const float t0 = max(teps, std::exp(depth * maxDistance)); + + const bool luminance = dehazeParams.luminance; + const TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); + + const float ambientY = Color::rgbLuminance(ambient[0], ambient[1], ambient[2], ws); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + + for (int y = 0; y < H; ++y) { + int x = 0; + for (; x < W; ++x) { + // ensure that the transmission is such that to avoid clipping... + float rgb[3] = { img->r(y, x), img->g(y, x), img->b(y, x) }; + // ... t >= tl to avoid negative values + float tl = 1.f - min(rgb[0] / ambient[0], rgb[1] / ambient[1], rgb[2] / ambient[2]); + // // ... t >= tu to avoid values > 1 + // float tu = t0 - teps; + // for (int c = 0; c < 3; ++c) { + // if (ambient[c] < 1) { + // tu = max(tu, (rgb[c] - ambient[c])/(1.f - ambient[c])); + // } + // } + float &ir = img->r(y, x); + float &ig = img->g(y, x); + float &ib = img->b(y, x); + const float mt = max(dark[y][x], t0, tl + teps); + + if (dehazeParams.showDepthMap) { + img->r(y, x) = img->g(y, x) = img->b(y, x) = LIM01(1.f - mt) * maxChannel; + } else if (luminance) { + float Y = Color::rgbLuminance(img->r(y, x), img->g(y, x), img->b(y, x), ws); + float YY = (Y - ambientY) / mt + ambientY; + + if (Y > 1e-5f) { + if (add_haze) { + YY = Y + Y - YY; + } + + float f = YY / Y; + ir = rgb[0] * f; + ig = rgb[1] * f; + ib = rgb[2] * f; + + } + } else { + float r = ((rgb[0] - ambient[0]) / mt + ambient[0]); + float g = ((rgb[1] - ambient[1]) / mt + ambient[1]); + float b = ((rgb[2] - ambient[2]) / mt + ambient[2]); + + if (add_haze) { + ir += (ir - r); + ig += (ig - g); + ib += (ib - b); + } else { + ir = r; + ig = g; + ib = b; + } + + } + } + } + + restore(img, maxChannel, multiThread); + +} + } // namespace rtengine diff --git a/rtengine/ipgrain.cc b/rtengine/ipgrain.cc new file mode 100644 index 000000000..b9079606a --- /dev/null +++ b/rtengine/ipgrain.cc @@ -0,0 +1,371 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2018 Alberto Griggio + * Small adaptation to Rawtherapee Locallab October 2019 + * 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 . + */ + +/* film grain emulation. + * Ported from darktable (src/iop/grain.c). Original copyright/license follows + */ +/* + This file is part of darktable, + copyright (c) 2010-2012 Henrik Andersson. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ + +#include "imagefloat.h" +#include "improcfun.h" +#include "rt_math.h" + + +namespace rtengine { + +namespace { + +constexpr float GRAIN_LIGHTNESS_STRENGTH_SCALE = 0.15f; +constexpr float GRAIN_SCALE_FACTOR = 213.2f; + +constexpr int GRAIN_LUT_SIZE = 128; +constexpr float GRAIN_LUT_DELTA_MAX = 2.0f; +constexpr float GRAIN_LUT_DELTA_MIN = 0.0001f; +constexpr float GRAIN_LUT_PAPER_GAMMA = 1.0f; + + +const int grad3[12][3] = { { 1, 1, 0 }, + { -1, 1, 0 }, + { 1, -1, 0 }, + { -1, -1, 0 }, + { 1, 0, 1 }, + { -1, 0, 1 }, + { 1, 0, -1 }, + { -1, 0, -1 }, + { 0, 1, 1 }, + { 0, -1, 1 }, + { 0, 1, -1 }, + { 0, -1, -1 } }; + +const int permutation[] + = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, + 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, + 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, + 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, + 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, + 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, + 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, + 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, + 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, + 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, + 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, + 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 }; + + +class GrainEvaluator { +public: + GrainEvaluator(int offset_x, int offset_y, int full_width, int full_height, double scale): + ox(offset_x), + oy(offset_y), + fw(full_width), + fh(full_height), + scale(scale) + { + simplex_noise_init(); + constexpr float mb = 100.f; + evaluate_grain_lut(mb); + } + + void operator()(int isogr, int strengr, int scalegr, Imagefloat *lab, bool multithread) + { + const double strength = (strengr / 100.0); + const double octaves = 3; + const double wd = std::min(fw, fh); + const double zoom = (1.0 + 8 * (double(isogr) / GRAIN_SCALE_FACTOR) / 100.0) / 800.0; + const double s = std::max(scale / 3.0, 1.0) / (double(std::max(scalegr, 1)) / 100.0); + + const int W = lab->getWidth(); + const int H = lab->getHeight(); + float **lab_L = lab->g.ptrs; + +#ifdef _OPENMP +# pragma omp parallel for if (multithread) +#endif + for (int j = 0; j < H; ++j) { + double wy = oy + j; + double y = wy / wd; + for (int i = 0; i < W; ++i) { + double wx = ox + i; + double x = wx / wd; + double noise = simplex_2d_noise(x, y, octaves, 1.0, zoom) / s; + lab_L[j][i] += lut_lookup(noise * strength * GRAIN_LIGHTNESS_STRENGTH_SCALE, lab_L[j][i] / 32768.f); + } + } + } + +private: + void simplex_noise_init() + { + for(int i = 0; i < 512; i++) perm[i] = permutation[i & 255]; + } + + double dot(const int *g, double x, double y, double z) + { + return g[0] * x + g[1] * y + g[2] * z; + } + + float FASTFLOOR(float x) + { + return (x > 0 ? (int)(x) : (int)(x)-1); + } + + double simplex_noise(double xin, double yin, double zin) + { + double n0, n1, n2, n3; // Noise contributions from the four corners + // Skew the input space to determine which simplex cell we're in + const double F3 = 1.0 / 3.0; + const double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D + const int i = FASTFLOOR(xin + s); + const int j = FASTFLOOR(yin + s); + const int k = FASTFLOOR(zin + s); + const double G3 = 1.0 / 6.0; // Very nice and simple unskew factor, too + const double t = (i + j + k) * G3; + const double X0 = i - t; // Unskew the cell origin back to (x,y,z) space + const double Y0 = j - t; + const double Z0 = k - t; + const double x0 = xin - X0; // The x,y,z distances from the cell origin + const double y0 = yin - Y0; + const double z0 = zin - Z0; + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if(x0 >= y0) + { + if(y0 >= z0) + { + i1 = 1; // X Y Z order + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } + else if(x0 >= z0) + { + i1 = 1; // X Z Y order + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } + else + { + i1 = 0; // Z X Y order + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } + } + else // x0localContrast.enabled) { + if (!localContrastParams.enabled) { return; } const int width = lab->W; const int height = lab->H; - const float a = params->localContrast.amount; - const float dark = params->localContrast.darkness; - const float light = params->localContrast.lightness; + const float a = localContrastParams.amount; + const float dark = localContrastParams.darkness; + const float light = localContrastParams.lightness; array2D buf(width, height); - const float sigma = params->localContrast.radius / scale; - + float sigma = localContrastParams.radius / scale; + //printf("wi%i he=%i am=%f da=%f li=%f si=%f\n", width, height, a, dark, light, sigma); + if(!fftwlc) { #ifdef _OPENMP - #pragma omp parallel if(multiThread) + #pragma omp parallel if(multiThread) #endif - gaussianBlur(lab->L, buf, width, height, sigma); - + gaussianBlur(lab->L, buf, width, height, sigma); + } else { + float kr = 1.f; + //emprical adjustment between FFTW radius and Gaussainblur + //under 50 ==> 10.f + //above 400 ==> 1.f + if(settings->fftwsigma == false) {//empirical formula + float ak = -9.f / 350.f; + float bk = 10.f - 50.f * ak; + kr = ak * sigma + bk; + if(sigma < 50.f) kr = 10.f; + if(sigma > 400.f) kr = 1.f; + } else {//sigma *= sigma + kr = sigma; + } + //OPENMP disabled + ImProcFunctions::fftw_convol_blur2(lab->L, buf, width, height, kr * sigma, 0, 0); + } #ifdef _OPENMP #pragma omp parallel for if(multiThread) #endif + for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { float bufval = (lab->L[y][x] - buf[y][x]) * a; @@ -61,7 +80,7 @@ void ImProcFunctions::localContrast(LabImage *lab) bufval *= (bufval > 0.f) ? light : dark; } - lab->L[y][x] = std::max(0.0001f, lab->L[y][x] + bufval); + destination[y][x] = LIM(lab->L[y][x] + bufval, 0.0001f, 32767.f); } } } diff --git a/rtengine/iplocallab.cc b/rtengine/iplocallab.cc new file mode 100644 index 000000000..7b27712e1 --- /dev/null +++ b/rtengine/iplocallab.cc @@ -0,0 +1,14608 @@ +/* + * 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 . + * 2016 - 2020 Jacques Desmis + * 2016 - 2020 Ingo Weyrich + + */ +#include +#include + +#include "improcfun.h" +#include "colortemp.h" +#include "curves.h" +#include "gauss.h" +#include "iccstore.h" +#include "imagefloat.h" +#include "labimage.h" +#include "color.h" +#include "rt_math.h" +#include "jaggedarray.h" +#include "rt_algo.h" +#include "settings.h" +#include "../rtgui/options.h" + +#include "utils.h" +#ifdef _OPENMP +#include +#endif +#include "../rtgui/thresholdselector.h" +#include "imagesource.h" + +#include "cplx_wavelet_dec.h" +#include "ciecam02.h" + +//#define BENCHMARK +#include "StopWatch.h" +#include "guidedfilter.h" + + +#pragma GCC diagnostic warning "-Wall" +#pragma GCC diagnostic warning "-Wextra" + +namespace +{ +constexpr int limscope = 80; +constexpr int mSPsharp = 39; //minimum size Spot Sharp due to buildblendmask +constexpr int mSPwav = 32; //minimum size Spot Wavelet +constexpr int mDEN = 64; //minimum size Spot Denoise +constexpr int mSP = 5; //minimum size Spot +constexpr float MAXSCOPE = 1.25f; +constexpr float MINSCOPE = 0.025f; +constexpr int TS = 64; // Tile size +constexpr float epsilonw = 0.001f / (TS * TS); //tolerance +constexpr int offset = 25; // shift between tiles + +constexpr float clipLoc(float x) { + return rtengine::LIM(x, 0.f, 32767.f); +} + +constexpr float clipDE(float x) { + return rtengine::LIM(x, 0.3f, 1.f); +} + +constexpr float clipC(float x) { + return rtengine::LIM(x, -42000.f, 42000.f); +} + +constexpr float clipChro(float x) { + return rtengine::LIM(x, 0.f, 140.f); +} + +float softlig(float a, float b, float minc, float maxc) +{ + // as Photoshop + if (2.f * b <= maxc - minc) { + return a * (2.f * b + a * (maxc - 2.f * b)); + } else { + return 2.f * a * (maxc - b) + std::sqrt(rtengine::LIM(a, 0.f, 2.f)) * (2.f * b - maxc); + } +} + +float softlig3(float a, float b) +{ + // as w3C + if (2.f * b <= 1.f) { + return a - (1.f - 2.f * b) * a * (1.f - a); + } else { + if (4.f * a <= 1.f) { + return a + (2.f * b - 1.f) * (4.f * a * (4.f * a + 1.f) * (a - 1.f) + 7.f * a); + } else { + return a + (2.f * a - 1.f) * (std::sqrt(a) - a); + } + } +} + +float softlig2(float a, float b) +{ + // illusions.hu + return pow_F(b, pow_F(2.f, (2.f * (0.5f - a)))); +} + +constexpr float colburn(float a, float b) +{ + // w3C + return b == 0.f ? 0.f : 1.f - rtengine::min(1.f, (1.f - a) / b); +} + +constexpr float coldodge(float a, float b) +{ + // w3C + return b == 1.f ? 1.f : rtengine::min(1.f, a / (1.f - b)); +} + +float overlay(float a, float b, float minc, float maxc) +{ + if (2.f * b <= maxc - minc) { + return 2.f * b * a; + } else { + return maxc - 2.f * (1.f - a) * (maxc - b); + } +} + +constexpr float screen(float a, float b, float maxc) +{ + return 1.f - (1.f - a) * (maxc - b); +} + +constexpr float exclusion(float a, float b) +{ + return a + b - 2.f * a * b; +} + +void calcGammaLut(double gamma, double ts, LUTf &gammaLut) +{ + double pwr = 1.0 / gamma; + double gamm = gamma; + const double gamm2 = gamma; + rtengine::GammaValues g_a; + + if (gamm2 < 1.0) { + std::swap(pwr, gamm); + } + + rtengine::Color::calcGamma(pwr, ts, 0, g_a); // call to calcGamma with selected gamma and slope + + const double start = gamm2 < 1. ? g_a[2] : g_a[3]; + const double add = g_a[4]; + const double mul = 1.0 + g_a[4]; + + if (gamm2 < 1.) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 1024) +#endif + for (int i = 0; i < 65536; i++) { + const double x = rtengine::Color::igammareti(i / 65535.0, gamm, start, ts, mul, add); + gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values + } + } else { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 1024) +#endif + for (int i = 0; i < 65536; i++) { + const double x = rtengine::Color::gammareti(i / 65535.0, gamm, start, ts, mul, add); + gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values + } + } +} + +float calcLocalFactor(const float lox, const float loy, const float lcx, const float dx, const float lcy, const float dy, const float ach, const float gradient) +{ + //ellipse x2/a2 + y2/b2=1 + //transition ellipsoidal + const float kelip = dx / dy; + const float belip = rtengine::max(0.0001f, std::sqrt((rtengine::SQR((lox - lcx) / kelip) + rtengine::SQR(loy - lcy)))); //determine position ellipse ==> a and b + + //gradient allows differentiation between transition x and y + const float rapy = std::fabs((loy - lcy) / belip); + const float aelip = belip * kelip; + const float degrad = aelip / dx; + const float gradreal = gradient * rapy + 1.f; + const float ap = rtengine::RT_PI_F / (1.f - ach); + const float bp = rtengine::RT_PI_F - ap; + return pow(0.5f * (1.f + xcosf(degrad * ap + bp)), rtengine::SQR(gradreal)); // trigo cos transition +} + +float calcLocalFactorrect(const float lox, const float loy, const float lcx, const float dx, const float lcy, const float dy, const float ach, const float gradient) +{ + constexpr float eps = 0.0001f; + const float krap = std::fabs(dx / dy); + const float kx = lox - lcx; + const float ky = loy - lcy; + + float ref; + //gradient allows differentiation between transition x and y + if (std::fabs(kx / (ky + eps)) < krap) { + ref = std::sqrt(rtengine::SQR(dy) * (1.f + rtengine::SQR(kx / (ky + eps)))); + } else { + ref = std::sqrt(rtengine::SQR(dx) * (1.f + rtengine::SQR(ky / (kx + eps)))); + } + + const float rad = rtengine::max(eps, std::sqrt(rtengine::SQR(kx) + rtengine::SQR(ky))); + const float rapy = std::fabs((loy - lcy) / rad); + const float gradreal = gradient * rapy + 1.f; + + const float coef = rad / ref; + const float fact = (coef - 1.f) / (ach - 1.f); + return pow(fact, rtengine::SQR(gradreal)); +} + +float calcreducdE(float dE, float maxdE, float mindE, float maxdElim, float mindElim, float iterat, int limscope, int scope) +{ + if (scope > limscope) {//80 arbitrary value, if we change we must change limscope + if (dE > maxdElim) { + return 0.f; + } else if (dE > mindElim) { + const float reducdElim = std::pow((dE - maxdElim) / (mindElim - maxdElim), iterat); + const float aalim = (1.f - reducdElim) / 20.f; + const float bblim = 1.f - 100.f * aalim; + return aalim * scope + bblim; + } else { + return 1.f; + } + } else { + if (dE > maxdE) { + return 0.f; + } else if (dE > mindE) { + return std::pow((dE - maxdE) / (mindE - maxdE), iterat); + } else { + return 1.f; + } + } +} + +void deltaEforLaplace(float *dE, const float lap, int bfw, int bfh, rtengine::LabImage* bufexporig, const float hueref, const float chromaref, const float lumaref) +{ + + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + const float refL = lumaref; + float maxdE = 5.f + MAXSCOPE * lap; + + std::unique_ptr dEforLaplace(new float [bfw * bfh]); + float maxC = std::sqrt((rtengine::SQR(refa - bufexporig->a[0][0]) + rtengine::SQR(refb - bufexporig->b[0][0])) + rtengine::SQR(refL - bufexporig->L[0][0])) / 327.68f; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxC) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + dEforLaplace[y * bfw + x] = std::sqrt((rtengine::SQR(refa - bufexporig->a[y][x]) + rtengine::SQR(refb - bufexporig->b[y][x])) + rtengine::SQR(refL - bufexporig->L[y][x])) / 327.68f; + maxC = rtengine::max(maxC, dEforLaplace[y * bfw + x]); + } + } + + if (maxdE > maxC) { + maxdE = maxC - 1.f; + } + + const float ade = 1.f / (maxdE - maxC); + const float bde = -ade * maxC; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + dE[y * bfw + x] = dEforLaplace[y * bfw + x] >= maxdE ? ade * dEforLaplace[y * bfw + x] + bde : 1.f; + } + } +} + +float calclight(float lum, const LUTf &lightCurveloc) +{ + return clipLoc(lightCurveloc[lum]); +} + +float calclightinv(float lum, float koef, const LUTf &lightCurveloc) +{ + return koef != -100.f ? clipLoc(lightCurveloc[lum]) : 0.f; +} + +float balancedeltaE(float kL) +{ + constexpr float mincurs = 0.3f; // minimum slider balan_ + constexpr float maxcurs = 1.7f; // maximum slider balan_ + constexpr float maxkab = 1.35; // 0.5 * (3 - 0.3) + constexpr float minkab = 0.65; // 0.5 * (3 - 1.7) + constexpr float abal = (maxkab - minkab) / (mincurs - maxcurs); + constexpr float bbal = maxkab - mincurs * abal; + return abal * kL + bbal; +} + +void SobelCannyLuma(float **sobelL, float **luma, int bfw, int bfh, float radius) +{ + // base of the process to detect shape in complement of deltaE + // use for calculate Spot reference + // and for structure of the shape + // actually , as the program don't use these function, I just create a simple "Canny" near of Sobel. This can be completed after with teta, etc. + array2D tmL(bfw, bfh); + + //inspired from Chen Guanghua Zhang Xiaolong + //Sobel Horizontal + constexpr float GX[3][3] = { + {1.f, 0.f, -1.f}, + {2.f, 0.f, -2.f}, + {1.f, 0.f, -1.f} + }; + + //Sobel Vertical + constexpr float GY[3][3] = { + {1.f, 2.f, 1.f}, + {0.f, 0.f, 0.f}, + {-1.f, -2.f, -1.f} + }; + + if (radius > 0.f) { + gaussianBlur(luma, tmL, bfw, bfh, rtengine::max(radius / 2.f, 0.5f)); + } else { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw ; x++) { + tmL[y][x] = luma[y][x]; + } + } + } + + for (int x = 0; x < bfw; x++) { + sobelL[0][x] = 0.f; + } + for (int y = 1; y < bfh - 1; y++) { + sobelL[y][0] = 0.f; + for (int x = 1; x < bfw - 1; x++) { + float sumXL = 0.f; + float sumYL = 0.f; + for (int i = -1; i < 2; i += 2) { + for (int j = -1; j < 2; j += 1) { + sumXL += GX[j + 1][i + 1] * tmL[y + i][x + j]; + sumYL += GY[j + 1][i + 1] * tmL[y + i][x + j]; + } + } + //Edge strength + //we can add if need teta = atan2 (sumYr, sumXr) + sobelL[y][x] = rtengine::min(std::sqrt(rtengine::SQR(sumXL) + rtengine::SQR(sumYL)), 32767.f); + } + sobelL[y][bfw - 1] = 0.f; + } + for (int x = 0; x < bfw; x++) { + sobelL[bfh - 1][x] = 0.f; + } +} + +} + +namespace rtengine + +{ +extern MyMutex *fftwMutex; + +using namespace procparams; + +struct local_params { + float yc, xc; + float lx, ly; + float lxL, lyT; + float transweak; + float transgrad; + float iterat; + float balance; + float balanceh; + int colorde; + int cir; + float thr; + float stru; + int chro, cont, sens, sensh, senscb, sensbn, senstm, sensex, sensexclu, sensden, senslc, senssf, senshs, senscolor; + float clarityml; + float contresid; + float blurcbdl; + bool deltaem; + float struco; + float strengrid; + float struexc; + float blendmacol; + float radmacol; + float chromacol; + float gammacol; + float slomacol; + float blendmalc; + float radmalc; + float chromalc; + float radmaexp; + float chromaexp; + float gammaexp; + float slomaexp; + float strmaexp; + float angmaexp; + float strexp; + float angexp; + float strSH; + float angSH; + float strcol; + float strcolab; + float strcolh; + float angcol; + float strvib; + float strvibab; + float strvibh; + float angvib; + float angwav; + float strwav; + + float strengthw; + float radiusw; + float detailw; + float gradw; + float tloww; + float thigw; + float edgw; + float basew; + + float anglog; + float strlog; + float softradiusexp; + float softradiuscol; + float softradiuscb; + float softradiusret; + float softradiustm; + float blendmaexp; + float radmaSH; + float blendmaSH; + float chromaSH; + float gammaSH; + float slomaSH; + float radmavib; + float blendmavib; + float chromavib; + float gammavib; + float slomavib; + float radmacb; + float blendmacb; + float chromacbm; + float gammacb; + float slomacb; + float radmatm; + float blendmatm; + float chromatm; + float gammatm; + float slomatm; + + float radmabl; + float blendmabl; + float chromabl; + float gammabl; + float slomabl; + + float struexp; + float blurexp; + float blurcol; + float blurcolmask; + float contcolmask; + float blurSH; + float ligh; + float lowA, lowB, highA, highB; + float lowBmerg, highBmerg, lowAmerg, highAmerg; + int shamo, shdamp, shiter, senssha, sensv; + float neig; + float strng; + float lap; + float lcamount; + double shrad; + double shblurr; + double rad; + double stren; + int it; + int guidb; + float strbl; + float epsb; + float trans; + float feath; + int dehaze; + int depth; + bool inv; + bool invex; + bool invsh; + bool curvact; + bool invrad; + bool invret; + bool equret; + bool equtm; + bool invshar; + bool actsp; + bool ftwlc; + bool ftwreti; + float str; + int qualmet; + int qualcurvemet; + int gridmet; + bool prevdE; + int showmaskcolmet; + int showmaskcolmetinv; + int showmaskexpmet; + int showmaskexpmetinv; + int showmaskSHmet; + int showmaskSHmetinv; + int showmaskvibmet; + int showmasklcmet; + int showmasksharmet; + int showmaskcbmet; + int showmaskretimet; + int showmasksoftmet; + int showmasktmmet; + int showmaskblmet; + bool fftbl; + float laplacexp; + float balanexp; + float linear; + int expmet; + int softmet; + int blurmet; + int blmet; + int smasktyp; + int chromet; + int shmeth; + int medmet; + int locmet; + float noiself; + float noiself0; + float noiself2; + float noiseldetail; + int detailthr; + int noiselequal; + float noisechrodetail; + float bilat; + float noiselc; + float noisecf; + float noisecc; + float mulloc[6]; + int mullocsh[5]; + int detailsh; + float threshol; + float chromacb; + float strengt; + float gamm; + float esto; + float scalt; + float rewe; + float amo; + bool colorena; + bool blurena; + bool tonemapena; + bool retiena; + bool sharpena; + bool lcena; + bool sfena; + bool cbdlena; + bool denoiena; + bool expvib; + bool exposena; + bool hsena; + bool vibena; + bool logena; + bool cut_past; + float past; + float satur; + int blac; + int shcomp; + int shadex; + int hlcomp; + int hlcompthr; + float expcomp; + float expchroma; + int excmet; + int mergemet; + int mergecolMethod; + float opacol; + int war; + float adjch; + int shapmet; + int edgwmet; + int neiwmet; + bool enaColorMask; + bool fftColorMask; + bool enaColorMaskinv; + bool enaExpMask; + bool enaExpMaskinv; + bool enaSHMask; + bool enaSHMaskinv; + bool enavibMask; + bool enalcMask; + bool enasharMask; + bool enacbMask; + bool enaretiMask; + bool enaretiMasktmap; + bool enatmMask; + bool enablMask; + int highlihs; + int shadowhs; + int radiushs; + int hltonalhs; + int shtonalhs; + int scalereti; + float sourcegray; + float targetgray; + float blackev; + float whiteev; + float detail; + int sensilog; + bool Autogray; + bool autocompute; + float baselog; + bool wavgradl; + bool edgwena; + bool lip3; + int daubLen; + float sigmadr; + float sigmabl; + float sigmaed; + float sigmalc; + float sigmalc2; + float residsha; + float residshathr; + float residhi; + float residhithr; + bool blwh; + +}; + +static void calcLocalParams(int sp, int oW, int oH, const LocallabParams& locallab, struct local_params& lp, bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, const LocwavCurve & locwavCurveden, bool locwavdenutili) +{ + int w = oW; + int h = oH; + int circr = locallab.spots.at(sp).circrad; + float streng = ((float)locallab.spots.at(sp).stren); + float gam = ((float)locallab.spots.at(sp).gamma); + float est = ((float)locallab.spots.at(sp).estop); + float scal_tm = ((float)locallab.spots.at(sp).scaltm); + float rewe = ((float)locallab.spots.at(sp).rewei); + float amo = ((float)locallab.spots.at(sp).amount); + float strlight = ((float)locallab.spots.at(sp).streng); + float strucc = locallab.spots.at(sp).struc; + float laplac = ((float)locallab.spots.at(sp).laplace); + float thre = locallab.spots.at(sp).thresh; + + if (thre > 8.f || thre < 0.f) {//to avoid artifacts if user does not clear cache with new settings. Can be suppressed after + thre = 2.f; + } + + double local_x = locallab.spots.at(sp).loc.at(0) / 2000.0; + double local_y = locallab.spots.at(sp).loc.at(2) / 2000.0; + double local_xL = locallab.spots.at(sp).loc.at(1) / 2000.0; + double local_yT = locallab.spots.at(sp).loc.at(3) / 2000.0; + double local_center_x = locallab.spots.at(sp).centerX / 2000.0 + 0.5; + double local_center_y = locallab.spots.at(sp).centerY / 2000.0 + 0.5; + float iterati = (float) locallab.spots.at(sp).iter; + float balanc = (float) locallab.spots.at(sp).balan; + float balanch = (float) locallab.spots.at(sp).balanh; + int colorde = (int) locallab.spots.at(sp).colorde; + + if (iterati > 4.f || iterati < 0.2f) {//to avoid artifacts if user does not clear cache with new settings Can be suppressed after + iterati = 2.f; + } + + float neigh = float (locallab.spots.at(sp).neigh); + float chromaPastel = float (locallab.spots.at(sp).pastels) / 100.0f; + float chromaSatur = float (locallab.spots.at(sp).saturated) / 100.0f; + int local_sensiv = locallab.spots.at(sp).sensiv; + int local_sensiex = locallab.spots.at(sp).sensiex; + + if (locallab.spots.at(sp).qualityMethod == "enh") { + lp.qualmet = 1; + } else if (locallab.spots.at(sp).qualityMethod == "enhden") { + lp.qualmet = 2; + } + + if (locallab.spots.at(sp).qualitycurveMethod == "none") { + lp.qualcurvemet = 0; + } else if (locallab.spots.at(sp).qualitycurveMethod == "std") { + lp.qualcurvemet = 1; + } + + if (locallab.spots.at(sp).gridMethod == "one") { + lp.gridmet = 0; + } else if (locallab.spots.at(sp).gridMethod == "two") { + lp.gridmet = 1; + } + + if (locallab.spots.at(sp).expMethod == "std") { + lp.expmet = 0; + } else if (locallab.spots.at(sp).expMethod == "pde") { + lp.expmet = 1; + } + + if (locallab.spots.at(sp).localcontMethod == "loc") { + lp.locmet = 0; + } else if (locallab.spots.at(sp).localcontMethod == "wav") { + lp.locmet = 1; + } + + lp.laplacexp = locallab.spots.at(sp).laplacexp; + lp.balanexp = locallab.spots.at(sp).balanexp; + lp.linear = locallab.spots.at(sp).linear; + + lp.fftColorMask = locallab.spots.at(sp).fftColorMask; + lp.prevdE = prevDeltaE; + lp.showmaskcolmet = llColorMask; + lp.showmaskcolmetinv = llColorMaskinv; + lp.showmaskexpmet = llExpMask; + lp.showmaskexpmetinv = llExpMaskinv; + lp.showmaskSHmet = llSHMask; + lp.showmaskSHmetinv = llSHMaskinv; + lp.showmaskvibmet = llvibMask; + lp.showmasklcmet = lllcMask; + lp.showmasksharmet = llsharMask; + lp.showmaskcbmet = llcbMask; + lp.showmaskretimet = llretiMask; + lp.showmasksoftmet = llsoftMask; + + lp.showmasktmmet = lltmMask; + lp.showmaskblmet = llblMask; + lp.enaColorMask = locallab.spots.at(sp).enaColorMask && llsoftMask == 0 && llColorMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaColorMaskinv = locallab.spots.at(sp).enaColorMask && llColorMaskinv == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaExpMask = locallab.spots.at(sp).enaExpMask && llExpMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaExpMaskinv = locallab.spots.at(sp).enaExpMask && llExpMaskinv == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0;// Exposure mask is deactivated if Color & Light mask is visible + lp.enaSHMask = locallab.spots.at(sp).enaSHMask && llSHMask == 0 && llColorMask == 0 && llsoftMask == 0 && lllcMask == 0 && llsharMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + lp.enaSHMaskinv = locallab.spots.at(sp).enaSHMask && llSHMaskinv == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + lp.enacbMask = locallab.spots.at(sp).enacbMask && llcbMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + lp.enaretiMask = locallab.spots.at(sp).enaretiMask && lllcMask == 0 && llsharMask == 0 && llsoftMask == 0 && llretiMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + lp.enatmMask = locallab.spots.at(sp).enatmMask && lltmMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && llblMask == 0 && llvibMask == 0; + lp.enablMask = locallab.spots.at(sp).enablMask && llblMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0; + lp.enavibMask = locallab.spots.at(sp).enavibMask && llvibMask == 0 && lllcMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llSHMask == 0; + lp.enalcMask = locallab.spots.at(sp).enalcMask && lllcMask == 0 && llcbMask == 0 && llsoftMask == 0 && llsharMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + lp.enasharMask = lllcMask == 0 && llcbMask == 0 && llsharMask == 0 && llsoftMask == 0 && llColorMask == 0 && llExpMask == 0 && llSHMask == 0 && llretiMask == 0 && lltmMask == 0 && llblMask == 0 && llvibMask == 0; + + // printf("llColorMask=%i lllcMask=%i llExpMask=%i llSHMask=%i llcbMask=%i llretiMask=%i lltmMask=%i llblMask=%i llvibMask=%i\n", llColorMask, lllcMask, llExpMask, llSHMask, llcbMask, llretiMask, lltmMask, llblMask, llvibMask); + if (locallab.spots.at(sp).softMethod == "soft") { + lp.softmet = 0; + } else if (locallab.spots.at(sp).softMethod == "reti") { + lp.softmet = 1; + } + + if (locallab.spots.at(sp).blMethod == "blur") { + lp.blmet = 0; + } else if (locallab.spots.at(sp).blMethod == "med") { + lp.blmet = 1; + } else if (locallab.spots.at(sp).blMethod == "guid") { + lp.blmet = 2; + } + + if (locallab.spots.at(sp).chroMethod == "lum") { + lp.chromet = 0; + } else if (locallab.spots.at(sp).chroMethod == "chr") { + lp.chromet = 1; + } else if (locallab.spots.at(sp).chroMethod == "all") { + lp.chromet = 2; + } + + if (locallab.spots.at(sp).shMethod == "std") { + lp.shmeth = 0; + } else if (locallab.spots.at(sp).shMethod == "tone") { + lp.shmeth = 1; + } + + + if (locallab.spots.at(sp).medMethod == "none") { + lp.medmet = -1; + } else if (locallab.spots.at(sp).medMethod == "33") { + lp.medmet = 0; + } else if (locallab.spots.at(sp).medMethod == "55") { + lp.medmet = 1; + } else if (locallab.spots.at(sp).medMethod == "77") { + lp.medmet = 2; + } else if (locallab.spots.at(sp).medMethod == "99") { + lp.medmet = 3; + } + + if (locallab.spots.at(sp).blurMethod == "norm") { + lp.blurmet = 0; + } else if (locallab.spots.at(sp).blurMethod == "inv") { + lp.blurmet = 1; + } + + if (locallab.spots.at(sp).showmaskblMethodtyp == "blur") { + lp.smasktyp = 0; + } else if (locallab.spots.at(sp).showmaskblMethodtyp == "nois") { + lp.smasktyp = 1; + } else if (locallab.spots.at(sp).showmaskblMethodtyp == "all") { + lp.smasktyp = 2; + } + + + if (locallab.spots.at(sp).spotMethod == "norm") { + lp.excmet = 0; + } else if (locallab.spots.at(sp).spotMethod == "exc") { + lp.excmet = 1; + } + + if (locallab.spots.at(sp).merMethod == "mone") { + lp.mergemet = 0; + } else if (locallab.spots.at(sp).merMethod == "mtwo") { + lp.mergemet = 1; + } else if (locallab.spots.at(sp).merMethod == "mthr") { + lp.mergemet = 2; + } else if (locallab.spots.at(sp).merMethod == "mfou") { + lp.mergemet = 3; + } else if (locallab.spots.at(sp).merMethod == "mfiv") { + lp.mergemet = 4; + } + + if (locallab.spots.at(sp).mergecolMethod == "one") { + lp.mergecolMethod = 0; + } else if (locallab.spots.at(sp).mergecolMethod == "two") { + lp.mergecolMethod = 1; + } else if (locallab.spots.at(sp).mergecolMethod == "thr") { + lp.mergecolMethod = 2; + } else if (locallab.spots.at(sp).mergecolMethod == "fou") { + lp.mergecolMethod = 3; + } else if (locallab.spots.at(sp).mergecolMethod == "fiv") { + lp.mergecolMethod = 4; + } else if (locallab.spots.at(sp).mergecolMethod == "six") { + lp.mergecolMethod = 5; + } else if (locallab.spots.at(sp).mergecolMethod == "sev") { + lp.mergecolMethod = 6; + } else if (locallab.spots.at(sp).mergecolMethod == "sev0") { + lp.mergecolMethod = 7; + } else if (locallab.spots.at(sp).mergecolMethod == "sev1") { + lp.mergecolMethod = 8; + } else if (locallab.spots.at(sp).mergecolMethod == "sev2") { + lp.mergecolMethod = 9; + } else if (locallab.spots.at(sp).mergecolMethod == "hei") { + lp.mergecolMethod = 10; + } else if (locallab.spots.at(sp).mergecolMethod == "nin") { + lp.mergecolMethod = 11; + } else if (locallab.spots.at(sp).mergecolMethod == "ten") { + lp.mergecolMethod = 12; + } else if (locallab.spots.at(sp).mergecolMethod == "ele") { + lp.mergecolMethod = 13; + } else if (locallab.spots.at(sp).mergecolMethod == "twe") { + lp.mergecolMethod = 14; + } else if (locallab.spots.at(sp).mergecolMethod == "thi") { + lp.mergecolMethod = 15; + } else if (locallab.spots.at(sp).mergecolMethod == "for") { + lp.mergecolMethod = 16; + } else if (locallab.spots.at(sp).mergecolMethod == "hue") { + lp.mergecolMethod = 17; + } else if (locallab.spots.at(sp).mergecolMethod == "sat") { + lp.mergecolMethod = 18; + } else if (locallab.spots.at(sp).mergecolMethod == "col") { + lp.mergecolMethod = 19; + } else if (locallab.spots.at(sp).mergecolMethod == "lum") { + lp.mergecolMethod = 20; + } + + if (locallab.spots.at(sp).localedgMethod == "fir") { + lp.edgwmet = 0; + } else if (locallab.spots.at(sp).localedgMethod == "sec") { + lp.edgwmet = 1; + } else if (locallab.spots.at(sp).localedgMethod == "thr") { + lp.edgwmet = 2; + } + + if (locallab.spots.at(sp).localneiMethod == "none") { + lp.neiwmet = -1; + lp.lip3 = false; + } else if (locallab.spots.at(sp).localneiMethod == "low") { + lp.neiwmet = 0; + lp.lip3 = true; + } else if (locallab.spots.at(sp).localneiMethod == "high") { + lp.lip3 = true; + lp.neiwmet = 1; + } + + + if (locallab.spots.at(sp).wavMethod == "D2") { + lp.daubLen = 4; + } else if (locallab.spots.at(sp).wavMethod == "D4") { + lp.daubLen = 6; + } else if (locallab.spots.at(sp).wavMethod == "D6") { + lp.daubLen = 8; + } else if (locallab.spots.at(sp).wavMethod == "D10") { + lp.daubLen = 12; + } else if (locallab.spots.at(sp).wavMethod == "D14") { + lp.daubLen = 16; + } + + + lp.edgwena = locallab.spots.at(sp).wavedg; + + lp.opacol = 0.01 * locallab.spots.at(sp).opacol; + + if (locallab.spots.at(sp).shape == "ELI") { + lp.shapmet = 0; + } else /*if (locallab.spots.at(sp).shape == "RECT")*/ { + lp.shapmet = 1; + } + + lp.denoiena = locallab.spots.at(sp).expblur; + + bool wavcurveden = false; + float local_noiself = 0.f; + float local_noiself0 = 0.f; + float local_noiself2 = 0.f; + float local_noiselc = 0.f; + + if (locwavCurveden && locwavdenutili) { + if (lp.denoiena) { + for (int i = 0; i < 500; i++) { + if (locwavCurveden[i] != 0.f) { + wavcurveden = true; + } + } + } + } + + if (wavcurveden) { + if (lp.denoiena) { + local_noiself0 = 250.f * locwavCurveden[0]; + local_noiself = 250.f * locwavCurveden[166]; + local_noiself2 = 250.f * locwavCurveden[323]; + local_noiselc = 200.f * locwavCurveden[500]; + } + } + + float local_noiseldetail = (float)locallab.spots.at(sp).noiselumdetail; + int local_noiselequal = locallab.spots.at(sp).noiselequal; + float local_noisechrodetail = (float)locallab.spots.at(sp).noisechrodetail; + int local_sensiden = locallab.spots.at(sp).sensiden; + float local_detailthr = (float)locallab.spots.at(sp).detailthr; + + float local_noisecf = ((float)locallab.spots.at(sp).noisechrof) / 10.f; + float local_noisecc = ((float)locallab.spots.at(sp).noisechroc) / 10.f; + float multi[6]; + + for (int y = 0; y < 6; y++) { + multi[y] = ((float) locallab.spots.at(sp).mult[y]); + } + + float multish[5]; + + for (int y = 0; y < 5; y++) { + multish[y] = ((float) locallab.spots.at(sp).multsh[y]); + } + + float thresho = ((float)locallab.spots.at(sp).threshold); + float chromcbdl = (float)locallab.spots.at(sp).chromacbdl; + + int local_chroma = locallab.spots.at(sp).chroma; + int local_sensi = locallab.spots.at(sp).sensi; + int local_sensibn = locallab.spots.at(sp).sensibn; + int local_sensitm = locallab.spots.at(sp).sensitm; + int local_sensiexclu = locallab.spots.at(sp).sensiexclu; + float structexclude = (float) locallab.spots.at(sp).structexclu; + int local_sensilc = locallab.spots.at(sp).sensilc; + int local_warm = locallab.spots.at(sp).warm; + int local_sensih = locallab.spots.at(sp).sensih; + int local_dehaze = locallab.spots.at(sp).dehaz; + int local_depth = locallab.spots.at(sp).depth; + int local_sensicb = locallab.spots.at(sp).sensicb; + float local_clarityml = (float) locallab.spots.at(sp).clarityml; + float local_contresid = (float) locallab.spots.at(sp).contresid; + int local_blurcbdl = 0; //(float) locallab.spots.at(sp).blurcbdl; + int local_contrast = locallab.spots.at(sp).contrast; + float local_lightness = (float) locallab.spots.at(sp).lightness; + float labgridALowloc = locallab.spots.at(sp).labgridALow; + float labgridBLowloc = locallab.spots.at(sp).labgridBLow; + float labgridBHighloc = locallab.spots.at(sp).labgridBHigh; + float labgridAHighloc = locallab.spots.at(sp).labgridAHigh; + float strengthgrid = (float) locallab.spots.at(sp).strengthgrid; + float labgridBLowlocmerg = locallab.spots.at(sp).labgridBLowmerg; + float labgridBHighlocmerg = locallab.spots.at(sp).labgridBHighmerg; + float labgridALowlocmerg = locallab.spots.at(sp).labgridALowmerg; + float labgridAHighlocmerg = locallab.spots.at(sp).labgridAHighmerg; + + float blendmasklc = ((float) locallab.spots.at(sp).blendmasklc) / 100.f ; + float radmasklc = ((float) locallab.spots.at(sp).radmasklc); + float chromasklc = ((float) locallab.spots.at(sp).chromasklc); + float structcolor = (float) locallab.spots.at(sp).structcol; + float blendmaskcolor = ((float) locallab.spots.at(sp).blendmaskcol) / 100.f ; + float radmaskcolor = ((float) locallab.spots.at(sp).radmaskcol); + float chromaskcolor = ((float) locallab.spots.at(sp).chromaskcol); + float gammaskcolor = ((float) locallab.spots.at(sp).gammaskcol); + float slomaskcolor = ((float) locallab.spots.at(sp).slomaskcol); + float blendmaskexpo = ((float) locallab.spots.at(sp).blendmaskexp) / 100.f ; + float radmaskexpo = ((float) locallab.spots.at(sp).radmaskexp); + float chromaskexpo = ((float) locallab.spots.at(sp).chromaskexp); + float gammaskexpo = ((float) locallab.spots.at(sp).gammaskexp); + float slomaskexpo = ((float) locallab.spots.at(sp).slomaskexp); + float strmaskexpo = ((float) locallab.spots.at(sp).strmaskexp); + float angmaskexpo = ((float) locallab.spots.at(sp).angmaskexp); + float strexpo = ((float) locallab.spots.at(sp).strexp); + float angexpo = ((float) locallab.spots.at(sp).angexp); + float strSH = ((float) locallab.spots.at(sp).strSH); + float angSH = ((float) locallab.spots.at(sp).angSH); + float strcol = ((float) locallab.spots.at(sp).strcol); + float strcolab = ((float) locallab.spots.at(sp).strcolab); + float strcolh = ((float) locallab.spots.at(sp).strcolh); + float angcol = ((float) locallab.spots.at(sp).angcol); + float strvib = ((float) locallab.spots.at(sp).strvib); + float strvibab = ((float) locallab.spots.at(sp).strvibab); + float strvibh = ((float) locallab.spots.at(sp).strvibh); + float angvib = ((float) locallab.spots.at(sp).angvib); + float strwav = ((float) locallab.spots.at(sp).strwav); + float angwav = ((float) locallab.spots.at(sp).angwav); + float strlog = ((float) locallab.spots.at(sp).strlog); + float anglog = ((float) locallab.spots.at(sp).anglog); + float softradiusexpo = ((float) locallab.spots.at(sp).softradiusexp); + float softradiuscolor = ((float) locallab.spots.at(sp).softradiuscol); + float softradiusreti = ((float) locallab.spots.at(sp).softradiusret); + float softradiustma = ((float) locallab.spots.at(sp).softradiustm); + float softradiuscbdl = ((float) locallab.spots.at(sp).softradiuscb); + float blendmaskSH = ((float) locallab.spots.at(sp).blendmaskSH) / 100.f ; + float radmaskSH = ((float) locallab.spots.at(sp).radmaskSH); + float chromaskSH = ((float) locallab.spots.at(sp).chromaskSH); + float gammaskSH = ((float) locallab.spots.at(sp).gammaskSH); + float slomaskSH = ((float) locallab.spots.at(sp).slomaskSH); + float blendmaskvib = ((float) locallab.spots.at(sp).blendmaskvib) / 100.f ; + float radmaskvib = ((float) locallab.spots.at(sp).radmaskvib); + float chromaskvib = ((float) locallab.spots.at(sp).chromaskvib); + float gammaskvib = ((float) locallab.spots.at(sp).gammaskvib); + float slomaskvib = ((float) locallab.spots.at(sp).slomaskvib); + float structexpo = (float) locallab.spots.at(sp).structexp; + float blurexpo = (float) locallab.spots.at(sp).blurexpde; + float blurcolor = (float) locallab.spots.at(sp).blurcolde; + float blurcolmask = (float) locallab.spots.at(sp).blurcol; + float contcolmask = (float) locallab.spots.at(sp).contcol; + float blurSH = (float) locallab.spots.at(sp).blurSHde; + float local_transit = locallab.spots.at(sp).transit; + float local_feather = locallab.spots.at(sp).feather; + float local_transitweak = (float)locallab.spots.at(sp).transitweak; + float local_transitgrad = (float)locallab.spots.at(sp).transitgrad; + float radius = (float) locallab.spots.at(sp).radius; + int itera = locallab.spots.at(sp).itera; + int guidbl = locallab.spots.at(sp).guidbl; + float epsbl = (float) locallab.spots.at(sp).epsbl; + float sharradius = LIM(locallab.spots.at(sp).sharradius, 0.42, 3.5); + float lcamount = ((float) locallab.spots.at(sp).lcamount); + lcamount = LIM01(lcamount); //to prevent crash with old pp3 integer + float sharblurr = LIM(locallab.spots.at(sp).sharblur, 0.2, 3.); //to prevent crash with old pp3 integer + int local_sensisha = locallab.spots.at(sp).sensisha; + int local_sharamount = locallab.spots.at(sp).sharamount; + int local_shardamping = locallab.spots.at(sp).shardamping; + int local_shariter = locallab.spots.at(sp).shariter; + bool inverse = locallab.spots.at(sp).invers; + bool curvacti = locallab.spots.at(sp).curvactiv; + bool acti = locallab.spots.at(sp).activlum; + bool cupas = false; // Provision + int local_sensisf = locallab.spots.at(sp).sensisf; + bool inverseex = locallab.spots.at(sp).inversex; + bool inversesh = locallab.spots.at(sp).inverssh; + bool equiltm = locallab.spots.at(sp).equiltm; + bool fftwlc = locallab.spots.at(sp).fftwlc; + bool fftwreti = locallab.spots.at(sp).fftwreti; + + bool equilret = locallab.spots.at(sp).equilret; + bool inverserad = false; // Provision + bool inverseret = locallab.spots.at(sp).inversret; + bool inversesha = locallab.spots.at(sp).inverssha; + double strength = (double) locallab.spots.at(sp).strength; + float str = (float)locallab.spots.at(sp).str; + int scaleret = (float)locallab.spots.at(sp).scalereti; + + int local_sensihs = locallab.spots.at(sp).sensihs; + int highhs = locallab.spots.at(sp).highlights; + int hltonahs = locallab.spots.at(sp).h_tonalwidth; + int shadhs = locallab.spots.at(sp).shadows; + int shtonals = locallab.spots.at(sp).s_tonalwidth; + int radhs = locallab.spots.at(sp).sh_radius; + float blendmaskcb = ((float) locallab.spots.at(sp).blendmaskcb) / 100.f ; + float radmaskcb = ((float) locallab.spots.at(sp).radmaskcb); + float chromaskcb = ((float) locallab.spots.at(sp).chromaskcb); + float gammaskcb = ((float) locallab.spots.at(sp).gammaskcb); + float slomaskcb = ((float) locallab.spots.at(sp).slomaskcb); + bool enaretiMasktm = locallab.spots.at(sp).enaretiMasktmap; + lp.enaretiMasktmap = enaretiMasktm; + float blendmasktm = ((float) locallab.spots.at(sp).blendmasktm) / 100.f ; + float radmasktm = ((float) locallab.spots.at(sp).radmasktm); + float chromasktm = ((float) locallab.spots.at(sp).chromasktm); + float gammasktm = ((float) locallab.spots.at(sp).gammasktm); + float slomasktm = ((float) locallab.spots.at(sp).slomasktm); + bool wavgradl = locallab.spots.at(sp).wavgradl; + + float blendmaskbl = ((float) locallab.spots.at(sp).blendmaskbl) / 100.f ; + float radmaskbl = ((float) locallab.spots.at(sp).radmaskbl); + float chromaskbl = ((float) locallab.spots.at(sp).chromaskbl); + float gammaskbl = ((float) locallab.spots.at(sp).gammaskbl); + float slomaskbl = ((float) locallab.spots.at(sp).slomaskbl); + bool fftbl = locallab.spots.at(sp).fftwbl; + + + lp.sourcegray = (float) locallab.spots.at(sp).sourceGray; + lp.targetgray = (float) locallab.spots.at(sp).targetGray; + lp.blackev = (float) locallab.spots.at(sp).blackEv; + lp.whiteev = (float) locallab.spots.at(sp).whiteEv; + lp.detail = locallab.spots.at(sp).detail; + lp.sensilog = locallab.spots.at(sp).sensilog; + lp.Autogray = locallab.spots.at(sp).Autogray; + lp.autocompute = locallab.spots.at(sp).autocompute; + lp.baselog = (float) locallab.spots.at(sp).baselog; + + lp.deltaem = locallab.spots.at(sp).deltae; + lp.scalereti = scaleret; + lp.cir = circr; + lp.actsp = acti; + lp.xc = w * local_center_x; + lp.yc = h * local_center_y; + lp.lx = w * local_x; + lp.ly = h * local_y; + lp.lxL = w * local_xL; + lp.lyT = h * local_yT; + lp.chro = local_chroma; + lp.struco = structcolor; + lp.strengrid = strengthgrid; + lp.blendmalc = blendmasklc; + lp.radmalc = radmasklc; + lp.chromalc = chromasklc; + lp.blendmacol = blendmaskcolor; + lp.radmacol = radmaskcolor; + lp.chromacol = chromaskcolor; + lp.gammacol = gammaskcolor; + lp.slomacol = slomaskcolor; + lp.radmaexp = radmaskexpo; + lp.chromaexp = chromaskexpo; + lp.gammaexp = gammaskexpo; + lp.slomaexp = slomaskexpo; + lp.strmaexp = strmaskexpo; + lp.angmaexp = angmaskexpo; + lp.strexp = strexpo; + lp.angexp = angexpo; + lp.strSH = strSH; + lp.angSH = angSH; + lp.strcol = strcol; + lp.strcolab = strcolab; + lp.strcolh = strcolh; + lp.angcol = angcol; + lp.strvib = strvib; + lp.strvibab = strvibab; + lp.strvibh = strvibh; + lp.angvib = angvib; + lp.strwav = strwav; + lp.angwav = angwav; + lp.strlog = strlog; + lp.anglog = anglog; + lp.softradiusexp = softradiusexpo; + lp.softradiuscol = softradiuscolor; + lp.softradiusret = softradiusreti; + lp.softradiuscb = softradiuscbdl; + lp.softradiustm = softradiustma; + lp.struexc = structexclude; + lp.blendmaexp = blendmaskexpo; + lp.blendmaSH = blendmaskSH; + lp.radmaSH = radmaskSH; + lp.chromaSH = chromaskSH; + lp.gammaSH = gammaskSH; + lp.slomaSH = slomaskSH; + lp.blendmavib = blendmaskvib; + lp.radmavib = radmaskvib; + lp.chromavib = chromaskvib; + lp.gammavib = gammaskvib; + lp.slomavib = slomaskvib; + lp.blendmacb = blendmaskcb; + lp.radmacb = radmaskcb; + lp.chromacbm = chromaskcb; + lp.gammacb = gammaskcb; + lp.slomacb = slomaskcb; + lp.blendmatm = blendmasktm; + lp.radmatm = radmasktm; + lp.chromatm = chromasktm; + lp.gammatm = gammasktm; + lp.slomatm = slomasktm; + lp.wavgradl = wavgradl; + + lp.strengthw = ((float) locallab.spots.at(sp).strengthw); + lp.radiusw = ((float) locallab.spots.at(sp).radiusw); + lp.detailw = ((float) locallab.spots.at(sp).detailw); + lp.gradw = ((float) locallab.spots.at(sp).gradw); + lp.tloww = ((float) locallab.spots.at(sp).tloww); + lp.thigw = ((float) locallab.spots.at(sp).thigw); + lp.edgw = ((float) locallab.spots.at(sp).edgw); + lp.basew = ((float) locallab.spots.at(sp).basew); + + lp.blendmabl = blendmaskbl; + lp.radmabl = radmaskbl; + lp.chromabl = chromaskbl; + lp.gammabl = gammaskbl; + lp.slomabl = slomaskbl; + lp.fftbl = fftbl; + lp.it = itera; + lp.guidb = guidbl; + lp.strbl = 0.01f * (float) locallab.spots.at(sp).strbl; + + lp.epsb = epsbl; + lp.struexp = structexpo; + lp.blurexp = blurexpo; + lp.blurcol = blurcolor; + lp.blurcolmask = blurcolmask; + lp.contcolmask = 0.01f * contcolmask; + lp.blurSH = blurSH; + lp.sens = local_sensi; + lp.sensh = local_sensih; + lp.dehaze = local_dehaze; + lp.depth = local_depth; + lp.senscb = local_sensicb; + lp.clarityml = local_clarityml; + lp.contresid = local_contresid; + lp.blurcbdl = local_blurcbdl; + lp.cont = local_contrast; + lp.ligh = local_lightness; + lp.lowA = labgridALowloc; + lp.lowB = labgridBLowloc; + lp.highB = labgridBHighloc; + lp.highA = labgridAHighloc; + lp.lowBmerg = labgridBLowlocmerg; + lp.highBmerg = labgridBHighlocmerg; + lp.lowAmerg = labgridALowlocmerg; + lp.highAmerg = labgridAHighlocmerg; + + lp.senssf = local_sensisf; + lp.strng = strlight; + lp.neig = neigh; + lp.lap = laplac; + + if (lp.ligh >= -2.f && lp.ligh <= 2.f) { + lp.ligh /= 5.f; + } + + lp.trans = local_transit; + lp.feath = local_feather; + lp.transweak = local_transitweak; + lp.transgrad = local_transitgrad; + lp.rad = radius; + lp.stren = strength; + lp.sensbn = local_sensibn; + lp.sensexclu = local_sensiexclu; + lp.senslc = local_sensilc; + lp.lcamount = lcamount; + lp.inv = inverse; + lp.invex = inverseex; + lp.invsh = inversesh; + lp.curvact = curvacti; + lp.invrad = inverserad; + lp.invret = inverseret; + lp.equret = equilret; + lp.equtm = equiltm; + lp.invshar = inversesha; + lp.str = str; + lp.shrad = sharradius; + lp.shblurr = sharblurr; + lp.senssha = local_sensisha; + lp.shamo = local_sharamount; + lp.shdamp = local_shardamping; + lp.shiter = local_shariter; + lp.iterat = iterati; + lp.balance = balanc; + lp.balanceh = balanch; + lp.colorde = colorde; + lp.thr = thre; + lp.stru = strucc; + lp.noiself = local_noiself; + lp.noiself0 = local_noiself0; + lp.noiself2 = local_noiself2; + lp.noiseldetail = local_noiseldetail; + lp.detailthr = local_detailthr; + lp.noiselequal = local_noiselequal; + lp.noisechrodetail = local_noisechrodetail; + lp.noiselc = local_noiselc; + lp.noisecf = local_noisecf; + lp.noisecc = local_noisecc; + lp.sensden = local_sensiden; + lp.bilat = locallab.spots.at(sp).bilateral; + lp.adjch = (float) locallab.spots.at(sp).adjblur; + lp.strengt = streng; + lp.gamm = gam; + lp.esto = est; + lp.scalt = scal_tm; + lp.rewe = rewe; + lp.senstm = local_sensitm; + lp.amo = amo; + + for (int y = 0; y < 6; y++) { + lp.mulloc[y] = LIM(multi[y], 0.f, 4.f);//to prevent crash with old pp3 integer + } + + for (int y = 0; y < 5; y++) { + lp.mullocsh[y] = multish[y]; + } + + lp.logena = locallab.spots.at(sp).explog; + + lp.detailsh = locallab.spots.at(sp).detailSH; + lp.threshol = thresho; + lp.chromacb = chromcbdl; + lp.expvib = locallab.spots.at(sp).expvibrance; + lp.colorena = locallab.spots.at(sp).expcolor && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0; // Color & Light tool is deactivated if Exposure mask is visible or SHMask + lp.blurena = locallab.spots.at(sp).expblur && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0; + lp.tonemapena = locallab.spots.at(sp).exptonemap && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llColorMask == 0 && llvibMask == 0; + lp.retiena = locallab.spots.at(sp).expreti && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0; + lp.lcena = locallab.spots.at(sp).expcontrast && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llcbMask == 0 && llsharMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0 && llSHMask == 0; + lp.cbdlena = locallab.spots.at(sp).expcbdl && llExpMask == 0 && llsoftMask == 0 && llSHMask == 0 && llretiMask == 0 && lllcMask == 0 && llsharMask == 0 && lllcMask == 0 && llColorMask == 0 && lltmMask == 0 && llvibMask == 0; + lp.exposena = locallab.spots.at(sp).expexpose && llColorMask == 0 && llsoftMask == 0 && llSHMask == 0 && lllcMask == 0 && llsharMask == 0 && llcbMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0; // Exposure tool is deactivated if Color & Light mask SHmask is visible + lp.hsena = locallab.spots.at(sp).expshadhigh && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && lltmMask == 0 && llvibMask == 0;// Shadow Highlight tool is deactivated if Color & Light mask or SHmask is visible + lp.vibena = locallab.spots.at(sp).expvibrance && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llsharMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0;// vibrance tool is deactivated if Color & Light mask or SHmask is visible + lp.sharpena = locallab.spots.at(sp).expsharp && llColorMask == 0 && llsoftMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0; + lp.sfena = locallab.spots.at(sp).expsoft && llColorMask == 0 && llExpMask == 0 && llcbMask == 0 && lllcMask == 0 && llretiMask == 0 && llcbMask == 0 && lltmMask == 0 && llSHMask == 0 && llvibMask == 0; + lp.sensv = local_sensiv; + lp.past = chromaPastel; + lp.satur = chromaSatur; + + lp.cut_past = cupas; + lp.blac = locallab.spots.at(sp).black; + lp.shcomp = locallab.spots.at(sp).shcompr; + lp.shadex = locallab.spots.at(sp).shadex; + lp.hlcomp = locallab.spots.at(sp).hlcompr; + lp.hlcompthr = locallab.spots.at(sp).hlcomprthresh; + lp.expcomp = LIM(locallab.spots.at(sp).expcomp, -2.0, 4.0); //to prevent crash with Old pp3 with integer + //increase sensitivity for low values + float proexp = lp.expcomp; + if (std::fabs(proexp) < 0.6f) { + float interm = std::fabs(proexp) / 0.6f; + interm = SQR(interm); + lp.expcomp = proexp * interm; + } + lp.expchroma = locallab.spots.at(sp).expchroma / 100.; + lp.sensex = local_sensiex; + lp.war = local_warm; + lp.highlihs = highhs; + lp.shadowhs = shadhs; + lp.radiushs = radhs; + lp.hltonalhs = hltonahs; + lp.shtonalhs = shtonals; + lp.senshs = local_sensihs; + lp.ftwlc = fftwlc; + lp.ftwreti = fftwreti; + lp.sigmadr = locallab.spots.at(sp).sigmadr; + lp.sigmabl = locallab.spots.at(sp).sigmabl; + lp.sigmaed = locallab.spots.at(sp).sigmaed; + lp.sigmalc = locallab.spots.at(sp).sigmalc; + lp.sigmalc2 = locallab.spots.at(sp).sigmalc2; + lp.residsha = locallab.spots.at(sp).residsha; + lp.residshathr = locallab.spots.at(sp).residshathr; + lp.residhi = locallab.spots.at(sp).residhi; + lp.residhithr = locallab.spots.at(sp).residhithr; + lp.blwh = locallab.spots.at(sp).blwh; + lp.senscolor = (int) locallab.spots.at(sp).colorscope; + //replace scope color exposure vibrance shadows + lp.sens = lp.senscolor; + lp.sensv = lp.senscolor; + lp.senshs = lp.senscolor; + if(lp.expmet == 0){ + lp.sensex = lp.senscolor; + } +} + +static void calcTransitionrect(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) +{ + zone = 0; + + if (lox >= lp.xc && lox < lp.xc + lp.lx) { + if (loy >= lp.yc && loy < lp.yc + lp.ly) { + if (lox < lp.xc + lp.lx * ach && loy < lp.yc + lp.ly * ach) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lx, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } else if (loy < lp.yc && loy > lp.yc - lp.lyT) { + if (lox < lp.xc + lp.lx * ach && loy > lp.yc - lp.lyT * ach) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lx, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } + } else if (lox < lp.xc && lox > lp.xc - lp.lxL) { + if (loy <= lp.yc && loy > lp.yc - lp.lyT) { + if (lox > (lp.xc - lp.lxL * ach) && loy > (lp.yc - lp.lyT * ach)) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lxL, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } else if (loy > lp.yc && loy < lp.yc + lp.ly) { + if (lox > (lp.xc - lp.lxL * ach) && loy < (lp.yc + lp.ly * ach)) { + zone = 2; + } else { + zone = 1; + localFactor = pow_F(calcLocalFactorrect(lox, loy, lp.xc, lp.lxL, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } + } +} + +static void calcTransition(const float lox, const float loy, const float ach, const local_params& lp, int &zone, float &localFactor) +{ + // returns the zone (0 = outside selection, 1 = transition zone between outside and inside selection, 2 = inside selection) + // and a factor to calculate the transition in case zone == 1 + + zone = 0; + + if (lox >= lp.xc && lox < lp.xc + lp.lx) { + if (loy >= lp.yc && loy < lp.yc + lp.ly) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lx)) + SQR((loy - lp.yc) / (ach * lp.ly)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lx)) + SQR((loy - lp.yc) / (lp.ly))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lx, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } + } else if (loy < lp.yc && loy > lp.yc - lp.lyT) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lx)) + SQR((loy - lp.yc) / (ach * lp.lyT)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lx)) + SQR((loy - lp.yc) / (lp.lyT))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lx, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } + } + } else if (lox < lp.xc && lox > lp.xc - lp.lxL) { + if (loy <= lp.yc && loy > lp.yc - lp.lyT) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lxL)) + SQR((loy - lp.yc) / (ach * lp.lyT)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lxL)) + SQR((loy - lp.yc) / (lp.lyT))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lxL, lp.yc, lp.lyT, ach, lp.transgrad), lp.transweak); + } + } + } else if (loy > lp.yc && loy < lp.yc + lp.ly) { + const float zoneVal = SQR((lox - lp.xc) / (ach * lp.lxL)) + SQR((loy - lp.yc) / (ach * lp.ly)); + zone = zoneVal < 1.f ? 2 : 0; + + if (!zone) { + zone = (zoneVal > 1.f && ((SQR((lox - lp.xc) / (lp.lxL)) + SQR((loy - lp.yc) / (lp.ly))) < 1.f)) ? 1 : 0; + if (zone == 1) { + localFactor = pow_F(calcLocalFactor(lox, loy, lp.xc, lp.lxL, lp.yc, lp.ly, ach, lp.transgrad), lp.transweak); + } + } + } + } +} + +// Copyright 2018 Alberto Griggio +//J.Desmis 12 2019 - I will try to port a raw process in local adjustments +// I choose this one because, it is "new" +// Perhaps - probably no result, but perhaps ?? + +float find_gray(float source_gray, float target_gray) +{ + // find a base such that log2lin(base, source_gray) = target_gray + // log2lin is (base^source_gray - 1) / (base - 1), so we solve + // + // (base^source_gray - 1) / (base - 1) = target_gray, that is + // + // base^source_gray - 1 - base * target_gray + target_gray = 0 + // + // use a bisection method (maybe later change to Netwon) + + if (source_gray <= 0.f) { + return 0.f; + } + + const auto f = + [ = ](float x) -> float { + return std::pow(x, source_gray) - 1 - target_gray * x + target_gray; + }; + + // first find the interval we are interested in + + float lo = 1.f; + + while (f(lo) <= 0.f) { + lo *= 2.f; + } + + float hi = lo * 2.f; + + while (f(hi) >= 0.f) { + hi *= 2.f; + } + + if (std::isinf(hi)) { + return 0.f; + } + + // now search for a zero + for (int iter = 0; iter < 100; ++iter) { + float mid = lo + (hi - lo) / 2.f; + float v = f(mid); + + if (std::abs(v) < 1e-4f || (hi - lo) / lo <= 1e-4f) { + return mid; + } + + if (v > 0.f) { + lo = mid; + } else { + hi = mid; + } + } + + return 0.f; // not found +} + + +// basic log encoding taken from ACESutil.Lin_to_Log2, from +// https://github.com/ampas/aces-dev +// (as seen on pixls.us) +void ImProcFunctions::log_encode(Imagefloat *rgb, const struct local_params & lp, bool multiThread, int bfw, int bfh) +{ + /* J.Desmis 12 2019 + small adaptations to local adjustments + replace log2 by log(lp.baselog) allows diferentiation between low and high lights + */ + BENCHFUN + const float gray = lp.sourcegray / 100.f; + const float shadows_range = lp.blackev; + const float dynamic_range = lp.whiteev - lp.blackev; + const float noise = pow_F(2.f, -16.f); + const float log2 = xlogf(lp.baselog); + const float base = lp.targetgray > 1 && lp.targetgray < 100 && dynamic_range > 0 ? find_gray(std::abs(lp.blackev) / dynamic_range, lp.targetgray / 100.f) : 0.f; + const float linbase = rtengine::max(base, 0.f); + TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); + + const auto apply = + [ = ](float x, bool scale = true) -> float { + if (scale) + { + x /= 65535.f; + } + + x = rtengine::max(x, noise); + x = rtengine::max(x / gray, noise); + x = rtengine::max((xlogf(x) / log2 - shadows_range) / dynamic_range, noise); + assert(x == x); + + if (linbase > 0.f) + { + x = xlog2lin(x, linbase); + } + + if (scale) + { + return x * 65535.f; + } else { + return x; + } + }; + + const auto norm = + [&](float r, float g, float b) -> float { + return Color::rgbLuminance(r, g, b, ws); + + // other possible alternatives (so far, luminance seems to work + // fine though). See also + // https://discuss.pixls.us/t/finding-a-norm-to-preserve-ratios-across-non-linear-operations + // + // MAX + //return max(r, g, b); + // + // Euclidean + //return std::sqrt(SQR(r) + SQR(g) + SQR(b)); + + // weighted yellow power norm from https://youtu.be/Z0DS7cnAYPk + // float rr = 1.22f * r / 65535.f; + // float gg = 1.20f * g / 65535.f; + // float bb = 0.58f * b / 65535.f; + // float rr4 = SQR(rr) * SQR(rr); + // float gg4 = SQR(gg) * SQR(gg); + // float bb4 = SQR(bb) * SQR(bb); + // float den = (rr4 + gg4 + bb4); + // if (den > 0.f) { + // return 0.8374319f * ((rr4 * rr + gg4 * gg + bb4 * bb) / den) * 65535.f; + // } else { + // return 0.f; + // } + }; + + const float detail = lp.detail; + const int W = rgb->getWidth(), H = rgb->getHeight(); + + if (detail == 0.f) {//no local contrast +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float r = rgb->r(y, x); + float g = rgb->g(y, x); + float b = rgb->b(y, x); + float m = norm(r, g, b); + + if (m > noise) { + float mm = apply(m); + float f = mm / m; + r *= f; + b *= f; + g *= f; + } + + assert(r == r); + assert(g == g); + assert(b == b); + + rgb->r(y, x) = r; + rgb->g(y, x) = g; + rgb->b(y, x) = b; + } + } + } else {//local contrast + + array2D Y(W, H); + { + constexpr float base_posterization = 20.f; + array2D Y2(W, H); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + Y2[y][x] = norm(rgb->r(y, x), rgb->g(y, x), rgb->b(y, x)) / 65535.f; + float l = xlogf(rtengine::max(Y2[y][x], 1e-9f)); + float ll = round(l * base_posterization) / base_posterization; + Y[y][x] = xexpf(ll); + assert(std::isfinite(Y[y][x])); + } + } + const float radius = rtengine::max(rtengine::max(bfw, W), rtengine::max(bfh, H)) / 30.f; + const float epsilon = 0.005f; + rtengine::guidedFilter(Y2, Y, Y, radius, epsilon, multiThread); + } + const float blend = detail; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float &r = rgb->r(y, x); + float &g = rgb->g(y, x); + float &b = rgb->b(y, x); + float t = Y[y][x]; + float t2; + + if (t > noise && (t2 = norm(r, g, b)) > noise) { + float c = apply(t, false); + float f = c / t; + // float t2 = norm(r, g, b); + float f2 = apply(t2) / t2; + f = intp(blend, f, f2); + assert(std::isfinite(f)); + r *= f; + g *= f; + b *= f; + assert(std::isfinite(r)); + assert(std::isfinite(g)); + assert(std::isfinite(b)); + } + } + } + } +} + +void ImProcFunctions::getAutoLogloc(int sp, ImageSource *imgsrc, float *sourceg, float *blackev, float *whiteev, bool *Autogr, int fw, int fh, float xsta, float xend, float ysta, float yend, int SCALE) +{ + BENCHFUN +//adpatation to local adjustments Jacques Desmis 12 2019 + const PreviewProps pp(0, 0, fw, fh, SCALE); + + Imagefloat img(int(fw / SCALE + 0.5), int(fh / SCALE + 0.5)); + const ProcParams neutral; + imgsrc->getImage(imgsrc->getWB(), TR_NONE, &img, pp, params->toneCurve, neutral.raw); + imgsrc->convertColorSpace(&img, params->icm, imgsrc->getWB()); + float minVal = RT_INFINITY; + float maxVal = -RT_INFINITY; + const float ec = std::pow(2.f, params->toneCurve.expcomp); + + constexpr float noise = 1e-5; + const int h = fh / SCALE; + const int w = fw / SCALE; + + const int hsta = ysta * h; + const int hend = yend * h; + + const int wsta = xsta * w; + const int wend = xend * w; + + double mean = 0.0; + int nc = 0; + for (int y = hsta; y < hend; ++y) { + for (int x = wsta; x < wend; ++x) { + const float r = img.r(y, x), g = img.g(y, x), b = img.b(y, x); + mean += static_cast(0.2126f * Color::gamma_srgb(r) + 0.7152f * Color::gamma_srgb(g) + 0.0722f * Color::gamma_srgb(b)); + nc++; + + const float m = rtengine::max(0.f, r, g, b) / 65535.f * ec; + if (m > noise) { + const float l = rtengine::min(r, g, b) / 65535.f * ec; + minVal = rtengine::min(minVal, l > noise ? l : m); + maxVal = rtengine::max(maxVal, m); + } + } + } + + //approximation sourcegray yb source = 0.4 * yb + + if (maxVal > minVal) { + const float log2 = std::log(2.f); + const float dynamic_range = -xlogf(minVal / maxVal) / log2; + + if (settings->verbose) { + std::cout << "AutoLog: min = " << minVal << ", max = " << maxVal + << ", DR = " << dynamic_range << std::endl; + } + + if (Autogr[sp]) { + double tot = 0.0; + int n = 0; + const float gmax = rtengine::min(maxVal / 2.f, 0.25f); + const float gmin = rtengine::max(minVal * std::pow(2.f, rtengine::max((dynamic_range - 1.f) / 2.f, 1.f)), 0.05f); + + if (settings->verbose) { + std::cout << " gray boundaries: " << gmin << ", " << gmax << std::endl; + } + + for (int y = ysta; y < yend; ++y) { + for (int x = wsta; x < wend; ++x) { + const float l = img.g(y, x) / 65535.f; + + if (l >= gmin && l <= gmax) { + tot += static_cast(l); + ++n; + } + } + } + + if (n > 0) { + sourceg[sp] = tot / n * 100.0; + + if (settings->verbose) { + std::cout << " computed gray point from " << n << " samples: " << sourceg[sp] << std::endl; + } + } else { + mean /= (nc * 65535.0); + float yb; + + if (mean < 0.15) { + yb = 3.0f; + } else if (mean < 0.3) { + yb = 5.0f; + } else if (mean < 0.4) { + yb = 10.0f; + } else if (mean < 0.45) { + yb = 15.0f; + } else if (mean < 0.5) { + yb = 18.0f; + } else if (mean < 0.55) { + yb = 23.0f; + } else if (mean < 0.6) { + yb = 30.0f; + } else { + yb = 45.f; + } + sourceg[sp] = 0.4f * yb; + if (settings->verbose) { + std::cout << " no samples found in range, resorting to Yb gray point value " << sourceg[sp] << std::endl; + } + } + } + + const float gray = sourceg[sp] / 100.f; + whiteev[sp] = xlogf(maxVal / gray) / log2; + blackev[sp] = whiteev[sp] - dynamic_range; + } +} + +void tone_eq(array2D &R, array2D &G, array2D &B, const struct local_params & lp, const Glib::ustring &workingProfile, double scale, bool multithread) +// adapted from the tone equalizer of darktable +/* + Copyright 2019 Alberto Griggio + Small adaptation to Local Adjustment 10 2019 Jacques Desmis + This file is part of darktable, + copyright (c) 2018 Aurelien Pierre. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ + +{ + BENCHFUN + + const int W = R.width(); + const int H = R.height(); + array2D Y(W, H); + + const auto log2 = + [](float x) -> float { + static const float l2 = xlogf(2); + return xlogf(x) / l2; + }; + + const auto exp2 = + [](float x) -> float { + return pow_F(2.f, x); + }; + // Build the luma channels: band-pass filters with gaussian windows of + // std 2 EV, spaced by 2 EV + const float centers[12] = { + -18.0f, -16.0f, -14.0f, -12.0f, -10.0f, -8.0f, -6.0f, + -4.0f, -2.0f, 0.0f, 2.0f, 4.0f + }; + + const auto conv = [&](int v, float lo, float hi) -> float { + const float f = v < 0 ? lo : hi; + return exp2(float(v) / 100.f * f); + }; + const float factors[12] = { + conv(lp.mullocsh[0], 2.f, 3.f), // -18 EV + conv(lp.mullocsh[0], 2.f, 3.f), // -16 EV + conv(lp.mullocsh[0], 2.f, 3.f), // -14 EV + conv(lp.mullocsh[0], 2.f, 3.f), // -12 EV + conv(lp.mullocsh[0], 2.f, 3.f), // -10 EV + conv(lp.mullocsh[0], 2.f, 3.f), // -8 EV + conv(lp.mullocsh[1], 2.f, 3.f), // -6 EV + conv(lp.mullocsh[2], 2.5f, 2.5f), // -4 EV + conv(lp.mullocsh[3], 3.f, 2.f), // -2 EV + conv(lp.mullocsh[4], 3.f, 2.f), // 0 EV + conv(lp.mullocsh[4], 3.f, 2.f), // 2 EV + conv(lp.mullocsh[4], 3.f, 2.f) // 4 EV + }; + + TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(workingProfile); + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + Y[y][x] = Color::rgbLuminance(R[y][x], G[y][x], B[y][x], ws); + } + } + + int detail = LIM(lp.detailsh + 5, 0, 5); + int radius = detail / scale + 0.5; + float epsilon2 = 0.01f + 0.002f * rtengine::max(detail - 3, 0); + + if (radius > 0) { + rtengine::guidedFilterLog(10.f, Y, radius, epsilon2, multithread); + } + + if (lp.detailsh > 0) { + array2D Y2(W, H); + constexpr float base_epsilon = 0.02f; + constexpr float base_posterization = 5.f; + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float l = LIM(log2(rtengine::max(Y[y][x], 1e-9f)), centers[0], centers[11]); + float ll = round(l * base_posterization) / base_posterization; + Y2[y][x] = Y[y][x]; + Y[y][x] = exp2(ll); + } + } + + radius = 350.0 / scale; + epsilon2 = base_epsilon / float(6 - rtengine::min(lp.detailsh, 5)); + rtengine::guidedFilter(Y2, Y, Y, radius, epsilon2, multithread); + } + + const auto gauss = + [](float b, float x) -> float { + return xexpf((-SQR(x - b) / 4.0f)); + }; + + // For every pixel luminance, the sum of the gaussian masks + float w_sum = 0.f; + + for (int i = 0; i < 12; ++i) { + w_sum += gauss(centers[i], 0.f); + } + + const auto process_pixel = + [&](float y) -> float { + // convert to log space + const float luma = rtengine::max(log2(rtengine::max(y, 0.f)), -18.0f); + + // build the correction as the sum of the contribution of each + // luminance channel to current pixel + float correction = 0.0f; + + for (int c = 0; c < 12; ++c) + { + correction += gauss(centers[c], luma) * factors[c]; + } + + correction /= w_sum; + + return correction; + }; + + LUTf lut(65536); + + for (int i = 0; i < 65536; ++i) { + float y = float(i) / 65535.f; + float c = process_pixel(y); + lut[i] = c; + } + + +#ifdef __SSE2__ + vfloat vfactors[12]; + vfloat vcenters[12]; + + for (int i = 0; i < 12; ++i) { + vfactors[i] = F2V(factors[i]); + vcenters[i] = F2V(centers[i]); + } + + const auto vgauss = + [](vfloat b, vfloat x) -> vfloat { + static const vfloat fourv = F2V(4.f); + return xexpf((-SQR(x - b) / fourv)); + }; + + vfloat zerov = F2V(0.f); + vfloat vw_sum = F2V(w_sum); + + const vfloat noisev = F2V(-18.f); + const vfloat xlog2v = F2V(xlogf(2.f)); + + const auto vprocess_pixel = + [&](vfloat y) -> vfloat { + const vfloat luma = vmaxf(xlogf(vmaxf(y, zerov)) / xlog2v, noisev); + + vfloat correction = zerov; + + for (int c = 0; c < 12; ++c) + { + correction += vgauss(vcenters[c], luma) * vfactors[c]; + } + + correction /= vw_sum; + + return correction; + }; + + + vfloat v1 = F2V(1.f); + vfloat v65535 = F2V(65535.f); +#endif // __SSE2__ + + +#ifdef _OPENMP + #pragma omp parallel for if (multithread) +#endif + for (int y = 0; y < H; ++y) { + int x = 0; + + +#ifdef __SSE2__ + + for (; x < W - 3; x += 4) { + vfloat cY = LVFU(Y[y][x]); + vmask m = vmaskf_gt(cY, v1); + vfloat corr; + + if (_mm_movemask_ps((vfloat)m)) { + corr = vprocess_pixel(cY); + } else { + corr = lut[cY * v65535]; + } + + STVF(R[y][x], LVF(R[y][x]) * corr); + STVF(G[y][x], LVF(G[y][x]) * corr); + STVF(B[y][x], LVF(B[y][x]) * corr); + } + +#endif // __SSE2__ + + for (; x < W; ++x) { + float cY = Y[y][x]; + float corr = cY > 1.f ? process_pixel(cY) : lut[cY * 65535.f]; + R[y][x] *= corr; + G[y][x] *= corr; + B[y][x] *= corr; + } + } + +} + + +void ImProcFunctions::ciecamloc_02float(int sp, LabImage* lab) +{ + //be careful quasi duplicate with branch cat02wb + BENCHFUN + + int width = lab->W, height = lab->H; + float Yw; + Yw = 1.0f; + double Xw, Zw; + float f = 0.f, nc = 0.f, la, c = 0.f, xw, yw, zw, f2 = 1.f, c2 = 1.f, nc2 = 1.f, yb2; + float fl, n, nbb, ncb, aw; //d + float xwd, ywd, zwd, xws, yws, zws; + // int alg = 0; + double Xwout, Zwout; + double Xwsc, Zwsc; + + int tempo; + + if (params->locallab.spots.at(sp).warm > 0) { + tempo = 5000 - 30 * params->locallab.spots.at(sp).warm; + } else { + tempo = 5000 - 49 * params->locallab.spots.at(sp).warm; + } + + ColorTemp::temp2mulxyz(params->wb.temperature, params->wb.method, Xw, Zw); //compute white Xw Yw Zw : white current WB + ColorTemp::temp2mulxyz(tempo, "Custom", Xwout, Zwout); + ColorTemp::temp2mulxyz(5000, "Custom", Xwsc, Zwsc); + + //viewing condition for surrsrc + f = 1.00f; + c = 0.69f; + nc = 1.00f; + //viewing condition for surround + f2 = 1.0f, c2 = 0.69f, nc2 = 1.0f; + //with which algorithm + // alg = 0; + + + xwd = 100.0 * Xwout; + zwd = 100.0 * Zwout; + ywd = 100.f; + + xws = 100.0 * Xwsc; + zws = 100.0 * Zwsc; + yws = 100.f; + + + yb2 = 18; + //La and la2 = ambiant luminosity scene and viewing + la = 400.f; + const float la2 = 400.f; + const float pilot = 2.f; + const float pilotout = 2.f; + + //algoritm's params + // const float rstprotection = 100. ;//- params->colorappearance.rstprotection; + LUTu hist16J; + LUTu hist16Q; + float yb = 18.f; + float d, dj; + + // const int gamu = 0; //(params->colorappearance.gamut) ? 1 : 0; + xw = 100.0 * Xw; + yw = 100.f * Yw; + zw = 100.0 * Zw; + float xw1 = xws, yw1 = yws, zw1 = zws, xw2 = xwd, yw2 = ywd, zw2 = zwd; + + float cz, wh, pfl; + Ciecam02::initcam1float(yb, pilot, f, la, xw, yw, zw, n, d, nbb, ncb, cz, aw, wh, pfl, fl, c); +// const float chr = 0.f; + const float pow1 = pow_F(1.64f - pow_F(0.29f, n), 0.73f); + float nj, nbbj, ncbj, czj, awj, flj; + Ciecam02::initcam2float(yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); +#ifdef __SSE2__ + const float reccmcz = 1.f / (c2 * czj); +#endif + const float pow1n = pow_F(1.64f - pow_F(0.29f, nj), 0.73f); + +#ifdef __SSE2__ + int bufferLength = ((width + 3) / 4) * 4; // bufferLength has to be a multiple of 4 +#endif +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + // one line buffer per channel and thread + float Jbuffer[bufferLength] ALIGNED16; + float Cbuffer[bufferLength] ALIGNED16; + float hbuffer[bufferLength] ALIGNED16; + float Qbuffer[bufferLength] ALIGNED16; + float Mbuffer[bufferLength] ALIGNED16; + float sbuffer[bufferLength] ALIGNED16; +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + for (int i = 0; i < height; i++) { +#ifdef __SSE2__ + // vectorized conversion from Lab to jchqms + int k; + vfloat x, y, z; + vfloat J, C, h, Q, M, s; + + vfloat c655d35 = F2V(655.35f); + + for (k = 0; k < width - 3; k += 4) { + Color::Lab2XYZ(LVFU(lab->L[i][k]), LVFU(lab->a[i][k]), LVFU(lab->b[i][k]), x, y, z); + x = x / c655d35; + y = y / c655d35; + z = z / c655d35; + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, F2V(aw), F2V(fl), F2V(wh), + x, y, z, + F2V(xw1), F2V(yw1), F2V(zw1), + F2V(c), F2V(nc), F2V(pow1), F2V(nbb), F2V(ncb), F2V(pfl), F2V(cz), F2V(d)); + STVF(Jbuffer[k], J); + STVF(Cbuffer[k], C); + STVF(hbuffer[k], h); + STVF(Qbuffer[k], Q); + STVF(Mbuffer[k], M); + STVF(sbuffer[k], s); + } + + for (; k < width; k++) { + float L = lab->L[i][k]; + float a = lab->a[i][k]; + float b = lab->b[i][k]; + float x, y, z; + //convert Lab => XYZ + Color::Lab2XYZ(L, a, b, x, y, z); + x = x / 655.35f; + y = y / 655.35f; + z = z / 655.35f; + float J, C, h, Q, M, s; + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, aw, fl, wh, + x, y, z, + xw1, yw1, zw1, + c, nc, pow1, nbb, ncb, pfl, cz, d); + Jbuffer[k] = J; + Cbuffer[k] = C; + hbuffer[k] = h; + Qbuffer[k] = Q; + Mbuffer[k] = M; + sbuffer[k] = s; + } + +#endif // __SSE2__ + + for (int j = 0; j < width; j++) { + float J, C, h, Q, M, s; + +#ifdef __SSE2__ + // use precomputed values from above + J = Jbuffer[j]; + C = Cbuffer[j]; + h = hbuffer[j]; + Q = Qbuffer[j]; + M = Mbuffer[j]; + s = sbuffer[j]; +#else + float x, y, z; + float L = lab->L[i][j]; + float a = lab->a[i][j]; + float b = lab->b[i][j]; + float x1, y1, z1; + //convert Lab => XYZ + Color::Lab2XYZ(L, a, b, x1, y1, z1); + x = x1 / 655.35f; + y = y1 / 655.35f; + z = z1 / 655.35f; + //process source==> normal + Ciecam02::xyz2jchqms_ciecam02float(J, C, h, + Q, M, s, aw, fl, wh, + x, y, z, + xw1, yw1, zw1, + c, nc, pow1, nbb, ncb, pfl, cz, d); +#endif + float Jpro, Cpro, hpro, Qpro, Mpro, spro; + Jpro = J; + Cpro = C; + hpro = h; + Qpro = Q; + Mpro = M; + spro = s; + /* + */ + + + //retrieve values C,J...s + C = Cpro; + J = Jpro; + Q = Qpro; + M = Mpro; + h = hpro; + s = spro; + +#ifdef __SSE2__ + // write to line buffers + Jbuffer[j] = J; + Cbuffer[j] = C; + hbuffer[j] = h; +#else + float xx, yy, zz; + //process normal==> viewing + + Ciecam02::jch2xyz_ciecam02float(xx, yy, zz, + J, C, h, + xw2, yw2, zw2, + c2, nc2, pow1n, nbbj, ncbj, flj, czj, dj, awj); + x = xx * 655.35f; + y = yy * 655.35f; + z = zz * 655.35f; + float Ll, aa, bb; + //convert xyz=>lab + Color::XYZ2Lab(x, y, z, Ll, aa, bb); + lab->L[i][j] = Ll; + lab->a[i][j] = aa; + lab->b[i][j] = bb; +#endif + } + +#ifdef __SSE2__ + // process line buffers + float *xbuffer = Qbuffer; + float *ybuffer = Mbuffer; + float *zbuffer = sbuffer; + + for (k = 0; k < bufferLength; k += 4) { + Ciecam02::jch2xyz_ciecam02float(x, y, z, + LVF(Jbuffer[k]), LVF(Cbuffer[k]), LVF(hbuffer[k]), + F2V(xw2), F2V(yw2), F2V(zw2), + F2V(nc2), F2V(pow1n), F2V(nbbj), F2V(ncbj), F2V(flj), F2V(dj), F2V(awj), F2V(reccmcz)); + STVF(xbuffer[k], x * c655d35); + STVF(ybuffer[k], y * c655d35); + STVF(zbuffer[k], z * c655d35); + } + + // XYZ2Lab uses a lookup table. The function behind that lut is a cube root. + // SSE can't beat the speed of that lut, so it doesn't make sense to use SSE + for (int j = 0; j < width; j++) { + float Ll, aa, bb; + //convert xyz=>lab + Color::XYZ2Lab(xbuffer[j], ybuffer[j], zbuffer[j], Ll, aa, bb); + + lab->L[i][j] = Ll; + lab->a[i][j] = aa; + lab->b[i][j] = bb; + } + +#endif + } + + } +} + +void ImProcFunctions::softproc(const LabImage* bufcolorig, const LabImage* bufcolfin, float rad, int bfh, int bfw, float epsilmax, float epsilmin, float thres, int sk, bool multiThread, int flag) +{ + if (rad > 0.f) { + array2D ble(bfw, bfh); + array2D guid(bfw, bfh); + if (flag == 0) { + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + guid[ir][jr] = Color::L2Y(bufcolorig->L[ir][jr]) / 32768.f; + ble[ir][jr] = Color::L2Y(bufcolfin->L[ir][jr]) / 32768.f; + } + } + + const float aepsil = (epsilmax - epsilmin) / 90.f; + const float bepsil = epsilmax - 100.f * aepsil; + const float epsil = aepsil * 0.1f * rad + bepsil; + const float blur = 10.f / sk * (thres + 0.8f * rad); + + rtengine::guidedFilter(guid, ble, ble, blur, epsil, multiThread, 4); + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufcolfin->L[ir][jr] = Color::computeXYZ2LabY(32768.f * ble[ir][jr]); + } + } + } else if (flag == 1) { + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + ble[ir][jr] = bufcolfin->L[ir][jr] / 32768.f; + guid[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; + } + + const float aepsil = (epsilmax - epsilmin) / 90.f; + const float bepsil = epsilmax - 100.f * aepsil; + const float epsil = rad < 0.f ? 0.0001f : aepsil * 0.1f * rad + bepsil; + const float blur = rad < 0.f ? -1.f / rad : 1.f + rad; + const int r2 = rtengine::max(int(25 / sk * blur + 0.5f), 1); + + rtengine::guidedFilter(guid, ble, ble, r2, epsil, multiThread); + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufcolfin->L[ir][jr] = 32768.f * ble[ir][jr]; + } + } + } + } +} + +void ImProcFunctions::softprocess(const LabImage* bufcolorig, array2D &buflight, float rad, int bfh, int bfw, double epsilmax, double epsilmin, float thres, int sk, bool multiThread) +{ + float minlig = buflight[0][0]; + +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minlig) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + minlig = rtengine::min(buflight[ir][jr], minlig); + } + } + + array2D guidsoft(bfw, bfh); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + buflight[ir][jr] = LIM01((buflight[ir][jr] - minlig) / (100.f - minlig)); + guidsoft[ir][jr] = bufcolorig->L[ir][jr] / 32768.f; + } + } + + double aepsil = (epsilmax - epsilmin) / 90.f; + double bepsil = epsilmax - 100.f * aepsil; + double epsil = aepsil * rad + bepsil; + float blur = 1.f / sk * (thres + 0.8f * rad); + guidedFilter(guidsoft, buflight, buflight, blur, epsil, multiThread, 4); + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + buflight[ir][jr] = (100.f - minlig) * buflight[ir][jr] + minlig; + } + } +} + +void ImProcFunctions::exlabLocal(local_params& lp, int bfh, int bfw, LabImage* bufexporig, LabImage* lab, const LUTf& hltonecurve, const LUTf& shtonecurve, const LUTf& tonecurve) +{ + BENCHFUN + //exposure local + + constexpr float maxran = 65536.f; + const float cexp_scale = std::pow(2.f, lp.expcomp); + const float ccomp = (rtengine::max(0.f, lp.expcomp) + 1.f) * lp.hlcomp / 100.f; + const float cshoulder = ((maxran / rtengine::max(1.0f, cexp_scale)) * (lp.hlcompthr / 200.f)) + 0.1f; + const float chlrange = maxran - cshoulder; + const float linear = lp.linear; + constexpr float kl = 1.f; + const float hlcompthr = lp.hlcompthr / 200.f; + const float hlcomp = lp.hlcomp / 100.f; + if (lp.linear > 0.f && lp.expcomp == 0.f) { + lp.expcomp = 0.01f; + } + + if (lp.expmet == 1 && lp.linear > 0.f && lp.laplacexp > 0.1f && !lp.invex) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float L = bufexporig->L[ir][jr]; + const float Llin = LIM01(L / 32768.f); + const float addcomp = linear * (-kl * Llin + kl);//maximum about 1. IL + const float exp_scale = pow_F(2.0, (lp.expcomp + addcomp)); + const float shoulder = ((maxran / rtengine::max(1.0f, exp_scale)) * hlcompthr) + 0.1f; + const float comp = (rtengine::max(0.f, (lp.expcomp + addcomp)) + 1.f) * hlcomp; + const float hlrange = maxran - shoulder; + + //highlight + const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(exp_scale, comp, hlrange, 2 * L)); + L *= hlfactor * pow_F(2.f, addcomp);//approximation but pretty good with Laplacian and L < mean, hl aren't call + //shadow tone curve + L *= shtonecurve[2 * L]; + //tonecurve + lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; + } + } + } else { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float L = bufexporig->L[ir][jr]; + //highlight + const float hlfactor = (2 * L < MAXVALF ? hltonecurve[2 * L] : CurveFactory::hlcurve(cexp_scale, ccomp, chlrange, 2 * L)); + L *= hlfactor;//approximation but pretty good with Laplacian and L < mean, hl aren't call + //shadow tone curve + L *= shtonecurve[2 * L]; + //tonecurve + lab->L[ir][jr] = 0.5f * tonecurve[2 * L]; + } + } + } +} + + +void ImProcFunctions::addGaNoise(LabImage *lab, LabImage *dst, const float mean, const float variance, const int sk) +{ +// BENCHFUN +//Box-Muller method. +// add luma noise to image + + srand(1); + + const float variaFactor = SQR(variance) / sk; + constexpr float randFactor1 = 1.f / RAND_MAX; + constexpr float randFactor2 = (2.f * rtengine::RT_PI_F) / RAND_MAX; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + float z0, z1; + bool generate = false; +#ifdef _OPENMP + #pragma omp for schedule(static) // static scheduling is important to avoid artefacts +#endif + for (int y = 0; y < lab->H; y++) { + for (int x = 0; x < lab->W; x++) { + generate = !generate; + float kvar = 1.f; + + if (lab->L[y][x] < 12000.f) { + constexpr float ah = -0.5f / 12000.f; + constexpr float bh = 1.5f; + kvar = ah * lab->L[y][x] + bh; //increase effect for low lights < 12000.f + } else if (lab->L[y][x] > 20000.f) { + constexpr float ah = -0.5f / 12768.f; + constexpr float bh = 1.f - 20000.f * ah; + kvar = ah * lab->L[y][x] + bh; //decrease effect for high lights > 20000.f + kvar = kvar < 0.5f ? 0.5f : kvar; + } + + float varia = SQR(kvar) * variaFactor; + + if (!generate) { + dst->L[y][x] = LIM(lab->L[y][x] + mean + varia * z1, 0.f, 32768.f); + continue; + } + + int u1 = 0; + int u2; + + while (u1 == 0) { + u1 = rand(); + u2 = rand(); + } + + float u1f = u1 * randFactor1; + float u2f = u2 * randFactor2; + + float2 sincosval = xsincosf(2.f * rtengine::RT_PI_F * u2f); + float factor = std::sqrt(-2.f * xlogf(u1f)); + z0 = factor * sincosval.y; + z1 = factor * sincosval.x; + + dst->L[y][x] = LIM(lab->L[y][x] + mean + varia * z0, 0.f, 32768.f); + + } + } + } +} + +void ImProcFunctions::DeNoise_Local(int call, const struct local_params& lp, LabImage* originalmask, int levred, float hueref, float lumaref, float chromaref, LabImage* original, LabImage* transformed, const LabImage &tmp1, int cx, int cy, int sk) +{ + //warning, but I hope used it next + // local denoise and impulse + //simple algo , perhaps we can improve as the others, but noise is here and not good for hue detection + // BENCHFUN + lumaref *= 327.68f; + const float ach = lp.trans / 100.f; + + const float factnoise1 = 1.f + (lp.noisecf) / 500.f; + const float factnoise2 = 1.f + (lp.noisecc) / 500.f; + const float factnoise = factnoise1 * factnoise2; + + const int GW = transformed->W; + const int GH = transformed->H; + + const float colorde = lp.colorde == 0 ? -1.f : lp.colorde; // -1.f to avoid black + const float amplabL = 2.f * colorde; + constexpr float darklim = 5000.f; + + const float refa = chromaref * std::cos(hueref) * 327.68f; + const float refb = chromaref * std::sin(hueref) * 327.68f; + const bool usemaskbl = lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4; + const bool blshow = lp.showmaskblmet == 1 || lp.showmaskblmet == 2; + const bool previewbl = lp.showmaskblmet == 4; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + const float radius = 3.f / sk; + + if (usemaskbl) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblur->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblur->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblur->b, GW, GH, radius); + } + } else { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + } + + const int begx = lp.xc - lp.lxL; + const int begy = lp.yc - lp.lyT; + constexpr float r327d68 = 1.f / 327.68f; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage* maskptr = origblur.get(); + const float mindE = 2.f + MINSCOPE * lp.sensden * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensden * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + for (int x = 0, lox = cx + x; x < transformed->W; x++, lox++) { + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float reducdEL = 1.f; + float reducdEa = 1.f; + float reducdEb = 1.f; + + if (levred == 7) { + const float dEL = std::sqrt(0.9f * SQR(refa - maskptr->a[y][x]) + 0.9f * SQR(refb - maskptr->b[y][x]) + 1.2f * SQR(lumaref - maskptr->L[y][x])) * r327d68; + const float dEa = std::sqrt(1.2f * SQR(refa - maskptr->a[y][x]) + 1.f * SQR(refb - maskptr->b[y][x]) + 0.8f * SQR(lumaref - maskptr->L[y][x])) * r327d68; + const float dEb = std::sqrt(1.f * SQR(refa - maskptr->a[y][x]) + 1.2f * SQR(refb - maskptr->b[y][x]) + 0.8f * SQR(lumaref - maskptr->L[y][x])) * r327d68; + reducdEL = SQR(calcreducdE(dEL, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); + reducdEa = SQR(calcreducdE(dEa, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); + reducdEb = SQR(calcreducdE(dEb, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensden)); + } + float difL, difa, difb; + + if (call == 2 /*|| call == 1 || call == 3 */) { //simpleprocess + difL = tmp1.L[loy - begy][lox - begx] - original->L[y][x]; + difa = tmp1.a[loy - begy][lox - begx] - original->a[y][x]; + difb = tmp1.b[loy - begy][lox - begx] - original->b[y][x]; + } else { //dcrop + difL = tmp1.L[y][x] - original->L[y][x]; + difa = tmp1.a[y][x] - original->a[y][x]; + difb = tmp1.b[y][x] - original->b[y][x]; + } + + difL *= localFactor * reducdEL; + difa *= localFactor * reducdEa; + difb *= localFactor * reducdEb; + transformed->L[y][x] = CLIP(original->L[y][x] + difL); + transformed->a[y][x] = clipC((original->a[y][x] + difa) * factnoise); + transformed->b[y][x] = clipC((original->b[y][x] + difb) * factnoise) ; + + if (blshow) { + transformed->L[y][x] = CLIP(12000.f + amplabL * difL);// * 10.f empirical to can visualize modifications + transformed->a[y][x] = clipC(amplabL * difa);// * 10.f empirical to can visualize modifications + transformed->b[y][x] = clipC(amplabL * difb);// * 10.f empirical to can visualize modifications + } else if (previewbl || lp.prevdE) { + const float difbdisp = (reducdEL + reducdEa + reducdEb) * 10000.f * colorde; + + if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! + transformed->L[y][x] = darklim - transformed->L[y][x]; + } + + if (colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + } + } + } +} + +void ImProcFunctions::InverseReti_Local(const struct local_params & lp, const float hueref, const float chromaref, const float lumaref, LabImage * original, LabImage * transformed, const LabImage * const tmp1, int cx, int cy, int chro, int sk) +{ + // BENCHFUN +//inverse local retinex + float ach = lp.trans / 100.f; + int GW = transformed->W; + int GH = transformed->H; + float refa = chromaref * cos(hueref); + float refb = chromaref * sin(hueref); + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + float radius = 3.f / sk; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + + } +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const float mindE = 2.f + MINSCOPE * lp.sensh * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + + int zone; + float localFactor; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + float rL = origblur->L[y][x] / 327.68f; + float dE = std::sqrt(kab * SQR(refa - origblur->a[y][x] / 327.68f) + kab * SQR(refb - origblur->b[y][x] / 327.68f) + kL * SQR(lumaref - rL)); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensh); + + switch (zone) { + case 0: { // outside selection and outside transition zone => full effect, no transition + if (chro == 0) { + float difL = tmp1->L[y][x] - original->L[y][x]; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + } + + if (chro == 1) { + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + + transformed->a[y][x] = clipC(original->a[y][x] + difa * reducdE); + transformed->b[y][x] = clipC(original->b[y][x] + difb * reducdE); + } + break; + } + + case 1: { // inside transition zone + float factorx = 1.f - localFactor; + + if (chro == 0) { + float difL = tmp1->L[y][x] - original->L[y][x]; + difL *= factorx; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + } + + if (chro == 1) { + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + + difa *= factorx; + difb *= factorx; + + transformed->a[y][x] = clipC(original->a[y][x] + difa * reducdE); + transformed->b[y][x] = clipC(original->b[y][x] + difb * reducdE); + } + break; + } + + case 2: { // inside selection => no effect, keep original values + if (chro == 0) { + transformed->L[y][x] = original->L[y][x]; + } + + if (chro == 1) { + transformed->a[y][x] = original->a[y][x]; + transformed->b[y][x] = original->b[y][x]; + } + } + } + } + } + } +} + + + + +void ImProcFunctions::InverseBlurNoise_Local(LabImage * originalmask, float **bufchro, const struct local_params & lp, const float hueref, const float chromaref, const float lumaref, LabImage * original, LabImage * transformed, const LabImage * const tmp1, int cx, int cy, int sk) +{ + // BENCHFUN +//inverse local blur and noise + float ach = lp.trans / 100.f; + int GW = transformed->W; + int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + + const bool blshow = (lp.showmaskblmet == 1 || lp.showmaskblmet == 2); + const bool previewbl = (lp.showmaskblmet == 4); + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + std::unique_ptr origblurmask; + const bool usemaskbl = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4); + const bool usemaskall = usemaskbl; + + float radius = 3.f / sk; + + if (usemaskall) { + origblurmask.reset(new LabImage(GW, GH)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); + } + } + + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + + } +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 2.f + MINSCOPE * lp.sensbn * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensbn * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + + int zone; + float localFactor; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + const float clc = (previewbl) ? settings->previewselection * 100.f : bufchro[y][x]; + float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); + float huedelta2 = abdelta2 - chrodelta2; + + float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensbn); + const float realstrchdE = reducdE * clc; + + switch (zone) { + + case 0: { // outside selection and outside transition zone => full effect, no transition + float difL = tmp1->L[y][x] - original->L[y][x]; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + float flia = 1.f, flib = 1.f; + flia = flib = ((100.f + realstrchdE) / 100.f); + const float chra = tmp1->a[y][x]; + const float chrb = tmp1->b[y][x]; + + if (!lp.actsp) { + difa = chra * flia - original->a[y][x]; + difb = chrb * flib - original->b[y][x]; + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + + if (blshow) { + transformed->L[y][x] = CLIP(12000.f + difL); + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } else if (previewbl || lp.prevdE) { + transformed->a[y][x] = 0.f; + transformed->b[y] [x] = (difb); + } + + break; + } + + case 1: { // inside transition zone + float difL = tmp1->L[y][x] - original->L[y][x]; + float difa = tmp1->a[y][x] - original->a[y][x]; + float difb = tmp1->b[y][x] - original->b[y][x]; + float flia = 1.f, flib = 1.f; + flia = flib = ((100.f + realstrchdE) / 100.f); + const float chra = tmp1->a[y][x]; + const float chrb = tmp1->b[y][x]; + + float factorx = 1.f - localFactor; + difL *= factorx; + + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (!lp.actsp) { + difa = chra * flia - original->a[y][x]; + difb = chrb * flib - original->b[y][x]; + difa *= factorx; + difb *= factorx; + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + + } + + if (blshow) { + transformed->L[y][x] = CLIP(12000.f + difL); + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } else if (previewbl) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = (difb); + } + + break; + } + + case 2: { // inside selection => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + + if (!lp.actsp) { + + transformed->a[y][x] = original->a[y][x]; + transformed->b[y][x] = original->b[y][x]; + } + } + } + } + } + } +} + +static void mean_fab(int xstart, int ystart, int bfw, int bfh, LabImage* bufexporig, const LabImage* original, float &fab, float &meanfab, float chrom, bool multiThread) +{ + const int nbfab = bfw * bfh; + + meanfab = 0.f; + fab = 50.f; + + if (nbfab > 0) { + double sumab = 0.0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:sumab) if(multiThread) +#else + static_cast(multiThread); +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->a[y][x] = original->a[y + ystart][x + xstart]; + bufexporig->b[y][x] = original->b[y + ystart][x + xstart]; + sumab += std::fabs(bufexporig->a[y][x]); + sumab += std::fabs(bufexporig->b[y][x]); + } + } + + meanfab = sumab / (2.f * nbfab); + + double som = 0.0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:som) if(multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + som += SQR(std::fabs(bufexporig->a[y][x]) - meanfab) + SQR(std::fabs(bufexporig->b[y][x]) - meanfab); + } + } + + const float multsigma = (chrom >= 0.f ? 0.035f : 0.018f) * chrom + 1.f; + + const float stddv = std::sqrt(som / nbfab); + fab = meanfab + multsigma * stddv; + + if (fab <= 0.f) { + fab = 50.f; + } + } +} + +struct grad_params { + bool angle_is_zero, transpose, bright_top; + float ta, yc, xc; + float ys, ys_inv; + float scale, botmul, topmul; + float top_edge_0; + int h; +}; + +void calclocalGradientParams(const struct local_params& lp, struct grad_params& gp, float ystart, float xstart, int bfw, int bfh, int indic) +{ + int w = bfw; + int h = bfh; + float stops = 0.f; + float angs = 0.f; + + if (indic == 0) { + stops = -lp.strmaexp; + angs = lp.angmaexp; + } else if (indic == 1) { + stops = lp.strexp; + angs = lp.angexp; + } else if (indic == 2) { + stops = lp.strSH; + angs = lp.angSH; + } else if (indic == 3) { + stops = lp.strcol; + angs = lp.angcol; + } else if (indic == 4) { + float redu = 1.f; + + if (lp.strcolab > 0.f) { + redu = 0.6f; + } else { + redu = 0.15f; + } + + stops = redu * lp.strcolab; + angs = lp.angcol; + } else if (indic == 5) { + stops = lp.strcolab; + angs = lp.angcol; + } else if (indic == 6) { + stops = lp.strcolh; + angs = lp.angcol; + } else if (indic == 7) { + stops = lp.strvib; + angs = lp.angvib; + } else if (indic == 8) { + float redu = 1.f; + + if (lp.strvibab > 0.f) { + redu = 0.7f; + } else { + redu = 0.5f; + } + + stops = redu * lp.strvibab; + angs = lp.angvib; + } else if (indic == 9) { + stops = lp.strvibh; + angs = lp.angvib; + } else if (indic == 10) { + stops = std::fabs(lp.strwav); + angs = lp.angwav; + } else if (indic == 11) { + stops = lp.strlog; + angs = lp.anglog; + } + + + double gradient_stops = stops; + double gradient_center_x = LIM01((lp.xc - xstart) / bfw); + double gradient_center_y = LIM01((lp.yc - ystart) / bfh); + double gradient_angle = angs / 180.0 * rtengine::RT_PI; + double varfeath = 0.01 * lp.feath; + + //printf("xstart=%f ysta=%f lpxc=%f lpyc=%f stop=%f bb=%f cc=%f ang=%f ff=%d gg=%d\n", xstart, ystart, lp.xc, lp.yc, gradient_stops, gradient_center_x, gradient_center_y, gradient_angle, w, h); + + // make 0.0 <= gradient_angle < 2 * rtengine::RT_PI + gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); + + if (gradient_angle < 0.0) { + gradient_angle += 2.0 * rtengine::RT_PI; + } + + gp.bright_top = false; + gp.transpose = false; + gp.angle_is_zero = false; + gp.h = h; + double cosgrad = cos(gradient_angle); + + if (std::fabs(cosgrad) < 0.707) { + // we transpose to avoid division by zero at 90 degrees + // (actually we could transpose only for 90 degrees, but this way we avoid + // division with extremely small numbers + gp.transpose = true; + gradient_angle += 0.5 * rtengine::RT_PI; + double gxc = gradient_center_x; + gradient_center_x = 1.0 - gradient_center_y; + gradient_center_y = gxc; + } + + gradient_angle = fmod(gradient_angle, 2 * rtengine::RT_PI); + + if (gradient_angle > 0.5 * rtengine::RT_PI && gradient_angle < rtengine::RT_PI) { + gradient_angle += rtengine::RT_PI; + gp.bright_top = true; + } else if (gradient_angle >= rtengine::RT_PI && gradient_angle < 1.5 * rtengine::RT_PI) { + gradient_angle -= rtengine::RT_PI; + gp.bright_top = true; + } + + if (std::fabs(gradient_angle) < 0.001 || std::fabs(gradient_angle - 2 * rtengine::RT_PI) < 0.001) { + gradient_angle = 0; + gp.angle_is_zero = true; + } + + if (gp.transpose) { + gp.bright_top = !gp.bright_top; + std::swap(w, h); + } + + gp.scale = 1.0 / pow(2, gradient_stops); + + if (gp.bright_top) { + gp.topmul = 1.0; + gp.botmul = gp.scale; + } else { + gp.topmul = gp.scale; + gp.botmul = 1.0; + } + + gp.ta = tan(gradient_angle); + gp.xc = w * gradient_center_x; + gp.yc = h * gradient_center_y; + gp.ys = std::sqrt((float)h * h + (float)w * w) * (varfeath / cos(gradient_angle)); + gp.ys_inv = 1.0 / gp.ys; + gp.top_edge_0 = gp.yc - gp.ys / 2.0; + + if (gp.ys < 1.0 / h) { + gp.ys_inv = 0; + gp.ys = 0; + } +} + +void ImProcFunctions::blendstruc(int bfw, int bfh, LabImage* bufcolorig, float radius, float stru, array2D & blend2, int sk, bool multiThread) +{ + SobelCannyLuma(blend2, bufcolorig->L, bfw, bfh, radius); + float rm = 20.f / sk; + + if (rm > 0) { + float **mb = blend2; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + array2D ble(bfw, bfh); + array2D guid(bfw, bfh); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float X, Y, Z; + float L = bufcolorig->L[ir][jr]; + float a = bufcolorig->a[ir][jr]; + float b = bufcolorig->b[ir][jr]; + Color::Lab2XYZ(L, a, b, X, Y, Z); + + guid[ir][jr] = Y / 32768.f; + + blend2[ir][jr] /= 32768.f; + } + } + + const float blur = 25 / sk * (10.f + 1.2f * stru); + + rtengine::guidedFilter(guid, blend2, ble, blur, 0.001, multiThread); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + blend2[ir][jr] = 32768.f * ble[ir][jr]; + } + } +} + + +static void blendmask(const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* original, LabImage* bufmaskor, LabImage* originalmas, float bl, int inv) +{ +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh ; y++) { + const int loy = y + ystart + cy; + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (inv == 0) { + if (zone > 0) { + bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); + bufexporig->a[y][x] *= (1.f + bl * bufmaskor->a[y][x]); + bufexporig->b[y][x] *= (1.f + bl * bufmaskor->b[y][x]); + + bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); + bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); + bufexporig->b[y][x] = clipC(bufexporig->b[y][x]); + + originalmas->L[y][x] = CLIP(bufexporig->L[y][x] - bufmaskor->L[y][x]); + originalmas->a[y][x] = clipC(bufexporig->a[y][x] * (1.f - bufmaskor->a[y][x])); + originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); + + original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->b[y][x]); + original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); + original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); + original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); + + } + } else if (inv == 1) { + localFactor = 1.f - localFactor; + + if (zone < 2) { + bufexporig->L[y][x] += (bl * bufmaskor->L[y][x]); + bufexporig->a[y][x] *= (1.f + bl * bufmaskor->a[y][x]); + bufexporig->b[y][x] *= (1.f + bl * bufmaskor->b[y][x]); + + bufexporig->L[y][x] = CLIP(bufexporig->L[y][x]); + bufexporig->a[y][x] = clipC(bufexporig->a[y][x]); + bufexporig->b[y][x] = clipC(bufexporig->b[y][x]); + + originalmas->L[y][x] = CLIP(bufexporig->L[y][x] - bufmaskor->L[y][x]); + originalmas->a[y][x] = clipC(bufexporig->a[y][x] * (1.f - bufmaskor->a[y][x])); + originalmas->b[y][x] = clipC(bufexporig->b[y][x] * (1.f - bufmaskor->b[y][x])); + + switch (zone) { + case 0: { + original->L[y + ystart][x + xstart] += (bl * bufmaskor->L[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + bl * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + bl * bufmaskor->b[y][x]); + original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); + original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); + original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); + break; + } + + case 1: { + original->L[y + ystart][x + xstart] += (bl * localFactor * bufmaskor->L[y][x]); + original->a[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->a[y][x]); + original->b[y + ystart][x + xstart] *= (1.f + bl * localFactor * bufmaskor->b[y][x]); + original->L[y + ystart][x + xstart] = CLIP(original->L[y + ystart][x + xstart]); + original->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart]); + original->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart]); + } + + } + } + + } + } + } +} + +void ImProcFunctions::deltaEforMask(float **rdE, int bfw, int bfh, LabImage* bufcolorig, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh) +{ + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + const float refL = lumaref; + + const float kL = balance; + const float kab = balancedeltaE(kL); + const float kH = balanceh; + const float kch = balancedeltaE(kH); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + const float abdelta2 = SQR(refa - bufcolorig->a[y][x] / 327.68f) + SQR(refb - bufcolorig->b[y][x] / 327.68f); + const float chrodelta2 = SQR(std::sqrt(SQR(bufcolorig->a[y][x]) + SQR(bufcolorig->b[y][x])) / 327.68f - chromaref); + const float huedelta2 = abdelta2 - chrodelta2; + const float tempdE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - bufcolorig->L[y][x] / 327.68f)); + + float reducdE; + if (tempdE > maxdE) { + reducdE = 0.f; + } else if (tempdE > mindE && tempdE <= maxdE) { + const float ar = 1.f / (mindE - maxdE); + const float br = - ar * maxdE; + reducdE = pow(ar * tempdE + br, iterat); + } else { + reducdE = 1.f; + } + + if (scope > limscope) { + if (tempdE > maxdElim) { + reducdE = 0.f; + } else if (tempdE > mindElim && tempdE <= maxdElim) { + const float arlim = 1.f / (mindElim - maxdElim); + const float brlim = - arlim * maxdElim; + const float reducdElim = pow(arlim * tempdE + brlim, iterat); + const float aalim = (1.f - reducdElim) / 20.f; + const float bblim = 1.f - 100.f * aalim; + reducdE = aalim * scope + bblim; + } else { + reducdE = 1.f; + } + } + + rdE[y][x] = reducdE ; + } + } +} + +static void showmask(int lumask, const local_params& lp, int xstart, int ystart, int cx, int cy, int bfw, int bfh, LabImage* bufexporig, LabImage* transformed, LabImage* bufmaskorigSH, int inv) +{ +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + const int loy = y + ystart + cy; + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (inv == 0) { + if (zone > 0) {//normal + transformed->L[y + ystart][x + xstart] = (lumask * 400.f) + clipLoc(bufmaskorigSH->L[y][x]); + transformed->a[y + ystart][x + xstart] = bufexporig->a[y][x] * bufmaskorigSH->a[y][x]; + transformed->b[y + ystart][x + xstart] = bufexporig->b[y][x] * bufmaskorigSH->b[y][x]; + } + } else if (inv == 1) { //inverse + if (zone == 0) { + transformed->L[y + ystart][x + xstart] = (lumask * 400.f) + clipLoc(bufmaskorigSH->L[y][x]); + transformed->a[y + ystart][x + xstart] = bufexporig->a[y][x] * bufmaskorigSH->a[y][x]; + transformed->b[y + ystart][x + xstart] = bufexporig->b[y][x] * bufmaskorigSH->b[y][x]; + } + } + } + } +} + +void ImProcFunctions::discrete_laplacian_threshold(float * data_out, const float * data_in, size_t nx, size_t ny, float t) +{ + BENCHFUN + + if (!data_in || !data_out) { + fprintf(stderr, "a pointer is NULL and should not be so\n"); + abort(); + } + + /* pointers to the data and neighbour values */ + /* + * y-1 + * x-1 ptr x+1 + * y+1 + * <---------------------nx-------> + */ + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (size_t j = 0; j < ny; j++) { + const float* ptr_in = &data_in[j * nx]; + float* ptr_out = &data_out[j * nx]; + for (size_t i = 0; i < nx; i++) { + float val = 0.f; + /* row differences */ + if (0 < i) { + const float diff = ptr_in[i] - ptr_in[i - 1]; + val += std::fabs(diff) > t ? diff : 0.f; + } + + if (nx - 1 > i) { + const float diff = ptr_in[i] - ptr_in[i + 1];; + val += std::fabs(diff) > t ? diff : 0.f; + } + + /* column differences */ + if (0 < j) { + const float diff = ptr_in[i] - ptr_in[i - nx];; + val += std::fabs(diff) > t ? diff : 0.f; + } + + if (ny - 1 > j) { + const float diff = ptr_in[i] - ptr_in[i + nx];; + val += std::fabs(diff) > t ? diff : 0.f; + } + + ptr_out[i] = val; + } + } + +} + +float *ImProcFunctions::cos_table(size_t size) +{ + float *table = NULL; + + /* allocate the cosinus table */ + if (NULL == (table = (float *) malloc(sizeof(*table) * size))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + /* + * fill the cosinus table, + * table[i] = cos(i Pi / n) for i in [0..n[ + */ + const double pi_size = rtengine::RT_PI / size; + + for (size_t i = 0; i < size; i++) { + table[i] = std::cos(pi_size * i); + } + + return table; +} + + +void ImProcFunctions::rex_poisson_dct(float * data, size_t nx, size_t ny, double m) +{ + /* + * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ + * + + * @file retinex_pde_lib.c discrete Poisson equation + * @brief laplacian, DFT and Poisson routines + * + * @author Nicolas Limare + * some adaptations for Rawtherapee + */ + BENCHFUN + + /* + * get the cosinus tables + * cosx[i] = cos(i Pi / nx) for i in [0..nx[ + * cosy[i] = cos(i Pi / ny) for i in [0..ny[ + */ + + float* cosx = cos_table(nx); + float* cosy = cos_table(ny); + + /* + * we will now multiply data[i, j] by + * m / (4 - 2 * cosx[i] - 2 * cosy[j])) + * and set data[0, 0] to 0 + */ + float m2 = m / 2.; + /* + * after that, by construction, we always have + * cosx[] + cosy[] != 2. + */ + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (size_t i = 0; i < ny; ++i) { + for (size_t j = 0; j < nx; ++j) { + data[i * nx + j] *= m2 / (2.f - cosx[j] - cosy[i]); + } + } + // handle the first value, data[0, 0] = 0 + data[0] = 0.f; + + free(cosx); + free(cosy); + +} + +void ImProcFunctions::mean_dt(const float* data, size_t size, double& mean_p, double& dt_p) +{ + + double mean = 0.; + double dt = 0.; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:mean,dt) if(multiThread) +#endif + for (size_t i = 0; i < size; i++) { + mean += data[i]; + dt += SQR(data[i]); + } + + mean /= size; + dt /= size; + dt -= SQR(mean); + mean_p = mean; + dt_p = std::sqrt(dt); +} + +void ImProcFunctions::normalize_mean_dt(float * data, const float * ref, size_t size, float mod, float sigm) +{ + /* + * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ + * + + * @file retinex_pde_lib.c discrete Poisson equation + * @brief laplacian, DFT and Poisson routines + * + * @author Nicolas Limare + * adapted for Rawtherapee - jacques Desmis july 2019 + */ + + if (NULL == data || NULL == ref) { + fprintf(stderr, "a pointer is NULL and should not be so\n"); + abort(); + } + + double mean_ref, mean_data, dt_ref, dt_data; + + /* compute mean and variance of the two arrays */ + mean_dt(ref, size, mean_ref, dt_ref); + mean_dt(data, size, mean_data, dt_data); + + /* compute the normalization coefficients */ + const double a = dt_ref / dt_data; + const double b = mean_ref - a * mean_data; + + const float modma = mod * a; + const float sigmmmodmb = sigm * mod * b; + const float onesmod = 1.f - mod; + /* normalize the array */ + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (size_t i = 0; i < size; i++) { + data[i] = (modma * data[i] + sigmmmodmb) + onesmod * ref[i];//normalize mean and stdv and balance PDE + } + +} + +void ImProcFunctions::retinex_pde(const float * datain, float * dataout, int bfw, int bfh, float thresh, float multy, float * dE, int show, int dEenable, int normalize) +{ + /* + * Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ + * + + * @file retinex_pde_lib.c discrete Poisson equation + * @brief laplacian, DFT and Poisson routines + * + * @author Nicolas Limare + * adapted for Rawtherapee by Jacques Desmis 6-2019 + */ + + BENCHFUN +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads(omp_get_max_threads()); + } +#endif + float *data_fft, *data_fft04, *data_tmp, *data, *data_tmp04; + float *datashow = nullptr; + + if (show != 0) { + if (NULL == (datashow = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + } + + if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (NULL == (data_tmp04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + //first call to laplacian with plein strength + ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); + + if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (show == 1) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + datashow[y * bfw + x] = data_tmp[y * bfw + x]; + } + } + } + + //second call to laplacian with 40% strength ==> reduce effect if we are far from ref (deltaE) + ImProcFunctions::discrete_laplacian_threshold(data_tmp04, datain, bfw, bfh, 0.4f * thresh); + + if (NULL == (data_fft04 = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + //execute first + const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_fw); + + //execute second + if (dEenable == 1) { + const auto dct_fw04 = fftwf_plan_r2r_2d(bfh, bfw, data_tmp04, data_fft04, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_fw04); + fftwf_destroy_plan(dct_fw04); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) {//mix two fftw Laplacian : plein if dE near ref + for (int x = 0; x < bfw; x++) { + float prov = pow(dE[y * bfw + x], 4.5f); + data_fft[y * bfw + x] = prov * data_fft[y * bfw + x] + (1.f - prov) * data_fft04[y * bfw + x]; + } + } + } + + if (show == 2) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + datashow[y * bfw + x] = data_fft[y * bfw + x]; + } + } + } + + fftwf_free(data_fft04); + fftwf_free(data_tmp); + fftwf_free(data_tmp04); + + /* solve the Poisson PDE in Fourier space */ + /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ + ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); + + if (show == 3) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + datashow[y * bfw + x] = data_fft[y * bfw + x]; + } + } + } + + const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_bw); + fftwf_destroy_plan(dct_fw); + fftwf_destroy_plan(dct_bw); + fftwf_free(data_fft); + fftwf_cleanup(); + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_cleanup_threads(); + } +#endif + if (show != 4 && normalize == 1) { + normalize_mean_dt(data, datain, bfw * bfh, 1.f, 1.f); + } + + if (show == 0 || show == 4) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(multy * data[y * bfw + x]); + } + } + } else if (show == 1 || show == 2 || show == 3) { + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(multy * datashow[y * bfw + x]); + } + } + + fftwf_free(datashow); + } + + fftwf_free(data); + +} + +void ImProcFunctions::maskcalccol(bool invmask, bool pde, int bfw, int bfh, int xstart, int ystart, int sk, int cx, int cy, LabImage* bufcolorig, LabImage* bufmaskblurcol, LabImage* originalmaskcol, LabImage* original, LabImage* reserved, int inv, struct local_params & lp, + float strumask, bool astool, + const LocCCmaskCurve & locccmasCurve, bool lcmasutili, + const LocLLmaskCurve & locllmasCurve, bool llmasutili, + const LocHHmaskCurve & lochhmasCurve, bool lhmasutili, const LocHHmaskCurve & lochhhmasCurve, bool lhhmasutili, + bool multiThread, bool enaMask, bool showmaske, bool deltaE, bool modmask, bool zero, bool modif, float chrom, float rad, float lap, float gamma, float slope, float blendm, int shado, float amountcd, float anchorcd, + const LUTf& lmasklocalcurve, bool localmaskutili, + const LocwavCurve & loclmasCurvecolwav, bool lmasutilicolwav, int level_bl, int level_hl, int level_br, int level_hr, + int shortcu, bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope + ) +{ + array2D ble(bfw, bfh); + array2D blechro(bfw, bfh); + array2D hue(bfw, bfh); + array2D guid(bfw, bfh); + const std::unique_ptr bufreserv(new LabImage(bfw, bfh)); + float meanfab, fab; + mean_fab(xstart, ystart, bfw, bfh, bufcolorig, original, fab, meanfab, chrom, multiThread); + float chromult = 1.f - 0.01f * chrom; + float kinv = 1.f; + float kneg = 1.f; + + if (invmask) { + kinv = 0.f; + kneg = -1.f; + } + + if (deltaE || modmask || enaMask || showmaske) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufmaskblurcol->L[y][x] = original->L[y + ystart][x + xstart]; + bufmaskblurcol->a[y][x] = original->a[y + ystart][x + xstart]; + bufmaskblurcol->b[y][x] = original->b[y + ystart][x + xstart]; + bufreserv->L[y][x] = reserved->L[y + ystart][x + xstart]; + bufreserv->a[y][x] = reserved->a[y + ystart][x + xstart]; + bufreserv->b[y][x] = reserved->b[y + ystart][x + xstart]; + } + } + + JaggedArray blendstru(bfw, bfh); + + if (lp.blurcolmask >= 0.25f && strumask == 0.f) { + strumask = 0.1f; // to enable a small mask make FFT good ...why ?? + } + + if (strumask > 0.f) { + float delstrumask = 4.1f - strumask;//4.1 = 2 * max slider strumask + 0.1 + buildBlendMask(bufcolorig->L, blendstru, bfw, bfh, delstrumask); + float radblur = 0.02f * std::fabs(0.1f * rad);//empirical value + float rm = radblur / sk; + + if (rm > 0) { + float **mb = blendstru; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + } + + JaggedArray blendblur(bfw, bfh); + + JaggedArray blur(bfw, bfh); + + if (lp.contcolmask > 0.f) { + float contra = lp.contcolmask; + buildBlendMask(bufcolorig->L, blendblur, bfw, bfh, contra); + + + float radblur = 0.25f + 0.002f * std::fabs(rad);//empirical value + float rm = radblur / sk; + + if (lp.fftColorMask) { + if (rm < 0.3f) { + rm = 0.3f; + } + } + + if (rm > 0) { + float **mb = blendblur; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + if (lp.blurcolmask >= 0.25f) { + if (!lp.fftColorMask) { // || (lp.fftColorMask && call != 2)) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufcolorig->L, blur, bfw, bfh, lp.blurcolmask / sk); + } + } else { + ImProcFunctions::fftw_convol_blur2(bufcolorig->L, blur, bfw, bfh, lp.blurcolmask / sk, 0, 0); + } + + for (int i = 0; i < bfh; i++) { + for (int j = 0; j < bfw; j++) { + blur[i][j] = intp(blendblur[i][j], bufcolorig->L[i][j], rtengine::max(blur[i][j], 0.0f)); + } + } + } + } + + bool HHmaskcurve = false; + + if (lochhhmasCurve && lhhmasutili) { + for (int i = 0; i < 500; i++) { + if (lochhhmasCurve[i] != 0.5) { + HHmaskcurve = true; + } + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + float atan2Buffer[bfw] ALIGNED64; +// float atan2BufferH[bfw] ALIGNED64; +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic, 16) +#endif + for (int ir = 0; ir < bfh; ir++) { +#ifdef __SSE2__ + + if (lochhmasCurve && lhmasutili) { + int i = 0; + + for (; i < bfw - 3; i += 4) { + STVF(atan2Buffer[i], xatan2f(LVFU(bufcolorig->b[ir][i]), LVFU(bufcolorig->a[ir][i]))); + } + + for (; i < bfw; i++) { + atan2Buffer[i] = xatan2f(bufcolorig->b[ir][i], bufcolorig->a[ir][i]); + } + } + +#endif + + for (int jr = 0; jr < bfw; jr++) { + float kmaskL = 0.f; + float kmaskC = 0.f; + float kmaskHL = 0.f; + float kmaskH = 0.f; + float kmasstru = 0.f; + float kmasblur = 0.f; + + if (strumask > 0.f && !astool) { + kmasstru = bufcolorig->L[ir][jr] * blendstru[ir][jr]; + } + + if (lp.contcolmask > 0.f) { + + if (lp.blurcolmask >= 0.25f) { + + float prov = intp(blendstru[ir][jr], bufcolorig->L[ir][jr], rtengine::max(blur[ir][jr], 0.0f)); + kmasblur = bufcolorig->L[ir][jr] - prov ; + + } + } + + if (locllmasCurve && llmasutili) { + kmaskL = 32768.f * LIM01(kinv - kneg * locllmasCurve[(500.f / 32768.f) * bufcolorig->L[ir][jr]]); + + } + + if (!deltaE && locccmasCurve && lcmasutili) { + kmaskC = LIM01(kinv - kneg * locccmasCurve[500.f * (0.0001f + std::sqrt(SQR(bufcolorig->a[ir][jr]) + SQR(bufcolorig->b[ir][jr])) / fab)]); + } + + if (lochhmasCurve && lhmasutili) { +#ifdef __SSE2__ + const float huema = atan2Buffer[jr]; +#else + const float huema = xatan2f(bufcolorig->b[ir][jr], bufcolorig->a[ir][jr]); +#endif + float h = Color::huelab_to_huehsv2(huema); + h += 1.f / 6.f; + + if (h > 1.f) { + h -= 1.f; + } + + const float valHH = LIM01(kinv - kneg * lochhmasCurve[500.f * h]); + + if (!deltaE) { + kmaskH = valHH; + } + + kmaskHL = 32768.f * valHH; + } + + /* + //keep here in case of...but !! + if (lochhhmasCurve && HHmaskcurve) { + + #ifdef __SSE2__ + huemah = atan2BufferH[jr]; + #else + huemah = xatan2f(bufcolorig->b[ir][jr], bufcolorig->a[ir][jr]); + #endif + + float hh = Color::huelab_to_huehsv2(huemah); + hh += 1.f / 6.f; + + if (hh > 1.f) { + hh -= 1.f; + } + + const float val_HH = float (LIM01(((0.5f - lochhhmasCurve[500.f * hh])))); + kmaskHH = 2.f * val_HH; + const float hhro = kmaskHH; + + if (hhro != 0) { + newhr = huemah + hhro; + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + } + sincosval = xsincosf(newhr); + + } + */ + bufmaskblurcol->L[ir][jr] = clipLoc(kmaskL + kmaskHL + kmasstru + kmasblur); + bufmaskblurcol->a[ir][jr] = clipC((kmaskC + chromult * kmaskH)); + bufmaskblurcol->b[ir][jr] = clipC((kmaskC + chromult * kmaskH)); + + if (shortcu == 1) { //short circuit all L curve + bufmaskblurcol->L[ir][jr] = 32768.f - bufcolorig->L[ir][jr]; + } + + ble[ir][jr] = bufmaskblurcol->L[ir][jr] / 32768.f; + hue[ir][jr] = xatan2f(bufmaskblurcol->b[ir][jr], bufmaskblurcol->a[ir][jr]); + const float chromah = std::sqrt(SQR(bufmaskblurcol->b[ir][jr]) + SQR(bufmaskblurcol->a[ir][jr])); + + blechro[ir][jr] = chromah / 32768.f;//must be good perhaps more or less, only incidence on LIM blea bleb + guid[ir][jr] = Color::L2Y(bufcolorig->L[ir][jr]) / 32768.f; + + } + } + } + + std::unique_ptr bufprov; + if (delt) { + bufprov.reset(new LabImage(bfw, bfh)); + bufprov->CopyFrom(bufmaskblurcol, multiThread); + } + + if (rad != 0.f) { + const float tmpblur = rad < 0.f ? -1.f / rad : 1.f + rad; + const int r1 = rtengine::max(4 / sk * tmpblur + 0.5, 1); + const int r2 = rtengine::max(25 / sk * tmpblur + 0.5, 1); + + constexpr float epsilmax = 0.0005f; + constexpr float epsilmin = 0.00001f; + + constexpr float aepsil = (epsilmax - epsilmin) / 90.f; + constexpr float bepsil = epsilmax - 100.f * aepsil; + const float epsil = rad < 0.f ? 0.001f : aepsil * 0.1f * rad + bepsil; + + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2f * epsil, multiThread); + } + + LUTf lutTonemaskexp(65536); + calcGammaLut(gamma, slope, lutTonemaskexp); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float2 sincosval = xsincosf(hue[ir][jr]); + bufmaskblurcol->L[ir][jr] = lutTonemaskexp[ble[ir][jr] * 65536.f]; + bufmaskblurcol->a[ir][jr] = 32768.f * blechro[ir][jr] * sincosval.y; + bufmaskblurcol->b[ir][jr] = 32768.f * blechro[ir][jr] * sincosval.x; + } + } + + + if (strumask > 0.f && astool) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufmaskblurcol->L[ir][jr] *= (1.f + blendstru[ir][jr]); + } + } + } + + if (lmasklocalcurve && localmaskutili) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + bufmaskblurcol->L[ir][jr] = 0.5f * lmasklocalcurve[2.f * bufmaskblurcol->L[ir][jr]]; + } + } + + if (shado > 0) { + ImProcFunctions::shadowsHighlights(bufmaskblurcol, true, 1, 0, shado, 40, sk, 0, 60); + } + + int wavelet_level = level_br; + + int minwin = rtengine::min(bfw, bfh); + int maxlevelspot = 9; + + while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { + --maxlevelspot ; + } + + wavelet_level = rtengine::min(wavelet_level, maxlevelspot); + int maxlvl = wavelet_level; +// float contrast = 0.f; + bool wavcurvemask = false; + + if (loclmasCurvecolwav && lmasutilicolwav) { + for (int i = 0; i < 500; i++) { + if (loclmasCurvecolwav[i] != 0.5) { + wavcurvemask = true; + } + } + } + + if (wavcurvemask) { +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + wavelet_decomposition *wdspot = new wavelet_decomposition(bufmaskblurcol->L[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + if (wdspot->memory_allocation_failed()) { + return; + } + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float alow = 1.f; + float blow = 0.f; + if (level_hl != level_bl) { + alow = 1.f / (level_hl - level_bl); + blow = -alow * level_bl; + } + + float ahigh = 1.f; + float bhigh = 0.f; + + if (level_hr != level_br) { + ahigh = 1.f / (level_hr - level_br); + bhigh = -ahigh * level_br; + } + + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + int W_L = wdspot->level_W(level); + int H_L = wdspot->level_H(level); + float* const* wav_L = wdspot->level_coeffs(level); + + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + float insigma = 0.666f; //SD + float logmax = log(MaxP[level]); //log Max + float rapX = (mean[level] + sigma[level]) / MaxP[level]; //rapport between sD / max + float inx = log(insigma); + float iny = log(rapX); + float rap = inx / iny; //koef + float asig = 0.166f / (sigma[level]); + float bsig = 0.5f - asig * mean[level]; + float amean = 0.5f / mean[level]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + if(loclmasCurvecolwav && lmasutilicolwav) { + float absciss; + float &val = wav_L[dir][i]; + + if (fabsf(val) >= (mean[level] + sigma[level])) { //for max + float valcour = xlogf(fabsf(val)); + float valc = valcour - logmax; + float vald = valc * rap; + absciss = xexpf(vald); + } else if (fabsf(val) >= mean[level]) { + absciss = asig * fabsf(val) + bsig; + } else { + absciss = amean * fabsf(val); + } + + float klev = 1.f; + if (level >= level_hl && level <= level_hr) { + klev = 1.f; + } + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alow * level + blow; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahigh * level + bhigh; + } + } + + float kc = klev * (loclmasCurvecolwav[absciss * 500.f] - 0.5f); + float amplieffect = kc <= 0.f ? 1.f : 4.f; + + float kinterm = 1.f + amplieffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + + val *= kinterm; + + } + } + } + + } + } + + wdspot->reconstruct(bufmaskblurcol->L[0], 1.f); + delete wdspot; + + } + + if (lochhhmasCurve && HHmaskcurve) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + float huemah = xatan2f(bufmaskblurcol->b[ir][jr], bufmaskblurcol->a[ir][jr]); + float chromah = std::sqrt(SQR(bufmaskblurcol->b[ir][jr]) + SQR(bufmaskblurcol->a[ir][jr])); + + + float hh = Color::huelab_to_huehsv2(huemah); + hh += 1.f / 6.f; + + if (hh > 1.f) { + hh -= 1.f; + } + + const float val_HH = float ((0.5f - lochhhmasCurve[500.f * hh])); + const float hhro = 1.5f * val_HH; + float newhr = 0.f; + + if (hhro != 0) { + newhr = huemah + hhro;//we add radians and other dim between 0 1.. always radians but addition "false" + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + } + + float2 sincosval = xsincosf(newhr); + bufmaskblurcol->a[ir][jr] = clipC(chromah * sincosval.y); + bufmaskblurcol->b[ir][jr] = clipC(chromah * sincosval.x); + + } + } + + if (amountcd > 1.f) { //dynamic range compression for Mask + FattalToneMappingParams fatParams; + fatParams.enabled = true; + fatParams.threshold = 100.f; + fatParams.amount = amountcd; + fatParams.anchor = anchorcd; + int nlev = 1; + Imagefloat *tmpImagefat = nullptr; + tmpImagefat = new Imagefloat(bfw, bfh); + lab2rgb(*bufmaskblurcol, *tmpImagefat, params->icm.workingProfile); + ToneMapFattal02(tmpImagefat, fatParams, nlev, 0, nullptr, 0, 0, 0); + rgb2lab(*tmpImagefat, *bufmaskblurcol, params->icm.workingProfile); + delete tmpImagefat; + } + + if (delt) { + const std::unique_ptr> rdEBuffer(new JaggedArray(bfw, bfh)); + float** rdE = *(rdEBuffer.get()); + + deltaEforMask(rdE, bfw, bfh, bufreserv.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, lp.balance, lp.balanceh); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float rdEval = rdE[ir][jr]; + bufmaskblurcol->L[ir][jr] = bufprov->L[ir][jr] + rdEval * (bufmaskblurcol->L[ir][jr] - bufprov->L[ir][jr]); + bufmaskblurcol->a[ir][jr] = bufprov->a[ir][jr] + rdEval * (bufmaskblurcol->a[ir][jr] - bufprov->a[ir][jr]); + bufmaskblurcol->b[ir][jr] = bufprov->b[ir][jr] + rdEval * (bufmaskblurcol->b[ir][jr] - bufprov->b[ir][jr]); + } + } + } + + struct grad_params gp; + + if (lp.strmaexp != 0.f) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 0); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufmaskblurcol->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gp, jr, ir); + } + } + } + + if (lap > 0.f) { + const float *datain = bufmaskblurcol->L[0]; + const std::unique_ptr data_tmp(new float[bfh * bfw]); + + if (!pde) { + ImProcFunctions::discrete_laplacian_threshold(data_tmp.get(), datain, bfw, bfh, 200.f * lap); + } else { + ImProcFunctions::retinex_pde(datain, data_tmp.get(), bfw, bfh, 12.f * lap, 1.f, nullptr, 0, 0, 1); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufmaskblurcol->L[y][x] = data_tmp[y * bfw + x]; + } + } + } + } + + const float radiusb = 1.f / sk; + + if (deltaE || modmask || enaMask || showmaske) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufmaskblurcol->L, bufmaskblurcol->L, bfw, bfh, radiusb); + gaussianBlur(bufmaskblurcol->a, bufmaskblurcol->a, bfw, bfh, 1.f + (0.5f * rad) / sk); + gaussianBlur(bufmaskblurcol->b, bufmaskblurcol->b, bfw, bfh, 1.f + (0.5f * rad) / sk); + } + + if (zero || modif || modmask || deltaE || enaMask) { + originalmaskcol->CopyFrom(bufcolorig, multiThread); + blendmask(lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig, original, bufmaskblurcol, originalmaskcol, blendm, inv); + } + } +} + +void ImProcFunctions::InverseSharp_Local(float **loctemp, const float hueref, const float lumaref, const float chromaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ +//local sharp + // BENCHFUN + const float ach = lp.trans / 100.f; + const int GW = transformed->W; + const int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + const bool sharshow = lp.showmasksharmet == 1; + const bool previewshar = lp.showmasksharmet == 2; + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + float ampli = 1.f + std::fabs(lp.colorde); + ampli = 2.f + 0.5f * (ampli - 2.f); + + constexpr float aadark = -1.f; + constexpr float bbdark = 5000.f; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + float radius = 3.f / sk; +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const float mindE = 2.f + MINSCOPE * lp.senssha * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.senssha * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + const float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.senssha); + + switch (zone) { + case 0: { // outside selection and outside transition zone => full effect, no transition + const float difL = loctemp[y][x] - original->L[y][x]; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (sharshow) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 5.f * difL * reducdE; + } else if (previewshar) { + float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y][x] < bbdark) { //enhance dark luminance as user can see! + float dark = transformed->L[y][x]; + transformed->L[y][x] = dark * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + + } + + break; + } + + case 1: { // inside transition zone + const float difL = (loctemp[y][x] - original->L[y][x]) * (1.f - localFactor); + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (sharshow) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 5.f * difL * reducdE; + } else if (previewshar || lp.prevdE) { + const float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y][x] < bbdark) { //enhance dark luminance as user can see! + const float dark = transformed->L[y][x]; + transformed->L[y][x] = dark * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + break; + } + + case 2: { // inside selection => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + } + } + } + } + } +} + +void ImProcFunctions::Sharp_Local(int call, float **loctemp, int senstype, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + BENCHFUN + const float ach = lp.trans / 100.f; + const float varsens = senstype == 1 ? lp.senslc : lp.senssha; + const bool sharshow = (lp.showmasksharmet == 1); + const bool previewshar = (lp.showmasksharmet == 2); + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + float ampli = 1.f + std::fabs(lp.colorde); + ampli = 2.f + 0.5f * (ampli - 2.f); + + float darklim = 5000.f; + float aadark = -1.f; + float bbdark = darklim; + + const int GW = transformed->W; + const int GH = transformed->H; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + const float radius = 3.f / sk; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const int begy = int (lp.yc - lp.lyT); + const int begx = int (lp.xc - lp.lxL); + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + for (int x = 0; x < transformed->W; x++) { + const int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + //deltaE + const float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + + float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + const float reducview = reducdE; + reducdE *= localFactor; + + float difL; + + if (call == 2) { + difL = loctemp[loy - begy][lox - begx] - original->L[y][x]; + } else { + difL = loctemp[y][x] - original->L[y][x]; + } + + transformed->L[y][x] = CLIP(original->L[y][x] + difL * reducdE); + + if (sharshow) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 5.f * difL * reducdE; + } else if (previewshar || lp.prevdE) { + float difbdisp = reducview * 10000.f * lp.colorde; + + if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! + transformed->L[y][x] = transformed->L[y][x] * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + } + } + } +} + +void ImProcFunctions::Exclude_Local(float **deltaso, float hueref, float chromaref, float lumaref, float sobelref, float meansobel, const struct local_params & lp, const LabImage * original, LabImage * transformed, const LabImage * rsv, const LabImage * reserv, int cx, int cy, int sk) +{ + + BENCHFUN { + const float ach = lp.trans / 100.f; + const float varsens = lp.sensexclu; + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + + const int GW = transformed->W; + const int GH = transformed->H; + + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + // lumaref *= 327.68f; + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + //sobel + sobelref = rtengine::min(sobelref / 100.f, 60.f); + + const bool recip = sobelref < meansobel && sobelref < lp.stru; + + sobelref = log1p(sobelref); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + const float radius = 3.f / sk; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(reserv->L, origblur->L, GW, GH, radius); + gaussianBlur(reserv->a, origblur->a, GW, GH, radius); + gaussianBlur(reserv->b, origblur->b, GW, GH, radius); + + +#ifdef _OPENMP + #pragma omp barrier + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) + { + const int loy = cy + y; + const bool isZone0 = loy > (lp.yc + lp.ly - 1) || loy < lp.yc - lp.lyT; // // -1 fix issue 5554 + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + for (int x = 0; x < transformed->W; x++) { + transformed->L[y][x] = original->L[y][x]; + } + + continue; + } + + for (int x = 0; x < transformed->W; x++) { + const int lox = cx + x; + const bool isZone0x = lox > (lp.xc + lp.lx - 1) || lox < lp.xc - lp.lxL; // -1 fix issue 5554 + + if (isZone0x) { // outside selection and outside transition zone => no effect, keep original values + for (int x = 0; x < transformed->W; x++) { + transformed->L[y][x] = original->L[y][x]; + } + + continue; + } + + const int begx = int (lp.xc - lp.lxL); + const int begy = int (lp.yc - lp.lyT); + + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + continue; + } + + float rs = 0.f; + + const float csob = xlogf(1.f + rtengine::min(deltaso[loy - begy][lox - begx] / 100.f, 60.f) + 0.001f); + + if (!recip) { + rs = sobelref / csob; + } else { + rs = csob / sobelref; + } + + float affsob = 1.f; + + if (lp.struexc > 0.f && rs > 0.f) { + const float rsob = 0.002f * lp.struexc * rs; + const float minrs = 1.3f + 0.05f * lp.stru; + + if (rs < minrs) { + affsob = 1.f; + } else { + affsob = 1.f / pow_F((1.f + rsob), SQR(SQR(rs - minrs))); + } + } + + float abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + float chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + const float rL = origblur->L[y][x]; + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + + if (rL > 32.768f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + if (zone > 0) { + + const float difL = (rsv->L[loy - begy][lox - begx] - original->L[y][x]) * localFactor; + transformed->L[y][x] = CLIP(original->L[y][x] + difL * affsob * reducdE); + + const float difa = (rsv->a[loy - begy][lox - begx] - original->a[y][x]) * localFactor; + transformed->a[y][x] = clipC(original->a[y][x] + difa * affsob * reducdE); + + const float difb = (rsv->b[loy - begy][lox - begx] - original->b[y][x]) * localFactor; + transformed->b[y][x] = clipC(original->b[y][x] + difb * affsob * reducdE); + + } + } + } + } + } + } +} + + + +void ImProcFunctions::transit_shapedetect_retinex(int call, int senstype, LabImage * bufexporig, LabImage * bufmask, LabImage * buforigmas, float **buflight, float **bufchro, const float hueref, const float chromaref, const float lumaref, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + + BENCHFUN { + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + + + const float ach = lp.trans / 100.f; + const float varsens = lp.sensh; + + int GW = transformed->W; + int GH = transformed->H; + + // const float refa = chromaref * cos(hueref); + // const float refb = chromaref * sin(hueref); + + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + const bool retishow = ((lp.showmaskretimet == 1 || lp.showmaskretimet == 2)); + const bool previewreti = ((lp.showmaskretimet == 4)); + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + const bool showmas = lp.showmaskretimet == 3 ; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + const float radius = 3.f / sk; + const bool usemaskreti = lp.enaretiMask && senstype == 4 && !lp.enaretiMasktmap; + float strcli = 0.03f * lp.str; + + if (lp.scalereti == 1) + { + strcli = 0.015 * lp.str; + } + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float previewint = settings->previewselection; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = ystart; y < yend; y++) + { + const int loy = cy + y; + + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float rL = origblur->L[y][x] / 327.68f; + float dE; + float abdelta2 = 0.f; + float chrodelta2 = 0.f; + float huedelta2 = 0.f; + + if (!usemaskreti) { + abdelta2 = SQR(refa - origblur->a[y][x]) + SQR(refb - origblur->b[y][x]); + chrodelta2 = SQR(std::sqrt(SQR(origblur->a[y][x]) + SQR(origblur->b[y][x])) - (chromaref * 327.68f)); + huedelta2 = abdelta2 - chrodelta2; + dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - origblur->L[y][x])); + } else { + if (call == 2) { + abdelta2 = SQR(refa - buforigmas->a[y - ystart][x - xstart]) + SQR(refb - buforigmas->b[y - ystart][x - xstart]); + chrodelta2 = SQR(std::sqrt(SQR(buforigmas->a[y - ystart][x - xstart]) + SQR(buforigmas->b[y - ystart][x - xstart])) - (chromaref * 327.68f)); + huedelta2 = abdelta2 - chrodelta2; + dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - buforigmas->L[y - ystart][x - xstart])); + + } else { + abdelta2 = SQR(refa - buforigmas->a[y][x]) + SQR(refb - buforigmas->b[y][x]); + chrodelta2 = SQR(std::sqrt(SQR(buforigmas->a[y][x]) + SQR(buforigmas->b[y][x])) - (chromaref * 327.68f)); + huedelta2 = abdelta2 - chrodelta2; + dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - buforigmas->L[y][x])); + } + } + + float cli, clc; + + if (call == 2) { + cli = buflight[y - ystart][x - xstart]; + clc = previewreti ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; + } else { + cli = buflight[y][x]; + clc = previewreti ? settings->previewselection * 100.f : bufchro[y][x]; + + } + + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens) / 100.f; + + cli *= reducdE; + clc *= reducdE; + cli *= (1.f + strcli); + + if (rL > 0.1f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + if (senstype == 4) {//all except color and light (TODO) and exposure + float lightc; + + if (call == 2) { + lightc = bufexporig->L[y - ystart][x - xstart]; + } else { + lightc = bufexporig->L[y][x]; + } + + float fli = 1.f + cli; + float diflc = lightc * fli - original->L[y][x]; + diflc *= localFactor; + + if (!showmas) { + transformed->L[y][x] = CLIP(original->L[y][x] + diflc); + } else { + if (call == 2) { + + transformed->L[y][x] = bufmask->L[y - ystart][x - xstart]; + } else { + transformed->L[y][x] = bufmask->L[y][x]; + } + } ; + + if (retishow) { + transformed->L[y][x] = CLIP(12000.f + diflc); + } + } + + float fliab = 1.f; + float chra, chrb; + + if (call == 2) { + chra = bufexporig->a[y - ystart][x - xstart]; + chrb = bufexporig->b[y - ystart][x - xstart]; + } else { + chra = bufexporig->a[y][x]; + chrb = bufexporig->b[y][x]; + + } + + if (senstype == 5) { + fliab = 1.f + clc; + } + + const float difa = (chra * fliab - original->a[y][x]) * localFactor; + float difb = (chrb * fliab - original->b[y][x]) * localFactor; + + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + + if (showmas) { + if (call == 2) { + transformed->a[y][x] = bufmask->a[y - ystart][x - xstart]; + transformed->b[y][x] = bufmask->b[y - ystart][x - xstart]; + } else { + transformed->a[y][x] = bufmask->a[y][x]; + transformed->b[y][x] = bufmask->b[y][x]; + + } + } + + if (retishow) { + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } + + if (previewreti) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = previewint * difb; + } + } + } + } + } + + if (showmas || retishow || previewreti) + { + return; + } + + } +} + + +void ImProcFunctions::transit_shapedetect(int senstype, const LabImage * bufexporig, LabImage * originalmask, float **bufchro, bool HHutili, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, const struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + + BENCHFUN + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfw = xend - xstart; + const int bfh = yend - ystart; + // printf("h=%f l=%f c=%f s=%f\n", hueref, lumaref, chromaref, sobelref); + const float ach = lp.trans / 100.f; + float varsens = lp.sensex; + + if (senstype == 6 || senstype == 7) //cbdl + { + varsens = lp.senscb; + } else if (senstype == 8) //TM + { + varsens = lp.senstm; + } else if (senstype == 10) //local contrast + { + varsens = lp.senslc; + } + + //sobel //keep in case of, not used + sobelref /= 100.f; + meansobel /= 100.f; + + sobelref = rtengine::min(sobelref, 60.f); + + const bool k = !(sobelref < meansobel && sobelref < lp.stru); //does not always work with noisy images + + sobelref = log1p(sobelref); + + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + const float previewint = settings->previewselection; + + const bool cbshow = ((lp.showmaskcbmet == 1 || lp.showmaskcbmet == 2) && senstype == 6); + const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); + const bool previewcb = ((lp.showmaskcbmet == 4) && senstype == 6); + const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); + + const std::unique_ptr origblur(new LabImage(bfw, bfh)); + std::unique_ptr origblurmask; + + float radius = 3.f / sk; + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const bool usemaskcb = (lp.showmaskcbmet == 2 || lp.enacbMask || lp.showmaskcbmet == 4) && senstype == 6; + const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; + const bool usemaskall = (usemaskcb || usemasktm); + + if (usemaskall) + { + origblurmask.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, bfw, bfh, radius); + gaussianBlur(originalmask->a, origblurmask->a, bfw, bfh, radius); + gaussianBlur(originalmask->b, origblurmask->b, bfw, bfh, radius); + } + } + if (lp.equtm && senstype == 8) //normalize luminance for Tone mapping , at this place we can use for others senstype! + { + float *datain = new float[bfh * bfw]; + float *data = new float[bfh * bfw]; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + datain[(y - ystart) * bfw + (x - xstart)] = original->L[y][x]; + data[(y - ystart)* bfw + (x - xstart)] = bufexporig->L[y - ystart][x - xstart]; + } + + normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = data[(y - ystart) * bfw + x - xstart]; + } + + delete [] datain; + delete [] data; + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) + { + for (int x = 0; x < bfw; x++) { + origblur->L[y][x] = original->L[y + ystart][x + xstart]; + origblur->a[y][x] = original->a[y + ystart][x + xstart]; + origblur->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + gaussianBlur(origblur->L, origblur->L, bfw, bfh, radius); + gaussianBlur(origblur->a, origblur->a, bfw, bfh, radius); + gaussianBlur(origblur->b, origblur->b, bfw, bfh, radius); + + } + + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + float atan2Buffer[transformed->W] ALIGNED16; +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = ystart; y < yend; y++) + { + const int loy = cy + y; + +#ifdef __SSE2__ + + if (HHutili || senstype == 7) { + int i = xstart; + + for (; i < xend - 3; i += 4) { + vfloat av = LVFU(origblur->a[y - ystart][i - xstart]); + vfloat bv = LVFU(origblur->b[y - ystart][i - xstart]); + STVFU(atan2Buffer[i], xatan2f(bv, av)); + } + + for (; i < xend; i++) { + atan2Buffer[i] = xatan2f(origblur->b[y - ystart][i - xstart], origblur->a[y - ystart][i - xstart]); + } + } + +#endif + + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float rhue = 0; + + if (HHutili || senstype == 7) { +#ifdef __SSE2__ + rhue = atan2Buffer[x]; +#else + rhue = xatan2f(origblur->b[y - ystart][x - xstart], origblur->a[y - ystart][x - xstart]); +#endif + } + + const float rL = origblur->L[y - ystart][x - xstart] / 327.68f; + float rsob = 0.f; + + if (blend2 && ((senstype == 1 && lp.struexp > 0.f) || ((senstype == 0 || senstype == 100) && lp.struco > 0.f))) {//keep in case of, not used + const float csob = xlogf(1.f + rtengine::min(blend2[y - ystart][x - xstart] / 100.f, 60.f) + 0.001f); + + float rs; + + if (k) { + rs = sobelref / csob; + } else { + rs = csob / sobelref; + } + + if (rs > 0.f && senstype == 1) { + rsob = 1.1f * lp.struexp * rs; + } else if (rs > 0.f && (senstype == 0 || senstype == 100)) { + rsob = 1.1f * lp.struco * rs; + } + } + + const float dE = rsob + std::sqrt(kab * (SQR(refa - maskptr->a[y - ystart][x - xstart]) + SQR(refb - maskptr->b[y - ystart][x - xstart])) + kL * SQR(refL - maskptr->L[y - ystart][x - xstart])); + const float clc = (previewcb) ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + const float realstrchdE = reducdE * clc; + + if (rL > 0.1f) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + if (zone > 0) { + float factorx = localFactor; + float difL = 0.f; + + if (senstype == 6 || senstype == 8 || senstype == 10) { + difL = (bufexporig->L[y - ystart][x - xstart] - original->L[y][x]) * localFactor * reducdE; + transformed->L[y][x] = CLIP(original->L[y][x] + difL); + } + + if (senstype == 7) { + float difab = bufexporig->L[y - ystart][x - xstart] - std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])); + float2 sincosval = xsincosf(rhue); + float difa = difab * sincosval.y; + float difb = difab * sincosval.x; + difa *= factorx * (100.f + realstrchdE) / 100.f; + difb *= factorx * (100.f + realstrchdE) / 100.f; + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } else { + float flia = 1.f; + float flib = 1.f; + float chra = bufexporig->a[y - ystart][x - xstart]; + float chrb = bufexporig->b[y - ystart][x - xstart]; + + if (senstype == 3 || senstype == 30 || senstype == 8 || senstype == 6 || senstype == 10) { + flia = flib = ((100.f + realstrchdE) / 100.f); + } + + + float difa = chra * flia - original->a[y][x]; + float difb = chrb * flib - original->b[y][x]; + difa *= factorx; + difb *= factorx; + + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + + + if (cbshow || tmshow) { + transformed->L[y][x] = CLIP(12000.f + difL); + transformed->a[y][x] = clipC(difa); + transformed->b[y][x] = clipC(difb); + } else if (previewcb || previewtm || lp.prevdE) { + if (std::fabs(difb) < 500.f) { + difb += difL; + } + + transformed->a[y][x] = 0.f; + transformed->b[y][x] = previewint * difb; + } + } + } + } + } + } + } +} + +void ImProcFunctions::InverseColorLight_Local(bool tonequ, bool tonecurv, int sp, int senstype, struct local_params & lp, LabImage * originalmask, const LUTf& lightCurveloc, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& exlocalcurve, const LUTf& cclocalcurve, float adjustr, bool localcutili, const LUTf& lllocalcurve, bool locallutili, LabImage * original, LabImage * transformed, int cx, int cy, const float hueref, const float chromaref, const float lumaref, int sk) +{ + // BENCHFUN + const float ach = lp.trans / 100.f; + const float facc = (100.f + lp.chro) / 100.f; //chroma factor transition + float varsens = lp.sens; + + if (senstype == 0) { //Color and Light + varsens = lp.sens; + } else if (senstype == 1) { //exposure + varsens = lp.sensex; + } else if (senstype == 2) { //shadows highlight + varsens = lp.senshs; + } + + const int GW = transformed->W; + const int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + const std::unique_ptr temp(new LabImage(GW, GH)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + temp->L[y][x] = original->L[y][x]; + temp->a[y][x] = original->a[y][x]; + temp->b[y][x] = original->b[y][x]; + } + } + + if (senstype == 2) { // Shadows highlight + if (lp.shmeth == 0) { + ImProcFunctions::shadowsHighlights(temp.get(), lp.hsena, 1, lp.highlihs, lp.shadowhs, lp.radiushs, sk, lp.hltonalhs, lp.shtonalhs); + } else if (lp.shmeth == 1) { + const std::unique_ptr tmpImage(new Imagefloat(GW, GH)); + + lab2rgb(*temp, *tmpImage, params->icm.workingProfile); + + if (tonecurv) { //Tone response curve : does nothing if gamma=2.4 and slope=12.92 ==> gamma sRGB + const float gamtone = params->locallab.spots.at(sp).gamSH; + const float slotone = params->locallab.spots.at(sp).sloSH; + cmsHTRANSFORM dummy = nullptr; + workingtrc(tmpImage.get(), tmpImage.get(), GW, GH, -5, params->icm.workingProfile, 2.4, 12.92310, dummy, true, false, false); + workingtrc(tmpImage.get(), tmpImage.get(), GW, GH, 5, params->icm.workingProfile, gamtone, slotone, dummy, false, true, true); + } + + if (tonequ) { + tmpImage->normalizeFloatTo1(); + array2D Rtemp(GW, GH, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); + array2D Gtemp(GW, GH, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); + array2D Btemp(GW, GH, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); + tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, sk, multiThread); + tmpImage->normalizeFloatTo65535(); + } + + rgb2lab(*tmpImage, *temp, params->icm.workingProfile); + } + + } else if (senstype == 1) { //exposure + ImProcFunctions::exlabLocal(lp, GH, GW, original, temp.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + + if (exlocalcurve) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < temp->H; y++) { + for (int x = 0; x < temp->W; x++) { + const float lh = 0.5f * exlocalcurve[2.f * temp->L[y][x]]; // / ((lighn) / 1.9f) / 3.61f; //lh between 0 and 0 50 or more + temp->L[y][x] = lh; + } + } + } + + if (lp.expchroma != 0.f) { + const float ch = (1.f + 0.02f * lp.expchroma) ; + float chprosl; + + if (ch <= 1.f) {//convert data curve near values of slider -100 + 100, to be used after to detection shape + chprosl = 99.f * ch - 99.f; + } else { + constexpr float ampli = 70.f; + chprosl = clipChro(ampli * ch - ampli); //ampli = 25.f arbitrary empirical coefficient between 5 and 50 + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + const float epsi = original->L[y][x] == 0.f ? 0.001f : 0.f; + const float rapexp = temp->L[y][x] / (original->L[y][x] + epsi); + temp->a[y][x] *= (1.f + chprosl * rapexp); + temp->b[y][x] *= (1.f + chprosl * rapexp); + } + } + } + } else if (senstype == 0) { //Color and Light curves L C + if (cclocalcurve && localcutili) { // C=f(C) curve +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + //same as in "normal" + const float chromat = std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])); + constexpr float ampli = 25.f; + const float ch = (cclocalcurve[chromat * adjustr ]) / ((chromat + 0.00001f) * adjustr); //ch between 0 and 0 50 or more + const float chprocu = clipChro(ampli * ch - ampli); //ampli = 25.f arbitrary empirical coefficient between 5 and 50 + temp->a[y][x] = original->a[y][x] * (1.f + 0.01f * chprocu); + temp->b[y][x] = original->b[y][x] * (1.f + 0.01f * chprocu); + + } + } + } + + if (lllocalcurve && locallutili) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H; y++) { + for (int x = 0; x < transformed->W; x++) { + temp->L[y][x] = 0.5f * lllocalcurve[2.f * original->L[y][x]]; + } + } + } + } + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + const std::unique_ptr origblur(new LabImage(GW, GH)); + std::unique_ptr origblurmask; + const bool usemaskcol = (lp.enaColorMaskinv) && senstype == 0; + const bool usemaskexp = (lp.enaExpMaskinv) && senstype == 1; + const bool usemasksh = (lp.enaSHMaskinv) && senstype == 2; + const bool usemaskall = (usemaskcol || usemaskexp || usemasksh); + + float radius = 3.f / sk; + + if (usemaskall) { + origblurmask.reset(new LabImage(GW, GH)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); + } + } + + if (senstype == 1) { + radius = (2.f + 0.2f * lp.blurexp) / sk; + } else if (senstype == 0) { + radius = (2.f + 0.2f * lp.blurcol) / sk; + } else if (senstype == 2) { + radius = (2.f + 0.2f * lp.blurSH) / sk; + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + + for (int x = 0; x < transformed->W; x++) { + const float rL = origblur->L[y][x] / 327.68f; + + if (std::fabs(origblur->b[y][x]) < 0.01f) { + origblur->b[y][x] = 0.01f; + } + + constexpr float th_r = 0.01f; + + if (rL > th_r) { //to avoid crash with very low gamut in rare cases ex : L=0.01 a=0.5 b=-0.9 + const int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor);//rect not good + } + + //deltaE + float reducdE; + if (zone != 2) { + const float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + } + + switch (zone) { + case 2: { // outside selection and outside transition zone => no effect, keep original values + transformed->L[y][x] = original->L[y][x]; + transformed->a[y][x] = original->a[y][x]; + transformed->b[y][x] = original->b[y][x]; + break; + } + + case 1: { // inside transition zone + const float factorx = 1.f - localFactor; + + if (senstype == 0) { + const float epsia = original->a[y][x] == 0.f ? 0.0001f : 0.f; + const float epsib = original->b[y][x] == 0.f ? 0.0001f : 0.f; + float lumnew = original->L[y][x]; + const float difL = (temp->L[y][x] - original->L[y][x]) * (reducdE * factorx); + const float difa = (temp->a[y][x] - original->a[y][x]) * (reducdE * factorx); + const float difb = (temp->b[y][x] - original->b[y][x]) * (reducdE * factorx); + const float facCa = 1.f + (difa / (original->a[y][x] + epsia)); + const float facCb = 1.f + (difb / (original->b[y][x] + epsib)); + + if (lp.sens < 75.f) { + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(lumnew, lp.ligh, lightCurveloc); //replace L-curve + } + + const float fac = (100.f + factorx * lp.chro * reducdE) / 100.f; //chroma factor transition + const float diflc = (lumnew - original->L[y][x]) * (reducdE * factorx); + + transformed->L[y][x] = CLIP(1.f * (original->L[y][x] + diflc + difL)); + transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa) ; + transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); + } else { + const float fac = (100.f + factorx * lp.chro) / 100.f; //chroma factor transition + + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(original->L[y][x], lp.ligh, lightCurveloc); + } + + const float diflc = (lumnew - original->L[y][x]) * factorx; + transformed->L[y][x] = CLIP(original->L[y][x] + diflc + difL); + transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa); + transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); + } + } else if (senstype == 1 || senstype == 2) { + const float diflc = (temp->L[y][x] - original->L[y][x]) * (reducdE * factorx); + const float difa = (temp->a[y][x] - original->a[y][x]) * (reducdE * factorx); + const float difb = (temp->b[y][x] - original->b[y][x]) * (reducdE * factorx); + transformed->L[y][x] = CLIP(original->L[y][x] + diflc); + transformed->a[y][x] = clipC(original->a[y][x] + difa) ; + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + + break; + } + + case 0: { // inside selection => full effect, no transition + if (senstype == 0) { + const float epsia = original->a[y][x] == 0.f ? 0.0001f : 0.f; + const float epsib = original->b[y][x] == 0.f ? 0.0001f : 0.f; + float lumnew = original->L[y][x]; + const float difL = (temp->L[y][x] - original->L[y][x]) * reducdE; + const float difa = (temp->a[y][x] - original->a[y][x]) * reducdE; + const float difb = (temp->b[y][x] - original->b[y][x]) * reducdE; + const float facCa = 1.f + difa / (original->a[y][x] + epsia); + const float facCb = 1.f + difb / (original->b[y][x] + epsib); + + if (lp.sens < 75.f) { + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(lumnew, lp.ligh, lightCurveloc); //replace L-curve + } + + const float fac = (100.f + lp.chro * reducdE) / 100.f; //chroma factor transition + const float diflc = (lumnew - original->L[y][x]) * reducdE; + + transformed->L[y][x] = CLIP(original->L[y][x] + diflc + difL); + transformed->a[y][x] = clipC(original->a[y][x] * fac * facCa) ; + transformed->b[y][x] = clipC(original->b[y][x] * fac * facCb); + } else { + if ((lp.ligh != 0.f || lp.cont != 0)) { + lumnew = calclightinv(original->L[y][x], lp.ligh, lightCurveloc); + } + + transformed->L[y][x] = CLIP(lumnew + difL) ; + transformed->a[y][x] = clipC(original->a[y][x] * facc * facCa); + transformed->b[y][x] = clipC(original->b[y][x] * facc * facCb); + } + } else if (senstype == 1 || senstype == 2) { + const float diflc = (temp->L[y][x] - original->L[y][x]) * reducdE; + const float difa = (temp->a[y][x] - original->a[y][x]) * reducdE; + const float difb = (temp->b[y][x] - original->b[y][x]) * reducdE; + transformed->L[y][x] = CLIP(original->L[y][x] + diflc); + transformed->a[y][x] = clipC(original->a[y][x] + difa) ; + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + } + } + } + } + } + } +} + +void ImProcFunctions::calc_ref(int sp, LabImage * original, LabImage * transformed, int cx, int cy, int oW, int oH, int sk, double & huerefblur, double & chromarefblur, double & lumarefblur, double & hueref, double & chromaref, double & lumaref, double & sobelref, float & avg, const LocwavCurve & locwavCurveden, bool locwavdenutili) +{ + if (params->locallab.enabled) { + //always calculate hueref, chromaref, lumaref before others operations use in normal mode for all modules exceprt denoise + struct local_params lp; + calcLocalParams(sp, oW, oH, params->locallab, lp, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, locwavCurveden, locwavdenutili); + int begy = lp.yc - lp.lyT; + int begx = lp.xc - lp.lxL; + int yEn = lp.yc + lp.ly; + int xEn = lp.xc + lp.lx; + float avg2 = 0.f; + int nc2 = 0; + + for (int y = 0; y < transformed->H ; y++) //{ + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int loy = cy + y; + + if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { + avg2 += original->L[y][x]; + nc2++; + } + } + + avg2 /= 32768.f; + avg = avg2 / nc2; +// double precision for large summations + double aveA = 0.; + double aveB = 0.; + double aveL = 0.; + double aveChro = 0.; + double aveAblur = 0.; + double aveBblur = 0.; + double aveLblur = 0.; + double aveChroblur = 0.; + + double avesobel = 0.; +// int precision for the counters + int nab = 0; + int nso = 0; + int nsb = 0; +// single precision for the result + float avA, avB, avL; + int spotSize = 0.88623f * rtengine::max(1, lp.cir / sk); //18 + //O.88623 = std::sqrt(PI / 4) ==> sqare equal to circle + int spotSise2; // = 0.88623f * max (1, lp.cir / sk); //18 + + // very small region, don't use omp here + LabImage *sobelL; + LabImage *deltasobelL; + LabImage *origsob; + LabImage *origblur = nullptr; + LabImage *blurorig = nullptr; + + int spotSi = 1 + 2 * rtengine::max(1, lp.cir / sk); + + if (spotSi < 5) { + spotSi = 5; + } + + spotSise2 = (spotSi - 1) / 2; + + JaggedArray blend3(spotSi, spotSi); + + origsob = new LabImage(spotSi, spotSi); + sobelL = new LabImage(spotSi, spotSi); + deltasobelL = new LabImage(spotSi, spotSi); + bool isdenoise = false; + + if ((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f) && lp.denoiena) { + isdenoise = true; + } + + if (isdenoise) { + origblur = new LabImage(spotSi, spotSi); + blurorig = new LabImage(spotSi, spotSi); + + for (int y = rtengine::max(cy, (int)(lp.yc - spotSise2)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSise2 + 1)); y++) { + for (int x = rtengine::max(cx, (int)(lp.xc - spotSise2)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSise2 + 1)); x++) { + int yb = rtengine::max(cy, (int)(lp.yc - spotSise2)); + + int xb = rtengine::max(cx, (int)(lp.xc - spotSise2)); + + int z = y - yb; + int u = x - xb; + origblur->L[z][u] = original->L[y - cy][x - cx]; + origblur->a[z][u] = original->a[y - cy][x - cx]; + origblur->b[z][u] = original->b[y - cy][x - cx]; + + } + } + + float radius = 3.f / sk; + { + //No omp + gaussianBlur(origblur->L, blurorig->L, spotSi, spotSi, radius); + gaussianBlur(origblur->a, blurorig->a, spotSi, spotSi, radius); + gaussianBlur(origblur->b, blurorig->b, spotSi, spotSi, radius); + + } + + for (int y = 0; y < spotSi; y++) { + for (int x = 0; x < spotSi; x++) { + aveLblur += blurorig->L[y][x]; + aveAblur += blurorig->a[y][x]; + aveBblur += blurorig->b[y][x]; + aveChroblur += std::sqrt(SQR(blurorig->b[y - cy][x - cx]) + SQR(blurorig->a[y - cy][x - cx])); + nsb++; + + } + } + } + + //ref for luma, chroma, hue + for (int y = rtengine::max(cy, (int)(lp.yc - spotSize)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSize + 1)); y++) { + for (int x = rtengine::max(cx, (int)(lp.xc - spotSize)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSize + 1)); x++) { + aveL += original->L[y - cy][x - cx]; + aveA += original->a[y - cy][x - cx]; + aveB += original->b[y - cy][x - cx]; + aveChro += std::sqrt(SQR(original->b[y - cy][x - cx]) + SQR(original->a[y - cy][x - cx])); + nab++; + } + } + + //ref for sobel + for (int y = rtengine::max(cy, (int)(lp.yc - spotSise2)); y < rtengine::min(transformed->H + cy, (int)(lp.yc + spotSise2 + 1)); y++) { + for (int x = rtengine::max(cx, (int)(lp.xc - spotSise2)); x < rtengine::min(transformed->W + cx, (int)(lp.xc + spotSise2 + 1)); x++) { + int yb = rtengine::max(cy, (int)(lp.yc - spotSise2)); + + int xb = rtengine::max(cx, (int)(lp.xc - spotSise2)); + + int z = y - yb; + int u = x - xb; + origsob->L[z][u] = original->L[y - cy][x - cx]; + nso++; + } + } + + const float radius = 3.f / (sk * 1.4f); //0 to 70 ==> see skip + + SobelCannyLuma(sobelL->L, origsob->L, spotSi, spotSi, radius); + int nbs = 0; + + for (int y = 0; y < spotSi ; y ++) + for (int x = 0; x < spotSi ; x ++) { + avesobel += sobelL->L[y][x]; + nbs++; + } + + sobelref = avesobel / nbs; + + delete sobelL; + + delete deltasobelL; + delete origsob; + aveL = aveL / nab; + aveA = aveA / nab; + aveB = aveB / nab; + aveChro = aveChro / nab; + aveChro /= 327.68f; + avA = aveA / 327.68f; + avB = aveB / 327.68f; + avL = aveL / 327.68f; + hueref = xatan2f(avB, avA); //mean hue + + if (isdenoise) { + aveLblur = aveLblur / nsb; + aveChroblur = aveChroblur / nsb; + aveChroblur /= 327.68f; + aveAblur = aveAblur / nsb; + aveBblur = aveBblur / nsb; + float avAblur = aveAblur / 327.68f; + float avBblur = aveBblur / 327.68f; + float avLblur = aveLblur / 327.68f; + huerefblur = xatan2f(avBblur, avAblur); + chromarefblur = aveChroblur; + lumarefblur = avLblur; + } else { + huerefblur = 0.f; + chromarefblur = 0.f; + lumarefblur = 0.f; + } + + chromaref = aveChro; + lumaref = avL; + + // printf("Calcref => sp=%i befend=%i huere=%2.1f chromare=%2.1f lumare=%2.1f sobelref=%2.1f\n", sp, befend, hueref, chromaref, lumaref, sobelref / 100.f); + + if (isdenoise) { + delete origblur; + delete blurorig; + } + + if (lumaref > 95.f) {//to avoid crash + lumaref = 95.f; + } + } +} +//doc fftw3 says optimum is with size 2^a * 3^b * 5^c * 7^d * 11^e * 13^f with e+f = 0 or 1 +//number for size between 18144 and 1 ==> 18000 pixels cover 99% all sensor +const int fftw_size[] = {18144, 18000, 17920, 17836, 17820, 17640, 17600, 17550, 17500, 17496, 17472, 17325, 17280, 17248, 17199, 17150, 17010, 16896, 16875, 16848, 16807, + 16800, 16640, 16632, 16500, 16464, 16384, 16380, 16250, 16200, 16170, 16128, 16038, 16000, 15925, 15876, 15840, 15795, 15750, 15680, 15625, 15600, 15552, 15435, 15400, + 15360, 15309, 15288, 15120, 15092, 15000, 14976, 14850, 14784, 14742, 14700, 14625, 14580, 14560, 14553, 14336, 14406, 14400, 14256, 14175, 14112, 14080, 14040, 14000, 13860, + 13824, 13750, 13720, 13650, 13608, 13500, 13475, 13440, 13377, 13365, 13312, 13230, 13200, 13125, 13122, 13104, 13000, 12960, 12936, 12800, 12740, 12672, 12636, 12600, + 12544, 12500, 12480, 12474, 12375, 12348, 12320, 12288, 12285, 12250, 12150, 12096, 12005, 12000, 11907, 11880, 11760, 11700, 11664, 11648, 11550, 11520, 11466, 11375, + 11340, 11319, 11264, 11250, 11232, 11200, 11088, 11025, 11000, 10976, 10935, 10920, 10800, 10780, 10752, 10692, 10584, 10560, 10530, 10400, 10395, 10368, 10290, 10240, + 10206, 10192, 10125, 10080, 10000, 9984, 9900, 9604, 9856, 9828, 9800, 9750, 9720, 9702, 9625, 9600, 9555, 9504, 9477, 9450, 9408, 9375, 9360, 9261, 9240, + 9216, 9100, 9072, 9000, 8960, 8918, 8910, 8820, 8800, 8775, 8750, 8748, 8736, 8640, 8624, 8575, 8505, 8448, 8424, 8400, 8320, 8316, 8250, 8232, 8192, 8190, 8125, + 8100, 8085, 8064, 8019, 8000, 7938, 7920, 7875, 7840, 7800, 7776, 7700, 7680, 7644, 7560, 7546, 7500, 7488, 7425, 7392, 7371, 7350, 7290, 7280, 7203, 7200, 7168, + 7128, 7056, 7040, 7020, 7000, 6930, 6912, 6875, 6860, 6825, 6804, 6750, 6720, 6656, 6615, 6600, 6561, 6552, 6500, 6480, 6468, 6400, 6370, 6336, 6318, 6300, + 6272, 6250, 6240, 6237, 6174, 6160, 6144, 6125, 6075, 6048, 6000, 5940, 5880, 5850, 5832, 5824, 5775, 5760, 5670, 5632, 5625, 5616, 5600, 5544, 5500, 5488, + 5460, 5400, 5390, 5376, 5346, 5292, 5280, 5265, 5250, 5200, 5184, 5145, 5120, 5103, 5096, 5040, 5000, 4992, 4950, 4928, 4914, 4900, 4875, 4860, 4851, 4802, + 4800, 4752, 4725, 4704, 4680, 4620, 4608, 4550, 4536, 4500, 4480, 4459, 4455, 4410, 4400, 4375, 4374, 4368, 4320, 4312, 4224, 4212, 4200, 4160, 4158, 4125, + 4116, 4096, 4095, 4050, 4032, 4000, 3969, 3960, 3920, 3900, 3888, 3850, 3840, 3822, 3780, 3773, 3750, 3744, 3696, 3675, 3645, 3640, 3600, 3584, 3564, 3528, + 3520, 3510, 3500, 3465, 3456, 3430, 3402, 3375, 3360, 3328, 3300, 3276, 3250, 3240, 3234, 3200, 3185, 3168, 3159, 3150, 3136, 3125, 3120, 3087, 3080, 3072, + 3024, 3000, 2970, 2940, 2925, 2916, 2912, 2880, 2835, 2816, 2808, 2800, 2772, 2750, 2744, 2730, 2700, 2695, 2688, 2673, 2646, 2640, 2625, 2600, 2592, 2560, + 2548, 2520, 2500, 2496, 2475, 2464, 2457, 2450, 2430, 2401, 2400, 2376, 2352, 2340, 2310, 2304, 2275, 2268, 2250, 2240, 2205, 2200, 2187, 2184, 2160, 2156, + 2112, 2106, 2100, 2080, 2079, 2058, 2048, 2025, 2016, 2000, 1980, 1960, 1950, 1944, 1936, 1925, 1920, 1911, 1890, 1875, 1872, 1848, 1820, 1800, 1792, 1782, + 1764, 1760, 1755, 1750, 1728, 1715, 1701, 1680, 1664, 1650, 1638, 1625, 1620, 1617, 1600, 1584, 1575, 1568, 1560, 1540, 1536, 1512, 1500, 1485, 1470, 1458, + 1456, 1440, 1408, 1404, 1400, 1386, 1375, 1372, 1365, 1350, 1344, 1323, 1320, 1300, 1296, 1280, 1274, 1260, 1250, 1248, 1232, 1225, 1215, 1200, 1188, 1176, + 1170, 1155, 1152, 1134, 1125, 1120, 1100, 1092, 1080, 1078, 1056, 1053, 1050, 1040, 1029, 1024, 1008, 1000, 990, 980, 975, 972, 960, 945, 936, 924, 910, 900, + 896, 891, 882, 880, 875, 864, 840, 832, 825, 819, 810, 800, 792, 784, 780, 770, 768, 756, 750, 735, 729, 728, 720, 704, 702, 700, 693, 686, 675, 672, 660, + 650, 648, 640, 637, 630, 625, 624, 616, 600, 594, 588, 585, 576, 567, 560, 550, 546, 540, 539, 528, 525, 520, 512, 504, 500, 495, 490, 486, 480, 468, 462, 455, + 450, 448, 441, 440, 432, 420, 416, 405, 400, 396, 392, 390, 385, 384, 378, 375, 364, 360, 352, 351, 350, 343, 336, 330, 325, 324, 320, 315, 312, 308, 300, 297, + 294, 288, 280, 275, 273, 270, 264, 260, 256, 252, 250, 245, 243, 240, 234, 231, 225, 224, 220, 216, 210, 208, 200, 198, 196, 195, 192, 189, 182, 180, 176, 175, + 168, 165, 162, 160, 156, 154, 150, 147, 144, 143, 140, 135, 132, 130, 128, 126, 125, 120, 117, 112, 110, 108, 105, 104, 100, 99, 98, 96, 91, 90, 88, 84, 81, + 80, 78, 77, 75, 72, 70, 66, 65, 64, 63, 60, 56, 55, 54, 52, 50, 49, 48, 45, 44, 42, 40, 39, 36, 35, 33, 32, 30, 28, 27, 26, 25, 24, 22, 21, 20, 18, 16, 15, + 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + }; + +int N_fftwsize = sizeof(fftw_size) / sizeof(fftw_size[0]); + + +void optfft(int N_fftwsize, int &bfh, int &bfw, int &bfhr, int &bfwr, struct local_params& lp, int H, int W, int &xstart, int &ystart, int &xend, int ¥d, int cx, int cy) +{ + int ftsizeH = 1; + int ftsizeW = 1; + + for (int ft = 0; ft < N_fftwsize; ft++) { //find best values + if (fftw_size[ft] <= bfh) { + ftsizeH = fftw_size[ft]; + break; + } + } + + for (int ft = 0; ft < N_fftwsize; ft++) { + if (fftw_size[ft] <= bfw) { + ftsizeW = fftw_size[ft]; + break; + } + } + + //optimize with size fftw + bool reduW = false; + bool reduH = false; + + if (ystart == 0 && yend < H) { + lp.ly -= (bfh - ftsizeH); + } else if (ystart != 0 && yend == H) { + lp.lyT -= (bfh - ftsizeH); + } else if (ystart != 0 && yend != H) { + if (lp.ly <= lp.lyT) { + lp.lyT -= (bfh - ftsizeH); + } else { + lp.ly -= (bfh - ftsizeH); + } + } else if (ystart == 0 && yend == H) { + bfhr = ftsizeH; + reduH = true; + } + + if (xstart == 0 && xend < W) { + lp.lx -= (bfw - ftsizeW); + } else if (xstart != 0 && xend == W) { + lp.lxL -= (bfw - ftsizeW); + } else if (xstart != 0 && xend != W) { + if (lp.lx <= lp.lxL) { + lp.lxL -= (bfw - ftsizeW); + } else { + lp.lx -= (bfw - ftsizeW); + } + } else if (xstart == 0 && xend == W) { + bfwr = ftsizeW; + reduW = true; + } + + //new values optimized + ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, H); + xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, W); + bfh = bfhr = yend - ystart; + bfw = bfwr = xend - xstart; + + if (reduH) { + bfhr = ftsizeH; + } + + if (reduW) { + bfwr = ftsizeW; + } + + if (settings->verbose) { + printf("Nyst=%i Nyen=%i lp.yc=%f lp.lyT=%f lp.ly=%f bfh=%i bfhr=%i origH=%i ftsizeH=%i\n", ystart, yend, lp.yc, lp.lyT, lp.ly, bfh, bfhr, H, ftsizeH); + printf("Nxst=%i Nxen=%i lp.xc=%f lp.lxL=%f lp.lx=%f bfw=%i bfwr=%i origW=%i ftsizeW=%i\n", xstart, xend, lp.xc, lp.lxL, lp.lx, bfw, bfwr, W, ftsizeW); + } +} + +void ImProcFunctions::BlurNoise_Local(LabImage *tmp1, LabImage * originalmask, float **bufchro, const float hueref, const float chromaref, const float lumaref, local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ +//local BLUR + BENCHFUN + + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + + const float ach = lp.trans / 100.f; + const int GW = transformed->W; + const int GH = transformed->H; + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + const bool blshow = lp.showmaskblmet == 1 || lp.showmaskblmet == 2; + const bool previewbl = lp.showmaskblmet == 4; + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + const float ampli = 1.5f + 0.5f * std::fabs(lp.colorde); + + constexpr float darklim = 5000.f; + constexpr float aadark = -1.f; + + const bool usemaskbl = lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 4; + const bool usemaskall = usemaskbl; + const float radius = 3.f / sk; + std::unique_ptr origblurmask; + + const std::unique_ptr origblur(new LabImage(GW, GH)); + + if (usemaskall) { + origblurmask.reset(new LabImage(GW, GH)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, GW, GH, radius); + gaussianBlur(originalmask->a, origblurmask->a, GW, GH, radius); + gaussianBlur(originalmask->b, origblurmask->b, GW, GH, radius); + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(original->L, origblur->L, GW, GH, radius); + gaussianBlur(original->a, origblur->a, GW, GH, radius); + gaussianBlur(original->b, origblur->b, GW, GH, radius); + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + const float mindE = 4.f + MINSCOPE * lp.sensbn * lp.thr;//best usage ?? with blurnoise + const float maxdE = 5.f + MAXSCOPE * lp.sensbn * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = ystart; y < yend; y++) { + const int loy = cy + y; + + for (int x = xstart, lox = cx + x; x < xend; x++, lox++) { + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + const float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + const float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - chromaref * 327.68f); + const float huedelta2 = abdelta2 - chrodelta2; + const float dE = std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, lp.sensbn); + const float clc = previewbl ? settings->previewselection * 100.f : bufchro[y - ystart][x - xstart]; + const float realstrchdE = reducdE * clc; + + float difL = (tmp1->L[y - ystart][x - xstart] - original->L[y][x]) * localFactor * reducdE; + transformed->L[y][x] = CLIP(original->L[y][x] + difL); + const float fli = (100.f + realstrchdE) / 100.f; + const float difa = tmp1->a[y - ystart][x - xstart] * fli - original->a[y][x] * localFactor; + const float difb = tmp1->b[y - ystart][x - xstart] * fli - original->b[y][x] * localFactor; + + if (!lp.actsp) { + transformed->a[y][x] = clipC(original->a[y][x] + difa); + transformed->b[y][x] = clipC(original->b[y][x] + difb); + } + + const float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); + + if (blshow && lp.colorde < 0) { //show modifications with use "b" + // (origshow && lp.colorde < 0) { //original Retinex + transformed->a[y][x] = 0.f; + transformed->b[y][x] = ampli * 8.f * difL * reducdE; + transformed->L[y][x] = CLIP(12000.f + 0.5f * ampli * difL); + + } else if (blshow && lp.colorde > 0) {//show modifications without use "b" + if (difL < 1000.f) {//if too low to be view use ab + difL += 0.5f * maxdifab; + } + + transformed->L[y][x] = CLIP(12000.f + 0.5f * ampli * difL); + transformed->a[y][x] = clipC(ampli * difa); + transformed->b[y][x] = clipC(ampli * difb); + } else if (previewbl || lp.prevdE) {//show deltaE + const float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y][x] < darklim) { //enhance dark luminance as user can see! + float dark = transformed->L[y][x]; + transformed->L[y][x] = dark * aadark + darklim; + } + + if (lp.colorde <= 0) { + transformed->a[y][x] = 0.f; + transformed->b[y][x] = difbdisp; + } else { + transformed->a[y][x] = -difbdisp; + transformed->b[y][x] = 0.f; + } + } + } + } + } +} + +void ImProcFunctions::transit_shapedetect2(int call, int senstype, const LabImage * bufexporig, const LabImage * bufexpfin, LabImage * originalmask, const float hueref, const float chromaref, const float lumaref, float sobelref, float meansobel, float ** blend2, struct local_params & lp, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + //initialize coordinates + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfw = xend - xstart; + int bfh = yend - ystart; + + int bfhr = bfh; + int bfwr = bfw; + if (lp.blurcolmask >= 0.25f && lp.fftColorMask && call == 2) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + bfh = bfhr; + bfw = bfwr; + + //initialize scope + float varsens = lp.sensex;//exposure + + if (senstype == 0) { //Color and light + varsens = lp.sens; + } else if (senstype == 2) { //vibrance + varsens = lp.sensv; + } else if (senstype == 9) { //shadowshighlight + varsens = lp.senshs; + } else if (senstype == 3) { //softlight + varsens = lp.senssf; + } else if (senstype == 30) { //dehaze + varsens = lp.sensh; + } else if (senstype == 8) { //TM + varsens = lp.senstm; + } else if (senstype == 10) { //local contrast + varsens = lp.senslc; + } else if (senstype == 11) { //encoding log + varsens = lp.sensilog; + } + + bool delt = lp.deltaem; + + //sobel + sobelref /= 100.f; + meansobel /= 100.f; + + sobelref = rtengine::min(sobelref, 60.f); + + const bool k = !(sobelref < meansobel && sobelref < lp.stru); //does not always work with noisy images + + sobelref = log1p(sobelref); + + //references Spot + const float refa = chromaref * cos(hueref) * 327.68f; + const float refb = chromaref * sin(hueref) * 327.68f; + const float refL = lumaref * 327.68f; + + //to preview modifications, scope, mask + const bool expshow = ((lp.showmaskexpmet == 1 || lp.showmaskexpmet == 2) && senstype == 1); + const bool vibshow = ((lp.showmaskvibmet == 1 || lp.showmaskvibmet == 2) && senstype == 2); + const bool colshow = ((lp.showmaskcolmet == 1 || lp.showmaskcolmet == 2) && senstype == 0); + const bool SHshow = ((lp.showmaskSHmet == 1 || lp.showmaskSHmet == 2) && senstype == 9); + const bool tmshow = ((lp.showmasktmmet == 1 || lp.showmasktmmet == 2) && senstype == 8); + const bool lcshow = ((lp.showmasklcmet == 1 || lp.showmasklcmet == 2) && senstype == 10); + const bool origshow = ((lp.showmasksoftmet == 5) && senstype == 3 && lp.softmet == 1); + + + const bool previewvib = ((lp.showmaskvibmet == 4) && senstype == 2); + const bool previewexp = ((lp.showmaskexpmet == 5) && senstype == 1); + const bool previewcol = ((lp.showmaskcolmet == 5) && senstype == 0); + const bool previewSH = ((lp.showmaskSHmet == 4) && senstype == 9); + const bool previewtm = ((lp.showmasktmmet == 4) && senstype == 8); + const bool previewlc = ((lp.showmasklcmet == 4) && senstype == 10); + const bool previeworig = ((lp.showmasksoftmet == 6) && senstype == 3 && lp.softmet == 1); + + float radius = 3.f / sk; + + if (senstype == 1) { + radius = (2.f + 0.2f * lp.blurexp) / sk; + } else if (senstype == 0) { + radius = (2.f + 0.2f * lp.blurcol) / sk; + } else if (senstype == 9) { + radius = (2.f + 0.2f * lp.blurSH) / sk; + } + + const std::unique_ptr origblur(new LabImage(bfw, bfh)); + std::unique_ptr origblurmask; + + //balance deltaE + const float kL = lp.balance / SQR(327.68f); + const float kab = balancedeltaE(lp.balance) / SQR(327.68f); + const float kH = lp.balanceh; + const float kch = balancedeltaE(kH); + + if (lp.colorde == 0) { + lp.colorde = -1;//to avoid black + } + + float ampli = 1.f + std::fabs(lp.colorde); + ampli = 2.f + 0.5f * (ampli - 2.f); + + float darklim = 5000.f; + float aadark = -1.f; + float bbdark = darklim; + + const bool usemaskvib = (lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 4) && senstype == 2; + const bool usemaskexp = (lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 5) && senstype == 1; + const bool usemaskcol = (lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 5) && senstype == 0; + const bool usemaskSH = (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 4) && senstype == 9; + const bool usemasktm = (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 4) && senstype == 8; + const bool usemasklc = (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 4) && senstype == 10; + const bool usemaskall = (usemaskexp || usemaskvib || usemaskcol || usemaskSH || usemasktm || usemasklc); + + //blur a little mask + if (usemaskall) { + origblurmask.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(originalmask->L, origblurmask->L, bfw, bfh, radius); + gaussianBlur(originalmask->a, origblurmask->a, bfw, bfh, radius); + gaussianBlur(originalmask->b, origblurmask->b, bfw, bfh, radius); + } + } + + if (lp.equtm && senstype == 8) { //normalize luminance for Tone mapping , at this place we can use for others senstype! + float *datain = new float[bfh * bfw]; + float *data = new float[bfh * bfw]; + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + datain[(y - ystart) * bfw + (x - xstart)] = original->L[y][x]; + data[(y - ystart)* bfw + (x - xstart)] = bufexpfin->L[y - ystart][x - xstart]; + } + + normalize_mean_dt(data, datain, bfh * bfw, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = ystart; y < yend; y++) + for (int x = xstart; x < xend; x++) { + bufexpfin->L[y - ystart][x - xstart] = data[(y - ystart) * bfw + x - xstart]; + } + + delete [] datain; + delete [] data; + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + origblur->L[y][x] = original->L[y + ystart][x + xstart]; + origblur->a[y][x] = original->a[y + ystart][x + xstart]; + origblur->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + gaussianBlur(origblur->L, origblur->L, bfw, bfh, radius); + gaussianBlur(origblur->a, origblur->a, bfw, bfh, radius); + gaussianBlur(origblur->b, origblur->b, bfw, bfh, radius); + + } + + + //choice between original and mask + const LabImage *maskptr = usemaskall ? origblurmask.get() : origblur.get(); + + //parameters deltaE + const float mindE = 2.f + MINSCOPE * varsens * lp.thr; + const float maxdE = 5.f + MAXSCOPE * varsens * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ +// float atan2Buffer[transformed->W] ALIGNED16;//keep in case of +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < bfh; y++) { + + const int loy = y + ystart + cy; +#ifdef __SSE2__ + /* //keep in case of + int i = 0; + + for (; i < bfw - 3; i += 4) { + vfloat av = LVFU(maskptr->a[y][i]); + vfloat bv = LVFU(maskptr->b[y][i]); + STVFU(atan2Buffer[i], xatan2f(bv, av)); + } + + for (; i < bfw; i++) { + atan2Buffer[i] = xatan2f(maskptr->b[y][i], maskptr->a[y][i]); + } + */ +#endif + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + //calculate transition + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + +// float hueh = 0; +#ifdef __SSE2__ +// hueh = atan2Buffer[x]; +#else +// hueh = xatan2f(maskptr->b[y][x], maskptr->a[y][x]); +#endif + + float rsob = 0.f; + + //calculate additive sobel to deltaE + if (blend2 && ((senstype == 1 && lp.struexp > 0.f) || ((senstype == 0) && lp.struco > 0.f))) { + const float csob = xlogf(1.f + rtengine::min(blend2[y][x] / 100.f, 60.f) + 0.001f); + + float rs; + + if (k) { + rs = sobelref / csob; + } else { + rs = csob / sobelref; + } + + if (rs > 0.f && senstype == 1) { + rsob = 1.1f * lp.struexp * rs; + } else if (rs > 0.f && (senstype == 0)) { + rsob = 1.1f * lp.struco * rs; + } + } + + //deltaE + float abdelta2 = SQR(refa - maskptr->a[y][x]) + SQR(refb - maskptr->b[y][x]); + float chrodelta2 = SQR(std::sqrt(SQR(maskptr->a[y][x]) + SQR(maskptr->b[y][x])) - (chromaref * 327.68f)); + float huedelta2 = abdelta2 - chrodelta2; + + const float dE = rsob + std::sqrt(kab * (kch * chrodelta2 + kH * huedelta2) + kL * SQR(refL - maskptr->L[y][x])); + //reduction action with deltaE + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, varsens); + + float cli = (bufexpfin->L[y][x] - bufexporig->L[y][x]); + float cla = (bufexpfin->a[y][x] - bufexporig->a[y][x]); + float clb = (bufexpfin->b[y][x] - bufexporig->b[y][x]); + + if (delt) { + cli = bufexpfin->L[y][x] - original->L[y + ystart][x + xstart]; + cla = bufexpfin->a[y][x] - original->a[y + ystart][x + xstart]; + clb = bufexpfin->b[y][x] - original->b[y + ystart][x + xstart]; + } + if(lp.blwh) { + cla = 0.f; + clb = 0.f; + } + + // const float previewint = settings->previewselection; + + const float realstrdE = reducdE * cli; + const float realstradE = reducdE * cla; + const float realstrbdE = reducdE * clb; + + float factorx = localFactor; + + if (zone > 0) { + //simplified transformed with deltaE and transition + transformed->L[y + ystart][x + xstart] = clipLoc(original->L[y + ystart][x + xstart] + factorx * realstrdE); + float diflc = factorx * realstrdE; + transformed->a[y + ystart][x + xstart] = clipC(original->a[y + ystart][x + xstart] + factorx * realstradE); + const float difa = factorx * realstradE; + transformed->b[y + ystart][x + xstart] = clipC(original->b[y + ystart][x + xstart] + factorx * realstrbdE); + const float difb = factorx * realstrbdE; + float maxdifab = rtengine::max(std::fabs(difa), std::fabs(difb)); + + if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow) && lp.colorde < 0) { //show modifications with use "b" + // (origshow && lp.colorde < 0) { //original Retinex + transformed->a[y + ystart][x + xstart] = 0.f; + transformed->b[y + ystart][x + xstart] = ampli * 8.f * diflc * reducdE; + transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); + + } else if ((expshow || vibshow || colshow || SHshow || tmshow || lcshow || origshow) && lp.colorde > 0) {//show modifications without use "b" + if (diflc < 1000.f) {//if too low to be view use ab + diflc += 0.5f * maxdifab; + } + + transformed->L[y + ystart][x + xstart] = CLIP(12000.f + 0.5f * ampli * diflc); + transformed->a[y + ystart][x + xstart] = clipC(ampli * difa); + transformed->b[y + ystart][x + xstart] = clipC(ampli * difb); + } else if (previewexp || previewvib || previewcol || previewSH || previewtm || previewlc || previeworig || lp.prevdE) {//show deltaE + float difbdisp = reducdE * 10000.f * lp.colorde; + + if (transformed->L[y + ystart][x + xstart] < darklim) { //enhance dark luminance as user can see! + float dark = transformed->L[y + ystart][x + xstart]; + transformed->L[y + ystart][x + xstart] = dark * aadark + bbdark; + } + + if (lp.colorde <= 0) { + transformed->a[y + ystart][x + xstart] = 0.f; + transformed->b[y + ystart][x + xstart] = difbdisp; + } else { + transformed->a[y + ystart][x + xstart] = -difbdisp; + transformed->b[y + ystart][x + xstart] = 0.f; + } + } + } + } + } + } +} + + + + +void ImProcFunctions::exposure_pde(float * dataor, float * datain, float * dataout, int bfw, int bfh, float thresh, float mod) +/* Jacques Desmis July 2019 +** adapted from Ipol Copyright 2009-2011 IPOL Image Processing On Line http://www.ipol.im/ +*/ +{ + + BENCHFUN +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads(omp_get_max_threads()); + } + +#endif + float *data_fft, *data_tmp, *data; + + if (NULL == (data_tmp = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, bfw, bfh, thresh); + + if (NULL == (data_fft = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + if (NULL == (data = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + const auto dct_fw = fftwf_plan_r2r_2d(bfh, bfw, data_tmp, data_fft, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_fw); + + fftwf_free(data_tmp); + + /* solve the Poisson PDE in Fourier space */ + /* 1. / (float) (bfw * bfh)) is the DCT normalisation term, see libfftw */ + ImProcFunctions::rex_poisson_dct(data_fft, bfw, bfh, 1. / (double)(bfw * bfh)); + + const auto dct_bw = fftwf_plan_r2r_2d(bfh, bfw, data_fft, data, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE | FFTW_DESTROY_INPUT); + fftwf_execute(dct_bw); + fftwf_destroy_plan(dct_fw); + fftwf_destroy_plan(dct_bw); + fftwf_free(data_fft); + fftwf_cleanup(); + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_cleanup_threads(); + } +#endif + + normalize_mean_dt(data, dataor, bfw * bfh, mod, 1.f); + { + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + dataout[y * bfw + x] = clipLoc(data[y * bfw + x]); + } + } + } + + fftwf_free(data); +} + +void ImProcFunctions::fftw_convol_blur(float * input, float * output, int bfw, int bfh, float radius, int fftkern, int algo) +{ + /* + ** Jacques Desmis june 2019 - inspired by Copyright 2013 IPOL Image Processing On Line http://www.ipol.im/ + ** when I read documentation on various FFT blur we found 2 possibilities + ** 0) kernel gauss is used with "normal" data + ** 1) kernel gauss is used with FFT + ** fftkern allows to change 0) or 1) and test It seems the good solution is with 0, but I keep the code in case of ?? + + ** input real data to blur + ** output real data blurred with radius + ** bfw bfh width and high area + ** radius = sigma for kernel + ** n_x n_y relative width and high for kernel + ** Gaussian blur is given by G(x,y) = (1/2*PI*sigma) * exp(-(x2 + y2) / 2* sigma2) + ** its traduction in Fourier transform is G(x,y) = exp((-sigma)*(PI * x2 + PI * y2)), for some authors it is not sigma but sigma^2..I have tried...huge differences with Gaussianblur + ** after several test the only result that works very well is with fftkern = 0 and algo = 0, and as there is differences with Gaussianblur, I put an empirical correction in Ipretinex and Iplocalcontrast + ** you can enabled or disabled this function with rtsettings.fftwsigma in options. By default empirical formula is disabled + ** in fact no importance....if it is this function (for sigma) or another... we are not in research :) + */ + BENCHFUN + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_init_threads(); + fftwf_plan_with_nthreads(omp_get_max_threads()); + } +#endif + + + float *out; //for FFT data + float *kern = nullptr;//for kernel gauss + float *outkern = nullptr;//for FFT kernel + fftwf_plan p; + fftwf_plan pkern;//plan for FFT + int image_size, image_sizechange; + float n_x = 1.f; + float n_y = 1.f;//relative coordinates for kernel Gauss + float radsig = 1.f; + + out = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT + + if (fftkern == 1) { //allocate memory FFT if kernel fft = 1 + // kern = new float[bfw * bfh]; + kern = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT + outkern = (float*) fftwf_malloc(sizeof(float) * (bfw * bfh));//allocate real data for FFT + } + + /*compute the Fourier transform of the input data*/ + + p = fftwf_plan_r2r_2d(bfh, bfw, input, out, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE);//FFT 2 dimensions forward FFTW_MEASURE FFTW_ESTIMATE + + fftwf_execute(p); + fftwf_destroy_plan(p); + + /*define the gaussian constants for the convolution kernel*/ + if (algo == 0) { + n_x = rtengine::RT_PI / (double) bfw; //ipol + n_y = rtengine::RT_PI / (double) bfh; + } else if (algo == 1) { + n_x = 1.f / bfw; //gauss + n_y = 1.f / bfh; + radsig = 1.f / (2.f * rtengine::RT_PI * radius * radius);//gauss + } + + n_x = n_x * n_x; + n_y = n_y * n_y; + + image_size = bfw * bfh; + image_sizechange = 4 * image_size; + + if (fftkern == 1) { //convolution with FFT kernel +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) + if (algo == 0) { + kern[ i + index] = exp((float)(-radius) * (n_x * i * i + n_y * j * j)); //calculate Gauss kernel Ipol formula + } else if (algo == 1) { + kern[ i + index] = radsig * exp((float)(-(n_x * i * i + n_y * j * j) / (2.f * radius * radius))); //calculate Gauss kernel with Gauss formula + } + } + + /*compute the Fourier transform of the kernel data*/ + pkern = fftwf_plan_r2r_2d(bfh, bfw, kern, outkern, FFTW_REDFT10, FFTW_REDFT10, FFTW_ESTIMATE); //FFT 2 dimensions forward + fftwf_execute(pkern); + fftwf_destroy_plan(pkern); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) { + out[i + index] *= outkern[i + index]; //apply Gauss kernel with FFT + } + } + + fftwf_free(outkern); + fftwf_free(kern); + + // delete [] kern; + + } else if (fftkern == 0) {//without FFT kernel + if (algo == 0) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) { + out[i + index] *= exp((float)(-radius) * (n_x * i * i + n_y * j * j)); //apply Gauss kernel without FFT - some authors says radius*radius but differences with Gaussianblur + } + } + } else if (algo == 1) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int j = 0; j < bfh; j++) { + int index = j * bfw; + + for (int i = 0; i < bfw; i++) { + out[i + index] *= radsig * exp((float)(-(n_x * i * i + n_y * j * j) / (2.f * radius * radius))); //calculate Gauss kernel with Gauss formula + } + } + } + } + + p = fftwf_plan_r2r_2d(bfh, bfw, out, output, FFTW_REDFT01, FFTW_REDFT01, FFTW_ESTIMATE);//FFT 2 dimensions backward + fftwf_execute(p); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int index = 0; index < image_size; index++) { //restore data + output[index] /= image_sizechange; + } + + fftwf_destroy_plan(p); + fftwf_free(out); + +#ifdef RT_FFTW3F_OMP + if (multiThread) { + fftwf_cleanup_threads(); + } +#endif +} + +void ImProcFunctions::fftw_convol_blur2(float **input2, float **output2, int bfw, int bfh, float radius, int fftkern, int algo) +{ + MyMutex::MyLock lock(*fftwMutex); + + float *input = nullptr; + + if (NULL == (input = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + + float *output = nullptr; + + if (NULL == (output = (float *) fftwf_malloc(sizeof(float) * bfw * bfh))) { + fprintf(stderr, "allocation error\n"); + abort(); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + input[y * bfw + x] = input2[y][x]; + } + } + + ImProcFunctions::fftw_convol_blur(input, output, bfw, bfh, radius, fftkern, algo); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + output2[y][x] = output[y * bfw + x]; + } + } + + fftwf_free(input); + fftwf_free(output); +} + + +void ImProcFunctions::fftw_tile_blur(int GW, int GH, int tilssize, int max_numblox_W, int min_numblox_W, float **tmp1, int numThreads, double radius) +{ + BENCHFUN + float epsil = 0.001f / (tilssize * tilssize); + fftwf_plan plan_forward_blox[2]; + fftwf_plan plan_backward_blox[2]; + + array2D tilemask_in(tilssize, tilssize); + array2D tilemask_out(tilssize, tilssize); + + float *Lbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + float *fLbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + + int nfwd[2] = {tilssize, tilssize}; + + //for DCT: + fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; + fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; + + // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit + plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, tilssize * tilssize, fLbloxtmp, nullptr, 1, tilssize * tilssize, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, tilssize * tilssize, Lbloxtmp, nullptr, 1, tilssize * tilssize, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, tilssize * tilssize, fLbloxtmp, nullptr, 1, tilssize * tilssize, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, tilssize * tilssize, Lbloxtmp, nullptr, 1, tilssize * tilssize, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + fftwf_free(Lbloxtmp); + fftwf_free(fLbloxtmp); + const int border = rtengine::max(2, tilssize / 16); + + for (int i = 0; i < tilssize; ++i) { + float i1 = abs((i > tilssize / 2 ? i - tilssize + 1 : i)); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + + for (int j = 0; j < tilssize; ++j) { + float j1 = abs((j > tilssize / 2 ? j - tilssize + 1 : j)); + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsil; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsil; + + } + } + + float *LbloxArray[numThreads]; + float *fLbloxArray[numThreads]; + + const int numblox_W = ceil((static_cast(GW)) / offset) + 2; + const int numblox_H = ceil((static_cast(GH)) / offset) + 2; + + array2D Lresult(GW, GH, ARRAY2D_CLEAR_DATA); + array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks + + for (int i = 0; i < numThreads; ++i) { + LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * tilssize * tilssize * sizeof(float))); + } + +#ifdef _OPENMP + int masterThread = omp_get_thread_num(); +#endif +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + int subThread = masterThread * 1 + omp_get_thread_num(); +#else + int subThread = 0; +#endif + float *Lblox = LbloxArray[subThread]; + float *fLblox = fLbloxArray[subThread]; + float pBuf[GW + tilssize + 2 * offset] ALIGNED16; +#ifdef _OPENMP + #pragma omp for +#endif + for (int vblk = 0; vblk < numblox_H; ++vblk) { + + int top = (vblk - 1) * offset; + float * datarow = pBuf + offset; + + for (int i = 0; i < tilssize; ++i) { + int row = top + i; + int rr = row; + + if (row < 0) { + rr = rtengine::min(-row, GH - 1); + } else if (row >= GH) { + rr = rtengine::max(0, 2 * GH - 2 - row); + } + + for (int j = 0; j < GW; ++j) { + datarow[j] = (tmp1[rr][j]); + } + + for (int j = -1 * offset; j < 0; ++j) { + datarow[j] = datarow[rtengine::min(-j, GW - 1)]; + } + + for (int j = GW; j < GW + tilssize + offset; ++j) { + datarow[j] = datarow[rtengine::max(0, 2 * GW - 2 - j)]; + }//now we have a padded data row + + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - 1) * offset; + int indx = (hblk) * tilssize; //index of block in malloc + + if (top + i >= 0 && top + i < GH) { + int j; + + for (j = 0; j < rtengine::min((-left), tilssize); ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + + for (; j < rtengine::min(tilssize, GW - left); ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; + } + + for (; j < tilssize; ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } else { + for (int j = 0; j < tilssize; ++j) { + Lblox[(indx + i)*tilssize + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } + + } + + }//end of filling block row + + //fftwf_print_plan (plan_forward_blox); + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles + } else { + fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles + } + + const float n_xy = rtengine::SQR(rtengine::RT_PI / tilssize); + + //radius = 30.f; + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int blkstart = hblk * tilssize * tilssize; + + for (int j = 0; j < tilssize; j++) { + int index = j * tilssize; + + for (int i = 0; i < tilssize; i++) { + fLblox[blkstart + index + i] *= exp((float)(-radius) * (n_xy * rtengine::SQR(i) + n_xy * rtengine::SQR(j))); + } + } + }//end of horizontal block loop + + //now perform inverse FT of an entire row of blocks + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT + } else { + fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT + } + + int topproc = (vblk - 1) * offset; + const int numblox_W = ceil((static_cast(GW)) / offset); + const float DCTnorm = 1.0f / (4 * tilssize * tilssize); //for DCT + + int imin = rtengine::max(0, - topproc); + int bottom = rtengine::min(topproc + tilssize, GH); + int imax = bottom - topproc; + + for (int i = imin; i < imax; ++i) { + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - 1) * offset; + int right = rtengine::min(left + tilssize, GW); + int jmin = rtengine::max(0, -left); + int jmax = right - left; + int indx = hblk * tilssize; + + for (int j = jmin; j < jmax; ++j) { + Lresult[topproc + i][left + j] += tilemask_out[i][j] * Lblox[(indx + i) * tilssize + j] * DCTnorm; //for DCT + } + } + } + }//end of vertical block loop + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + tmp1[i][j] = Lresult[i][j] / totwt[i][j]; + tmp1[i][j] = clipLoc(tmp1[i][j]); + } + } + + for (int i = 0; i < numThreads; ++i) { + fftwf_free(LbloxArray[i]); + fftwf_free(fLbloxArray[i]); + } + + fftwf_destroy_plan(plan_forward_blox[0]); + fftwf_destroy_plan(plan_backward_blox[0]); + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + fftwf_cleanup(); +} + +void ImProcFunctions::wavcbd(wavelet_decomposition &wdspot, int level_bl, int maxlvl, + const LocwavCurve& locconwavCurve, bool locconwavutili, float sigm, float offs, float chromalev, int sk) +{ + if (locconwavCurve && locconwavutili) { + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; +#endif + Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + const int W_L = wdspot.level_W(level); + const int H_L = wdspot.level_H(level); + float mea[9]; + + float* const* wav_L = wdspot.level_coeffs(level); + //offset + float rap = offs * mean[level] - 2.f * sigm * sigma[level]; + + if (rap > 0.f) { + mea[0] = rap; + } else { + mea[0] = mean[level] / 6.f; + } + + rap = offs * mean[level] - sigm * sigma[level]; + + if (rap > 0.f) { + mea[1] = rap; + } else { + mea[1] = mean[level] / 2.f; + } + + mea[2] = offs * mean[level]; // 50% data + mea[3] = offs * mean[level] + sigm * sigma[level] / 2.f; + mea[4] = offs * mean[level] + sigm * sigma[level]; //66% + mea[5] = offs * mean[level] + sigm * 1.2f * sigma[level]; + mea[6] = offs * mean[level] + sigm * 1.5f * sigma[level]; // + mea[7] = offs * mean[level] + sigm * 2.f * sigma[level]; //95% + mea[8] = offs * mean[level] + sigm * 2.5f * sigma[level]; //99% + + float cpMul = 200.f * (locconwavCurve[level * 55.5f] - 0.5f); + + if (cpMul > 0.f) { + cpMul *= 3.5f; + } + + cpMul /= sk; + + for (int i = 0; i < W_L * H_L; i++) { + const float WavCL = std::fabs(wav_L[dir][i]); + float beta; + + //reduction amplification: max action between mean / 2 and mean + sigma + // arbitrary coefficient, we can add a slider !! + if (WavCL < mea[0]) { + beta = 0.6f; //preserve very low contrast (sky...) + } else if (WavCL < mea[1]) { + beta = 0.8f; + } else if (WavCL < mea[2]) { + beta = 1.f; //standard + } else if (WavCL < mea[3]) { + beta = 1.f; + } else if (WavCL < mea[4]) { + beta = 0.8f; //+sigma + } else if (WavCL < mea[5]) { + beta = 0.6f; + } else if (WavCL < mea[6]) { + beta = 0.4f; + } else if (WavCL < mea[7]) { + beta = 0.2f; // + 2 sigma + } else if (WavCL < mea[8]) { + beta = 0.1f; + } else { + beta = 0.0f; + } + + const float alpha = rtengine::max((1024.f + 15.f * cpMul * beta) / 1024.f, 0.02f) ; + wav_L[dir][i] *= alpha * chromalev; + } + } + } + } +} + +void ImProcFunctions::Compresslevels(float **Source, int W_L, int H_L, float compression, float detailattenuator, float thres, float mean, float maxp, float meanN, float maxN, float madL) +{ + //J.Desmis 12-2019 + + float exponent; + + if (detailattenuator > 0.f && detailattenuator < 0.05f) { + const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; //0.69315 = log(2) + exponent = 1.2f * xlogf(-betemp); + exponent /= 20.f; + } else if (detailattenuator >= 0.05f && detailattenuator < 0.25f) { + const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; + exponent = 1.2f * xlogf(-betemp); + exponent /= (-75.f * detailattenuator + 23.75f); + } else if (detailattenuator >= 0.25f) { + const float betemp = expf(-(2.f - detailattenuator + 0.693147f)) - 1.f; + exponent = 1.2f * xlogf(-betemp); + exponent /= (-2.f * detailattenuator + 5.5f); + } else { + exponent = (compression - 1.0f) / 20.f; + } + + float ap = (thres - 1.f) / (maxp - mean); + float bp = 1.f - ap * mean; + ap *= exponent; + bp *= exponent; + + float a0 = (1.33f * thres - 1.f) / (1.f - mean); + float b0 = 1.f - a0 * mean; + a0 *= exponent; + b0 *= exponent; + + float apn = (thres - 1.f) / (maxN - meanN); + float bpn = 1.f - apn * meanN; + apn *= -exponent; + bpn *= exponent; + + float a0n = (1.33f * thres - 1.f) / (1.f - meanN); + float b0n = 1.f - a0n * meanN; + a0n *= -exponent; + b0n *= exponent; + + madL *= 0.05f; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + const vfloat apv = F2V(ap); + const vfloat bpv = F2V(bp); + const vfloat a0v = F2V(a0); + const vfloat b0v = F2V(b0); + const vfloat apnv = F2V(apn); + const vfloat bpnv = F2V(bpn); + const vfloat a0nv = F2V(a0n); + const vfloat b0nv = F2V(b0n); + const vfloat madLv = F2V(madL); + const vfloat meanv = F2V(mean); + const vfloat onev = F2V(1.f); +#endif +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < H_L; y++) { + int x = 0; +#ifdef __SSE2__ + for (; x < W_L - 3; x += 4) { + vfloat exponev = onev; + vfloat valv = LVFU(Source[y][x]); + const vmask mask1v = vmaskf_ge(valv, ZEROV); + const vmask mask2v = vmaskf_gt(vself(mask1v, valv, -valv), meanv); + const vfloat av = vself(mask2v, vself(mask1v, apv, apnv), vself(mask1v, a0v, a0nv)); + const vfloat bv = vself(mask2v, vself(mask1v, bpv, bpnv), vself(mask1v, b0v, b0nv)); + exponev += av * valv + bv; + valv = vself(mask1v, valv, -valv); + const vfloat multv = vself(mask1v, onev, -onev); + const vfloat resultv = multv * xexpf(xlogf(valv + madLv) * exponev); + STVFU(Source[y][x], resultv); + } +#endif + for (; x < W_L; x++) { + float expone = 1.f; + + if (Source[y][x] >= 0.f) { + if (Source[y][x] > mean) { + expone += ap * Source[y][x] + bp; + } else { + expone += a0 * Source[y][x] + b0; + } + + Source[y][x] = xexpf(xlogf(Source[y][x] + madL) * expone); + } else { + if (-Source[y][x] > mean) { + expone += apn * Source[y][x] + bpn; + } else { + expone += a0n * Source[y][x] + b0n; + } + + Source[y][x] = -xexpf(xlogf(-Source[y][x] + madL) * expone); + } + } + } + } +} + +void ImProcFunctions::wavcont(const struct local_params& lp, float ** tmp, wavelet_decomposition& wdspot, int level_bl, int maxlvl, + const LocwavCurve & loclevwavCurve, bool loclevwavutili, + const LocwavCurve & loccompwavCurve, bool loccompwavutili, + const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, + float radlevblur, int process, float chromablu, float thres, float sigmadc, float deltad) +{ + BENCHFUN + const int W_L = wdspot.level_W(0); + const int H_L = wdspot.level_H(0); + + const std::unique_ptr beta(new float[W_L * H_L]); + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; +#endif + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + + if (process == 1 && loclevwavCurve && loclevwavutili) { //blur + array2D templevel(W_L, H_L); + for (int dir = 1; dir < 4; ++dir) { + for (int level = level_bl; level < maxlvl; ++level) { + const auto WavL = wdspot.level_coeffs(level)[dir]; + const float effect = lp.sigmabl; + constexpr float offs = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offs); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(WavL[co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.5f; + } else if (WavCL < mea[7]) { + beta[co] = 0.3f; + } else if (WavCL < mea[8]) { + beta[co] = 0.2f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.1f; + } else { + beta[co] = 0.05f; + } + } + + const float klev = 0.25f * loclevwavCurve[level * 55.5f]; + float* src[H_L]; + for (int i = 0; i < H_L; ++i) { + src[i] = &wdspot.level_coeffs(level)[dir][i * W_L]; + } +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(src, templevel, W_L, H_L, radlevblur * klev * chromablu); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + int j = y * W_L + x; + WavL[j] = intp(beta[j], templevel[y][x], WavL[j]); + } + } + } + } + } else if (process == 2 && loccompwavCurve && loccompwavutili) { //Directional contrast + for (int dir = 1; dir < 4; ++dir) { + for (int level = level_bl; level < maxlvl; ++level) { + const auto WavL = wdspot.level_coeffs(level)[dir]; + const float effect = sigmadc; + constexpr float offs = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offs); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(WavL[co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.7f; + } else if (WavCL < mea[7]) { + beta[co] = 0.5f; + } else if (WavCL < mea[8]) { + beta[co] = 0.3f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.2f; + } else { + beta[co] = 0.1f; + } + } + + const int iteration = deltad; + const int itplus = 7 + iteration; + const int itmoins = 7 - iteration; + const int med = maxlvl / 2; + int it; + + if (level < med) { + it = itmoins; + } else if (level == med) { + it = 7; + } else { + it = itplus; + } + + const float itf = it; + const float factor = dir < 3 ? 0.3f : -0.6f; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + const vfloat c327d68v = F2V(327.68f); + const vfloat factorv = F2V(factor); + const vfloat sixv = F2V(6.f); + const vfloat zd5v = F2V(0.5f); + const vfloat onev = F2V(1.f); + const vfloat itfv = F2V(itf); +#endif +#ifdef _OPENMP + #pragma omp for +#endif + for (int i = 0; i < H_L; ++i) { + int j = 0; +#ifdef __SSE2__ + for (; j < W_L - 3; j += 4) { + const vfloat LL100v = LC2VFU(tmp[i * 2][j * 2]) / c327d68v; + const vfloat kbav = factorv * (loccompwavCurve[sixv * LL100v] - zd5v); //k1 between 0 and 0.5 0.5==> 1/6=0.16 + STVFU(WavL[i * W_L + j], LVFU(WavL[i * W_L + j]) * pow_F(onev + kbav * LVFU(beta[i * W_L + j]), itfv)); + } +#endif + for (; j < W_L; ++j) { + const float LL100 = tmp[i * 2][j * 2] / 327.68f; + const float kba = factor * (loccompwavCurve[6.f * LL100] - 0.5f); //k1 between 0 and 0.5 0.5==> 1/6=0.16 + WavL[i * W_L + j] *= pow_F(1.f + kba * beta[i * W_L + j], itf); + } + } + } + } + } + } else if (process == 3 && loccomprewavCurve && loccomprewavutili) { //Dynamic compression wavelet + float madL[10][3]; + array2D templevel(W_L, H_L); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + const int W_L = wdspot.level_W(level); + const int H_L = wdspot.level_H(level); + const auto wav_L = wdspot.level_coeffs(level)[dir]; + madL[level][dir - 1] = Mad(wav_L, W_L * H_L);//evaluate noise by level + } + } + + for (int dir = 1; dir < 4; ++dir) { + for (int level = level_bl; level < maxlvl; ++level) { + const auto WavL = wdspot.level_coeffs(level)[dir]; + const float effect = lp.sigmadr; + constexpr float offs = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offs); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(WavL[co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.65f; + } else if (WavCL < mea[7]) { + beta[co] = 0.5f; + } else if (WavCL < mea[8]) { + beta[co] = 0.4f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.25f; + } else { + beta[co] = 0.1f; + } + } + + float klev = (loccomprewavCurve[level * 55.5f] - 0.75f); + if (klev < 0.f) { + klev *= 2.6666f;//compression increase contraste + } else { + klev *= 4.f;//dilatation reduce contraste - detailattenuator + } + const float compression = expf(-klev); + const float detailattenuator = std::max(klev, 0.f); + + const auto wav_L = wdspot.level_coeffs(level)[dir]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + int j = y * W_L + x; + templevel[y][x] = wav_L[j]; + } + } + + Compresslevels(templevel, W_L, H_L, compression, detailattenuator, thres, mean[level], MaxP[level], meanN[level], MaxN[level], madL[level][dir - 1]); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + int j = y * W_L + x; + wav_L[j] = intp(beta[j], templevel[y][x], wav_L[j]); + } + } + } + } + } +} + + +void ImProcFunctions::wavcontrast4(struct local_params& lp, float ** tmp, float ** tmpa, float ** tmpb, float contrast, float radblur, float radlevblur, int bfw, int bfh, int level_bl, int level_hl, int level_br, int level_hr, int sk, int numThreads, + const LocwavCurve & locwavCurve, bool locwavutili, bool wavcurve, const LocwavCurve& loclevwavCurve, bool loclevwavutili, bool wavcurvelev, + const LocwavCurve & locconwavCurve, bool locconwavutili, bool wavcurvecon, + const LocwavCurve & loccompwavCurve, bool loccompwavutili, bool wavcurvecomp, + const LocwavCurve & loccomprewavCurve, bool loccomprewavutili, bool wavcurvecompre, + const LocwavCurve & locedgwavCurve, bool locedgwavutili, + float sigm, float offs, int & maxlvl, float sigmadc, float deltad, float chromalev, float chromablu, bool blurlc, bool blurena, bool levelena, bool comprena, bool compreena, float compress, float thres) +{ +BENCHFUN + wavelet_decomposition *wdspot = new wavelet_decomposition(tmp[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + + //first decomposition for compress dynamic range positive values and other process + if (wdspot->memory_allocation_failed()) { + return; + } + + struct grad_params gpwav; + + maxlvl = wdspot->maxlevel(); + + int W_Lm = wdspot->level_W(maxlvl - 1); //I assume all decomposition have same W and H + + int H_Lm = wdspot->level_H(maxlvl - 1); + + if (lp.strwav != 0.f && lp.wavgradl) { + array2D factorwav(W_Lm, H_Lm); + calclocalGradientParams(lp, gpwav, 0, 0, W_Lm, H_Lm, 10); + + + for (int y = 0; y < H_Lm; y++) { + for (int x = 0; x < W_Lm; x++) { + float factor = ImProcFunctions::calcGradientFactor(gpwav, x, y); + factorwav[y][x] = factor; + factorwav[y][x] = 1.f - factorwav[y][x]; + + if (lp.strwav < 0.f) { + factorwav[y][x] *= -1.f; + } + } + } + + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float alowg = 1.f; + float blowg = 0.f; + + if (level_hl != level_bl) { + alowg = 1.f / (level_hl - level_bl); + blowg = -alowg * level_bl; + } + + float ahighg = 1.f; + float bhighg = 0.f; + + if (level_hr != level_br) { + ahighg = 1.f / (level_hr - level_br); + bhighg = -ahighg * level_br; + } + + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + const int W_L = wdspot->level_W(level); + const int H_L = wdspot->level_H(level); + float* const* wav_L = wdspot->level_coeffs(level); + const float effect = lp.sigmalc2; + constexpr float offset = 1.f; + float mea[10]; + calceffect(level, mean, sigma, mea, effect, offset); + constexpr float insigma = 0.666f; //SD + const float logmax = std::log(MaxP[level]); //log Max + const float rapX = (mean[level] + lp.sigmalc2 * sigma[level]) / MaxP[level]; //rapport between sD / max + const float inx = std::log(insigma); + const float iny = std::log(rapX); + const float rap = inx / iny; //koef + const float asig = 0.166f / (sigma[level] * lp.sigmalc2); + const float bsig = 0.5f - asig * mean[level]; + const float amean = 0.5f / mean[level]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + const float WavCL = std::fabs(wav_L[dir][y * W_L + x]); + float beta; + + if (WavCL < mea[0]) { + beta = 0.05f; + } else if (WavCL < mea[1]) { + beta = 0.2f; + } else if (WavCL < mea[2]) { + beta = 0.7f; + } else if (WavCL < mea[3]) { + beta = 1.f; //standard + } else if (WavCL < mea[4]) { + beta = 1.f; + } else if (WavCL < mea[5]) { + beta = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta = 0.6f; + } else if (WavCL < mea[7]) { + beta = 0.5f; + } else if (WavCL < mea[8]) { + beta = 0.4f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta = 0.3f; + } else { + beta = 0.1f; + } + + float absciss; + float &val = wav_L[dir][y * W_L + x]; + + if (std::fabs(val) >= (mean[level] + lp.sigmalc2 * sigma[level])) { //for max + const float valc = xlogf(std::fabs(val)) - logmax; + absciss = xexpf(valc * rap); + } else if (std::fabs(val) >= mean[level]) { + absciss = asig * std::fabs(val) + bsig; + } else { + absciss = amean * std::fabs(val); + } + + float klev = 1.f; + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alowg * level + blowg; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahighg * level + bhighg; + } + } + + const float kc = 0.8f * klev * factorwav[y][x] * absciss; + const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; + + float kinterm = 1.f + reduceeffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + val *= (1.f + (kinterm - 1.f) * beta); + } + } + } + } + } + } + + //declare a and b if need + wavelet_decomposition *wdspota = nullptr; + wavelet_decomposition *wdspotb = nullptr; + + int W_L = wdspot->level_W(0); + int H_L = wdspot->level_H(0); + float *wav_L0 = wdspot->get_coeff0(); + + if (radblur > 0.f && blurena) { + array2D bufl(W_L, H_L); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + bufl[y][x] = wav_L0[y * W_L + x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufl, bufl, W_L, H_L, radblur); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + wav_L0[y * W_L + x] = bufl[y][x]; + } + } + } + + if (compress != 0.f && compreena) { + + float Compression = expf(-compress); + float DetailBoost = compress; + + if (compress < 0.0f) { + DetailBoost = 0.0f; + } + + CompressDR(wav_L0, W_L, H_L, Compression, DetailBoost); + + } + + if ((lp.residsha != 0.f || lp.residhi != 0.f)) { + float tran = 5.f;//transition shadow + + if (lp.residshathr > (100.f - tran)) { + tran = 100.f - lp.residshathr; + } + constexpr float alp = 3.f; + const float aalp = (1.f - alp) / lp.residshathr; + const float ath = -lp.residsha / tran; + const float bth = lp.residsha - ath * lp.residshathr; + + //highlight + const float tranh = rtengine::min(5.f, lp.residhithr); + const float athH = lp.residhi / tranh; + const float bthH = lp.residhi - athH * lp.residhithr; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + const float LL100 = wav_L0[i] / 327.68f; + + if (LL100 < lp.residshathr) { + const float kk = aalp * LL100 + alp; + wav_L0[i] *= (1.f + kk * lp.residsha / 200.f); + } else if (LL100 < lp.residshathr + tran) { + wav_L0[i] *= (1.f + (LL100 * ath + bth) / 200.f); + } + + if (LL100 > lp.residhithr) { + wav_L0[i] *= (1.f + lp.residhi / 200.f); + } else if (LL100 > (lp.residhithr - tranh)) { + wav_L0[i] *= (1.f + (LL100 * athH + bthH) / 200.f); + } + } + } + + if (contrast != 0.) { + + double avedbl = 0.0; // use double precision for large summations + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:avedbl) if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + avedbl += wav_L0[i]; + } + + float ave = avedbl / double(W_L * H_L); + + float avg = ave / 32768.f; + avg = LIM01(avg); + double contreal = 0.6 * contrast; + DiagonalCurve resid_contrast({ + DCT_NURBS, + 0, 0, + avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0), + avg + (1. - avg) * (0.6 - contreal / 250.0), avg + (1. - avg) * (0.6 + contreal / 250.0), + 1, 1 + }); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + float buf = LIM01(wav_L0[i] / 32768.f); + buf = resid_contrast.getVal(buf); + buf *= 32768.f; + wav_L0[i] = buf; + } + + } + + float alow = 1.f; + float blow = 0.f; + + if (level_hl != level_bl) { + alow = 1.f / (level_hl - level_bl); + blow = -alow * level_bl; + } + + float ahigh = 1.f; + float bhigh = 0.f; + + if (level_hr != level_br) { + ahigh = 1.f / (level_hr - level_br); + bhigh = -ahigh * level_br; + } + + if (wavcurvelev || wavcurvecomp || wavcurvecompre) {//compress dynamic and blur + if (wavcurvelev && radlevblur > 0.f && blurena) { + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, 1.f, 0.f, 0.f, 0.f); + } + + if (wavcurvecomp && comprena) { + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 2, 1.f, 0.f, sigmadc, deltad); + } + + if (wavcurvecompre && compreena) { + wavcont(lp, tmp, *wdspot, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 3, 1.f, thres, 0.f, 0.f); + } + } + + if (wavcurvecon && levelena) {//contrast by levels for luminance + wavcbd(*wdspot, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, 1.f, sk); + } + +//edge sharpness begin + if (lp.edgwena && level_bl == 0 && level_br >= 3 && locedgwavCurve && locedgwavutili && lp.strengthw > 0) { //needs the first levels to work! + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float edd = 3.f; + float eddlow = 15.f; + float eddlipinfl = 0.005f * lp.edgw + 0.4f; + float eddlipampl = 1.f + lp.basew / 50.f; + int W_L = wdspot->level_W(0);//provisory W_L H_L + int H_L = wdspot->level_H(0); + float *koeLi[12]; + float maxkoeLi[12] = {0.f}; + float *beta = new float[W_L * H_L]; + + float *koeLibuffer = new float[12 * H_L * W_L]; //12 + + for (int i = 0; i < 12; i++) { + koeLi[i] = &koeLibuffer[i * W_L * H_L]; + } + + for (int j = 0; j < 12; j++) { + for (int i = 0; i < W_L * H_L; i++) { + koeLi[j][i] = 0.f; + } + } + + array2D tmC(W_L, H_L); + + float gradw = lp.gradw; + float tloww = lp.tloww; +//StopWatch Stop1("test"); + for (int lvl = 0; lvl < 4; lvl++) { + for (int dir = 1; dir < 4; dir++) { + const int W_L = wdspot->level_W(lvl); + const int H_L = wdspot->level_H(lvl); + float* const* wav_L = wdspot->level_coeffs(lvl); + if (lvl == 3 && dir == 3) { + const float effect = lp.sigmaed; + constexpr float offset = 1.f; + float mea[10]; + calceffect(lvl, mean, sigma, mea, effect, offset); + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int co = 0; co < H_L * W_L; co++) { + const float WavCL = std::fabs(wav_L[dir][co]); + + if (WavCL < mea[0]) { + beta[co] = 0.05f; + } else if (WavCL < mea[1]) { + beta[co] = 0.2f; + } else if (WavCL < mea[2]) { + beta[co] = 0.7f; + } else if (WavCL < mea[3]) { + beta[co] = 1.f; //standard + } else if (WavCL < mea[4]) { + beta[co] = 1.f; + } else if (WavCL < mea[5]) { + beta[co] = 0.8f; //+sigma + } else if (WavCL < mea[6]) { + beta[co] = 0.5f; + } else if (WavCL < mea[7]) { + beta[co] = 0.3f; + } else if (WavCL < mea[8]) { + beta[co] = 0.2f; // + 2 sigma + } else if (WavCL < mea[9]) { + beta[co] = 0.1f; + } else { + beta[co] = 0.05f; + } + } + } + calckoe(wav_L, gradw, tloww, koeLi, lvl, dir, W_L, H_L, edd, maxkoeLi[lvl * 3 + dir - 1], tmC); + // return convolution KoeLi and maxkoeLi of level 0 1 2 3 and Dir Horiz, Vert, Diag + } + } + tmC.free(); +//Stop1.stop(); + float aamp = 1.f + lp.thigw / 100.f; + + const float alipinfl = (eddlipampl - 1.f) / (1.f - eddlipinfl); + const float blipinfl = eddlipampl - alipinfl; + + for (int lvl = 0; lvl < 4; lvl++) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + for (int i = 1; i < H_L - 1; i++) { + for (int j = 1; j < W_L - 1; j++) { + //treatment of koeLi and maxkoeLi + if (lp.lip3) {//Sobel Canny algo improve with parameters + // comparison between pixel and neighbors + const auto neigh = lp.neiwmet == 1; + const auto kneigh = neigh ? 28.f : 38.f; + const auto somm = neigh ? 40.f : 50.f; + + for (int dir = 1; dir < 4; dir++) { //neighbors proxi + koeLi[lvl * 3 + dir - 1][i * W_L + j] = (kneigh * koeLi[lvl * 3 + dir - 1][i * W_L + j] + + 2.f * koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j + 1] + 2.f * koeLi[lvl * 3 + dir - 1][i * W_L + j - 1] + + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i - 1) * W_L + j + 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j - 1] + koeLi[lvl * 3 + dir - 1][(i + 1) * W_L + j + 1]) / somm; + } + } + + float interm = 0.f; + for (int dir = 1; dir < 4; dir++) { + //here I evaluate combination of vert / diag / horiz...we are with multiplicators of the signal + interm += SQR(koeLi[lvl * 3 + dir - 1][i * W_L + j]); + } + + interm = std::sqrt(interm) * 0.57736721f; + + constexpr float eps = 0.0001f; + // I think this double ratio (alph, beta) is better than arctg + + float alph = koeLi[lvl * 3][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between horizontal and vertical + float beta = koeLi[lvl * 3 + 2][i * W_L + j] / (koeLi[lvl * 3 + 1][i * W_L + j] + eps); //ratio between diagonal and horizontal + + //alph evaluate the direction of the gradient regularity Lipschitz + // if = 1 we are on an edge + // if 0 we are not + // we can change and use log..or Arctg but why ?? we can change if need ... + //Liamp=1 for eddlipinfl + //liamp > 1 for alp >eddlipinfl and alph < 1 + //Liamp < 1 for alp < eddlipinfl and alph > 0 + if (alph > 1.f) { + alph = 1.f / alph; + } + + if (beta > 1.f) { + beta = 1.f / beta; + } + + //take into account diagonal + //if in same value OK + //if not no edge or reduction + float bet = 1.f; + + if (alph > eddlipinfl && beta < 0.85f * eddlipinfl) { //0.85 arbitrary value ==> eliminate from edge if H V D too different + bet = beta; + } + + float kampli; + if (alph > eddlipinfl) { + kampli = alipinfl * alph + blipinfl; //If beta low reduce kampli + kampli = SQR(bet) * kampli * aamp; + } else { + kampli = SQR(SQR(alph * bet)) / eddlipinfl; //Strong Reduce if beta low + kampli = kampli / aamp; + } + + + interm *= kampli; + + if (interm * eddlow < lp.tloww) { + interm = 0.01f; //eliminate too low values + } + + //we can change this part of algo==> not equal but ponderate + koeLi[lvl * 3][i * W_L + j] = koeLi[lvl * 3 + 1][i * W_L + j] = koeLi[lvl * 3 + 2][i * W_L + j] = interm; //new value + //here KoeLi contains values where gradient is high and coef high, and eliminate low values... + } + } + } + + constexpr float scales[10] = {1.f, 2.f, 4.f, 8.f, 16.f, 32.f, 64.f, 128.f, 256.f, 512.f}; + float scaleskip[10]; + + for (int sc = 0; sc < 10; sc++) { + scaleskip[sc] = scales[sc] / sk; + } + + const float rad = lp.radiusw / 60.f; //radius ==> not too high value to avoid artifacts + float value = lp.strengthw / 8.f; //strength + + if (scaleskip[1] < 1.f) { + constexpr float atten01234 = 0.80f; + value *= atten01234 * scaleskip[1]; //for zoom < 100% reduce strength...I choose level 1...but!! + } + + constexpr float lim0 = 20.f; //arbitrary limit for low radius and level between 2 or 3 to 30 maxi + float repart = lp.detailw; + + if (lp.edgwmet != 1) { + float brepart; + if (lp.edgwmet == 0) { + brepart = 3.f; + } else /*if (lp.edgwmet == 2)*/ { + brepart = 0.5f; //arbitrary value to increase / decrease repart, between 1 and 0 + } + if (rad < lim0 / 60.f) { + const float arepart = - (brepart - 1.f) / (lim0 / 60.f); + repart *= arepart * rad + brepart; //linear repartition of repart + } + } + + const float bk = 1.f + repart / 50.f; + constexpr float al10 = 1.0f; //arbitrary value ==> less = take into account high levels + const float ak = - (bk - al10) / 10.f; //10 = maximum levels + + for (int lvl = 0; lvl < maxlvl; lvl++) { + if (MaxP[lvl] > 0.f) { //curve + const int W_L = wdspot->level_W(lvl); + const int H_L = wdspot->level_H(lvl); + float* const* wav_L = wdspot->level_coeffs(lvl); + const float koef = ak * lvl + bk; //modulate for levels : more levels high, more koef low ==> concentrated action on low levels, without or near for high levels + float expkoef = -pow_F(std::fabs(rad - lvl), koef); //reduce effect for high levels + if (lp.edgwmet == 2) { + if (rad < lim0 / 60.f && lvl == 0) { + expkoef *= abs(repart); //reduce effect for low values of rad and level=0==> quasi only level 1 is effective + } + } else if (lp.edgwmet == 0) { + if (rad < lim0 / 60.f && lvl == 1) { + expkoef /= repart; //increase effect for low values of rad and level=1==> quasi only level 0 is effective + } + } + //take into account local contrast + const float refin = value * xexpf(expkoef); + const float edgePrecalc = 1.f + refin; //estimate edge "pseudo variance" + constexpr float insigma = 0.666f; //SD + const float logmax = xlogf(MaxP[lvl]); //log Max + const float rapX = (mean[lvl] + sigma[lvl]) / MaxP[lvl]; //rapport between sD / max + const float inx = xlogf(insigma); + const float iny = xlogf(rapX); + const float rap = inx / iny; //koef + const float asig = 0.166f / sigma[lvl]; + const float bsig = 0.5f - asig * mean[lvl]; + const float amean = 0.5f / mean[lvl]; + constexpr int borderL = 1; + constexpr float abssd = 4.f; //amplification reference + constexpr float bbssd = 2.f; //mini ampli + constexpr float maxamp = 2.5f; //maxi ampli at end + constexpr float maxampd = 10.f; //maxi ampli at end + constexpr float a_abssd = (maxamp - abssd) / 0.333f; + constexpr float b_abssd = maxamp - a_abssd; + constexpr float da_abssd = (maxampd - abssd) / 0.333f; + constexpr float db_abssd = maxampd - da_abssd; + constexpr float am = (abssd - bbssd) / 0.666f; + for (int dir = 1; dir < 4; dir++) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16) if(multiThread) +#endif + for (int i = borderL; i < H_L - borderL; i++) { + for (int j = borderL; j < W_L - borderL; j++) { + const int k = i * W_L + j; + + float edge; + if (lvl < 4) { + edge = 1.f + (edgePrecalc - 1.f) * (koeLi[lvl * 3][k]) / (1.f + 0.9f * maxkoeLi[lvl * 3 + dir - 1]); + } else { + edge = edgePrecalc; + } + + float absciss = 0.f; + if (std::fabs(wav_L[dir][k]) >= mean[lvl] + sigma[lvl]) { //for max + absciss = xexpf((xlogf(std::fabs(wav_L[dir][k])) - logmax) * rap); + } else if (std::fabs(wav_L[dir][k]) >= mean[lvl]) { + absciss = asig * std::fabs(wav_L[dir][k]) + bsig; + } else /*if (std::fabs(wav_L[dir][k]) < mean[lvl])*/ { + absciss = amean * std::fabs(wav_L[dir][k]); + } + + // Threshold adjuster settings==> approximative for curve + //kmul about average cbrt(3--40 / 10)==>1.5 to 2.5 + //kmul about SD 10--60 / 35 ==> 2 + // kmul about low cbrt((5.f+cp.edg_low)/5.f);==> 1.5 + // kmul about max ==> 9 + // we can change these values + // result is different not best or bad than threshold slider...but similar + float kmul; + float kmuld; + + if (absciss > 0.666f && absciss < 1.f) { + kmul = a_abssd * absciss + b_abssd; //about max ==> kinterm + kmuld = da_abssd * absciss + db_abssd; + } else { + kmul = kmuld = absciss * am + bbssd; + } + + const float kc = kmul * (locedgwavCurve[absciss * 500.f] - 0.5f); + + float kinterm; + if (kc >= 0.f) { + constexpr float reduceeffect = 0.6f; + kinterm = 1.f + reduceeffect * kc; //about 1 to 3 general and big amplification for max (under 0) + } else { + const float kcd = kmuld * (locedgwavCurve[absciss * 500.f] - 0.5f); + kinterm = 1.f - SQR(kcd) / 10.f; + } + + if (kinterm < 0.f) { + kinterm = 0.01f; + } + + edge = std::max(edge * kinterm, 1.f); + wav_L[dir][k] *= 1.f + (edge - 1.f) * beta[k]; + } + } + } + } + } + + if (koeLibuffer) { + delete [] koeLibuffer; + } + + delete[] beta; + } + +//edge sharpness end + + if (locwavCurve && locwavutili && wavcurve) {//simple local contrast in function luminance + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + Evaluate2(*wdspot, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + int W_L = wdspot->level_W(level); + int H_L = wdspot->level_H(level); + float klev = 1.f; + + if (level >= level_hl && level <= level_hr) { + klev = 1.f; + } + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alow * level + blow; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahigh * level + bhigh; + } + } + float* const* wav_L = wdspot->level_coeffs(level); + + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + constexpr float insigma = 0.666f; //SD + const float logmax = log(MaxP[level]); //log Max + const float rapX = (mean[level] + lp.sigmalc * sigma[level]) / MaxP[level]; //rapport between sD / max + const float inx = log(insigma); + const float iny = log(rapX); + const float rap = inx / iny; //koef + const float asig = 0.166f / (sigma[level] * lp.sigmalc); + const float bsig = 0.5f - asig * mean[level]; + const float amean = 0.5f / mean[level]; + const float limit1 = mean[level] + lp.sigmalc * sigma[level]; + const float limit2 = mean[level]; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 16 * W_L) if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + const float val = wav_L[dir][i]; + + float absciss; + if (std::fabs(val) >= limit1) { //for max + const float valcour = xlogf(std::fabs(val)); + absciss = xexpf((valcour - logmax) * rap); + } else if (std::fabs(val) >= limit2) { + absciss = asig * std::fabs(val) + bsig; + } else { + absciss = amean * std::fabs(val); + } + + const float kc = klev * (locwavCurve[absciss * 500.f] - 0.5f); + const float reduceeffect = kc <= 0.f ? 1.f : 1.5f; + + float kinterm = 1.f + reduceeffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + + wav_L[dir][i] *= kinterm <= 0.f ? 0.01f : kinterm; + } + } + } + } + } + + //reconstruct all for L + wdspot->reconstruct(tmp[0], 1.f); + delete wdspot; + + if (wavcurvecon && (chromalev != 1.f) && levelena) { // a and b if need ) {//contrast by levels for chroma a and b + wdspota = new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + + if (wdspota->memory_allocation_failed()) { + return; + } + + wavcbd(*wdspota, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); + wdspota->reconstruct(tmpa[0], 1.f); + delete wdspota; + + wdspotb = new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + + if (wdspotb->memory_allocation_failed()) { + return; + } + + wavcbd(*wdspotb, level_bl, maxlvl, locconwavCurve, locconwavutili, sigm, offs, chromalev, sk); + wdspotb->reconstruct(tmpb[0], 1.f); + delete wdspotb; + + } + + if (wavcurvelev && radlevblur > 0.f && blurena) {//chroma blur if need + if (!blurlc) { + // a + wdspota = new wavelet_decomposition(tmpa[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + + if (wdspota->memory_allocation_failed()) { + return; + } + + if (radlevblur > 0.f && chromablu > 0.f) { + wavcont(lp, tmp, *wdspota, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); + } + + wdspota->reconstruct(tmpa[0], 1.f); + delete wdspota; + + //b + wdspotb = new wavelet_decomposition(tmpb[0], bfw, bfh, maxlvl, 1, sk, numThreads, lp.daubLen); + + if (wdspotb->memory_allocation_failed()) { + return; + } + + if (radlevblur > 0.f && chromablu > 0.f) { + wavcont(lp, tmp, *wdspotb, level_bl, maxlvl, loclevwavCurve, loclevwavutili, loccompwavCurve, loccompwavutili, loccomprewavCurve, loccomprewavutili, radlevblur, 1, chromablu, 0.f, 0.f, 0.f); + } + + wdspotb->reconstruct(tmpb[0], 1.f); + delete wdspotb; + } + } + +} + + +void ImProcFunctions::fftw_denoise(int GW, int GH, int max_numblox_W, int min_numblox_W, float **tmp1, array2D *Lin, int numThreads, const struct local_params & lp, int chrom) +{ + BENCHFUN + + fftwf_plan plan_forward_blox[2]; + fftwf_plan plan_backward_blox[2]; + + array2D tilemask_in(TS, TS); + array2D tilemask_out(TS, TS); + + float *Lbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + float *fLbloxtmp = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + float params_Ldetail = 0.f; + + int nfwd[2] = {TS, TS}; + + //for DCT: + fftw_r2r_kind fwdkind[2] = {FFTW_REDFT10, FFTW_REDFT10}; + fftw_r2r_kind bwdkind[2] = {FFTW_REDFT01, FFTW_REDFT01}; + + // Creating the plans with FFTW_MEASURE instead of FFTW_ESTIMATE speeds up the execute a bit + plan_forward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[0] = fftwf_plan_many_r2r(2, nfwd, max_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_forward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, Lbloxtmp, nullptr, 1, TS * TS, fLbloxtmp, nullptr, 1, TS * TS, fwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + plan_backward_blox[1] = fftwf_plan_many_r2r(2, nfwd, min_numblox_W, fLbloxtmp, nullptr, 1, TS * TS, Lbloxtmp, nullptr, 1, TS * TS, bwdkind, FFTW_MEASURE | FFTW_DESTROY_INPUT); + fftwf_free(Lbloxtmp); + fftwf_free(fLbloxtmp); + const int border = rtengine::max(2, TS / 16); + + for (int i = 0; i < TS; ++i) { + float i1 = abs((i > TS / 2 ? i - TS + 1 : i)); + float vmask = (i1 < border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + float vmask2 = (i1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * i1) / (2 * border))) : 1.0f); + + for (int j = 0; j < TS; ++j) { + float j1 = abs((j > TS / 2 ? j - TS + 1 : j)); + tilemask_in[i][j] = (vmask * (j1 < border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsilonw; + tilemask_out[i][j] = (vmask2 * (j1 < 2 * border ? SQR(sin((rtengine::RT_PI_F * j1) / (2 * border))) : 1.0f)) + epsilonw; + + } + } + + + float *LbloxArray[numThreads]; + float *fLbloxArray[numThreads]; + + + + const int numblox_W = ceil((static_cast(GW)) / offset) + 2; + const int numblox_H = ceil((static_cast(GH)) / offset) + 2; + + + //residual between input and denoised L channel + array2D Ldetail(GW, GH, ARRAY2D_CLEAR_DATA); + array2D totwt(GW, GH, ARRAY2D_CLEAR_DATA); //weight for combining DCT blocks + array2D prov(GW, GH, ARRAY2D_CLEAR_DATA); + + for (int i = 0; i < numThreads; ++i) { + LbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + fLbloxArray[i] = reinterpret_cast(fftwf_malloc(max_numblox_W * TS * TS * sizeof(float))); + } + +#ifdef _OPENMP + int masterThread = omp_get_thread_num(); +#endif +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef _OPENMP + int subThread = masterThread * 1 + omp_get_thread_num(); +#else + int subThread = 0; +#endif + float *Lblox = LbloxArray[subThread]; + float *fLblox = fLbloxArray[subThread]; + float pBuf[GW + TS + 2 * offset] ALIGNED16; +#ifdef _OPENMP + #pragma omp for +#endif + for (int vblk = 0; vblk < numblox_H; ++vblk) { + + int top = (vblk - 1) * offset; + float * datarow = pBuf + offset; + + for (int i = 0; i < TS; ++i) { + int row = top + i; + int rr = row; + + if (row < 0) { + rr = rtengine::min(-row, GH - 1); + } else if (row >= GH) { + rr = rtengine::max(0, 2 * GH - 2 - row); + } + + for (int j = 0; j < GW; ++j) { + datarow[j] = ((*Lin)[rr][j] - tmp1[rr][j]); + prov[rr][j] = std::fabs(tmp1[rr][j]); + + } + + for (int j = -1 * offset; j < 0; ++j) { + datarow[j] = datarow[rtengine::min(-j, GW - 1)]; + } + + for (int j = GW; j < GW + TS + offset; ++j) { + datarow[j] = datarow[rtengine::max(0, 2 * GW - 2 - j)]; + }//now we have a padded data row + + //now fill this row of the blocks with Lab high pass data + for (int hblk = 0; hblk < numblox_W; ++hblk) { + int left = (hblk - 1) * offset; + int indx = (hblk) * TS; //index of block in malloc + + if (top + i >= 0 && top + i < GH) { + int j; + + for (j = 0; j < rtengine::min((-left), TS); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + + for (; j < rtengine::min(TS, GW - left); ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + totwt[top + i][left + j] += tilemask_in[i][j] * tilemask_out[i][j]; + } + + for (; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } else { + for (int j = 0; j < TS; ++j) { + Lblox[(indx + i)*TS + j] = tilemask_in[i][j] * datarow[left + j]; // luma data + } + } + + } + + }//end of filling block row + + //fftwf_print_plan (plan_forward_blox); + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_forward_blox[0], Lblox, fLblox); // DCT an entire row of tiles + } else { + fftwf_execute_r2r(plan_forward_blox[1], Lblox, fLblox); // DCT an entire row of tiles + } + + // now process the vblk row of blocks for noise reduction + + float noisevar_Ldetail = 1.f; + + if (chrom == 0) { + params_Ldetail = rtengine::min(float(lp.noiseldetail), 99.9f); // max out to avoid div by zero when using noisevar_Ldetail as divisor + noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); + } else if (chrom == 1) { + params_Ldetail = rtengine::min(float(lp.noisechrodetail), 99.9f); + // noisevar_Ldetail = 100.f * pow((static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f), 2);//to test ??? + noisevar_Ldetail = 100.f * pow((static_cast(SQR(100. - params_Ldetail)) * TS * 0.5f), 2);//to test ??? + } + + // float noisevar_Ldetail = SQR(static_cast(SQR(100. - params_Ldetail) + 50.*(100. - params_Ldetail)) * TS * 0.5f); + + + + for (int hblk = 0; hblk < numblox_W; ++hblk) { + ImProcFunctions::RGBtile_denoise(fLblox, hblk, noisevar_Ldetail); + + }//end of horizontal block loop + + + //now perform inverse FT of an entire row of blocks + if (numblox_W == max_numblox_W) { + fftwf_execute_r2r(plan_backward_blox[0], fLblox, Lblox); //for DCT + } else { + fftwf_execute_r2r(plan_backward_blox[1], fLblox, Lblox); //for DCT + } + + int topproc = (vblk - 1) * offset; + + //add row of blocks to output image tile + ImProcFunctions::RGBoutput_tile_row(Lblox, Ldetail, tilemask_out, GH, GW, topproc); + + }//end of vertical block loop + } + + //Threshold DCT from Alberto Grigio + const int detail_thresh = lp.detailthr; + array2D mask; + + if (detail_thresh > 0) { + mask(GW, GH); + float thr = log2lin(float(detail_thresh) / 200.f, 100.f); + buildBlendMask(prov, mask, GW, GH, thr); +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mask, mask, GW, GH, 20.0); + } + array2D m2(GW, GH); + constexpr float alfa = 0.856f; + const float beta = 1.f + std::sqrt(log2lin(thr, 100.f)); + buildGradientsMask(GW, GH, prov, m2, params_Ldetail / 100.f, 7, 3, alfa, beta, multiThread); + + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + mask[i][j] *= m2[i][j]; + } + } + } + + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + float d = Ldetail[i][j] / totwt[i][j]; + + if (detail_thresh > 0) { + d *= mask[i][j]; + } + + //may want to include masking threshold for large hipass data to preserve edges/detail + tmp1[i][j] += d; + } + } + + mask.free(); +//end Threshold DCT + + + delete Lin; + + + for (int i = 0; i < numThreads; ++i) { + fftwf_free(LbloxArray[i]); + fftwf_free(fLbloxArray[i]); + } + + fftwf_destroy_plan(plan_forward_blox[0]); + fftwf_destroy_plan(plan_backward_blox[0]); + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + fftwf_cleanup(); + + +} + +void ImProcFunctions::DeNoise(int call, int del, float * slidL, float * slida, float * slidb, int aut, bool noiscfactiv, const struct local_params & lp, LabImage * originalmaskbl, int levred, float huerefblur, float lumarefblur, float chromarefblur, LabImage * original, LabImage * transformed, int cx, int cy, int sk) +{ + +//local denoise + //all these variables are to prevent use of denoise when non necessary + // but with qualmet = 2 (default for best quality) we must denoise chroma with little values to prevent artifacts due to variations of Hue + // but if user select voluntary denoise, it is that choice the good (prioritary) + bool execcolor = (lp.chro != 0.f || lp.ligh != 0.f || lp.cont != 0); // only if one slider or more is engaged + bool execbdl = (lp.mulloc[0] != 1.f || lp.mulloc[1] != 1.f || lp.mulloc[2] != 1.f || lp.mulloc[3] != 1.f || lp.mulloc[4] != 1.f || lp.mulloc[5] != 1.f) ;//only if user want cbdl + bool execdenoi = noiscfactiv && ((lp.colorena && execcolor) || (lp.tonemapena && lp.strengt != 0.f) || (lp.cbdlena && execbdl) || (lp.sfena && lp.strng > 0.f) || (lp.lcena && lp.lcamount > 0.f) || (lp.sharpena && lp.shrad > 0.42) || (lp.retiena && lp.str > 0.f) || (lp.exposena && lp.expcomp != 0.f) || (lp.expvib && lp.past != 0.f)); + bool execmaskden = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.smasktyp != 0; + + if (((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f +// || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4 || aut == 1 || aut == 2) && lp.denoiena) || execdenoi) { // sk == 1 ?? + || execmaskden || aut == 1 || aut == 2) && lp.denoiena) || execdenoi) { // sk == 1 ?? + + StopWatch Stop1("locallab Denoise called"); + + if (aut == 0) { + MyMutex::MyLock lock(*fftwMutex); + } + + + if (lp.noisecf >= 0.01f || lp.noisecc >= 0.01f || aut == 1 || aut == 2) { + noiscfactiv = false; + levred = 7; + } + + int GW = transformed->W; + int GH = transformed->H; + + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + + if (call == 1 && GW >= mDEN && GH >= mDEN) { + + + LabImage tmp1(transformed->W, transformed->H); + LabImage tmp2(transformed->W, transformed->H); + tmp2.clear(); + + array2D *Lin = nullptr; + array2D *Ain = nullptr; + array2D *Bin = nullptr; + + int max_numblox_W = ceil((static_cast(GW)) / offset) + 2; + // calculate min size of numblox_W. + int min_numblox_W = ceil((static_cast(GW)) / offset) + 2; + + + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + tmp1.L[ir][jr] = original->L[ir][jr]; + tmp1.a[ir][jr] = original->a[ir][jr]; + tmp1.b[ir][jr] = original->b[ir][jr]; + } + + // int DaubLen = 6; + + int levwavL = levred; + int skip = 1; + + wavelet_decomposition Ldecomp(tmp1.L[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition adecomp(tmp1.a[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition bdecomp(tmp1.b[0], tmp1.W, tmp1.H, levwavL, 1, skip, numThreads, lp.daubLen); + + float madL[10][3]; + int edge = 2; + + if (!Ldecomp.memory_allocation_failed()) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int lvl = 0; lvl < levred; lvl++) { + for (int dir = 1; dir < 4; dir++) { + int Wlvl_L = Ldecomp.level_W(lvl); + int Hlvl_L = Ldecomp.level_H(lvl); + const float* const* WavCoeffs_L = Ldecomp.level_coeffs(lvl); + + madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); + } + } + + float vari[levred]; + float mxsl = 0.f; + // float mxsfl = 0.f; + + if (aut == 0) { + if (levred == 7) { + edge = 2; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiself2 / 125.0) * (1.0 + lp.noiself2 / 25.0)); + + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[4] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[5] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[6] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + } else if (levred == 4) { + edge = 3; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + vari[0] = SQR(slidL[0]); + vari[1] = SQR(slidL[1]); + vari[2] = SQR(slidL[2]); + vari[3] = SQR(slidL[3]); + vari[4] = SQR(slidL[4]); + vari[5] = SQR(slidL[5]); + vari[6] = SQR(slidL[6]); + float mxslid34 = rtengine::max(slidL[3], slidL[4]); + float mxslid56 = rtengine::max(slidL[5], slidL[6]); + mxsl = rtengine::max(mxslid34, mxslid56); + + } + + { + float kr3 = 0.f; + float kr4 = 0.f; + float kr5 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noiselc < 30.f && aut == 0) || (mxsl < 30.f && aut == 1)) { + kr3 = 0.f; + kr4 = 0.f; + kr5 = 0.f; + } else if ((lp.noiselc < 50.f && aut == 0) || (mxsl < 50.f && aut == 1)) { + kr3 = 0.5f; + kr4 = 0.3f; + kr5 = 0.2f; + } else if ((lp.noiselc < 70.f && aut == 0) || (mxsl < 70.f && aut == 1)) { + kr3 = 0.7f; + kr4 = 0.5f; + kr5 = 0.3f; + } else { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + } else if (aut == 2) { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + + vari[0] = rtengine::max(0.000001f, vari[0]); + vari[1] = rtengine::max(0.000001f, vari[1]); + vari[2] = rtengine::max(0.000001f, vari[2]); + vari[3] = rtengine::max(0.000001f, kr3 * vari[3]); + + if (levred == 7) { + vari[4] = rtengine::max(0.000001f, kr4 * vari[4]); + vari[5] = rtengine::max(0.000001f, kr5 * vari[5]); + vari[6] = rtengine::max(0.000001f, kr5 * vari[6]); + } + + float* noisevarlum = new float[GH * GW]; + int GW2 = (GW + 1) / 2; + + float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value + float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value + + float seuillow = 3000.f;//low + float seuilhigh = 18000.f;//high + int i = 10 - lp.noiselequal; + float ac = (nvlh[i] - nvll[i]) / (seuillow - seuilhigh); + float bc = nvlh[i] - seuillow * ac; + //ac and bc for transition +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + float lN = tmp1.L[ir][jr]; + + if (lN < seuillow) { + noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = nvlh[i]; + } else if (lN < seuilhigh) { + noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = ac * lN + bc; + } else { + noisevarlum[(ir >> 1)*GW2 + (jr >> 1)] = nvll[i]; + } + } + + if ((lp.noiselc < 0.02f && aut == 0) || (mxsl < 1.f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + + } else { + + WaveletDenoiseAll_BiShrinkL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + + } + + delete[] noisevarlum; + + } + } + + + float variC[levred]; + float variCb[levred]; + + float noisecfr = lp.noisecf; + float noiseccr = lp.noisecc; + + if (lp.adjch > 0.f) { + noisecfr = lp.noisecf + 0.1f * lp.adjch; + noiseccr = lp.noisecc + 0.1f * lp.adjch; + } + + float noisecfb = lp.noisecf; + float noiseccb = lp.noisecc; + + if (lp.adjch < 0.f) { + noisecfb = lp.noisecf - 0.1f * lp.adjch; + noiseccb = lp.noisecc - 0.1f * lp.adjch; + } + + + if (noisecfr < 0.f) { + noisecfr = 0.00001f; + } + + if (noiseccr < 0.f) { + noiseccr = 0.00001f; + } + + if (noisecfb < 0.f) { + noisecfb = 0.00001f; + } + + if (noiseccb < 0.f) { + noiseccb = 0.00001f; + } + + if (!adecomp.memory_allocation_failed() && !bdecomp.memory_allocation_failed()) { + float maxcfine = 0.f; + float maxccoarse = 0.f; + + if (aut == 0) { + if (levred == 7) { + edge = 2; + variC[0] = SQR(noisecfr); + variC[1] = SQR(noisecfr); + variC[2] = SQR(noisecfr); + + variC[3] = SQR(noisecfr); + variC[4] = SQR(noisecfr); + variC[5] = SQR(noiseccr); + variC[6] = SQR(noiseccr); + + variCb[0] = SQR(noisecfb); + variCb[1] = SQR(noisecfb); + variCb[2] = SQR(noisecfb); + + variCb[3] = SQR(noisecfb); + variCb[4] = SQR(noisecfb); + variCb[5] = SQR(noiseccb); + variCb[6] = SQR(noiseccb); + + } else if (levred == 4) { + edge = 3; + variC[0] = SQR(lp.noisecf / 10.0); + variC[1] = SQR(lp.noisecf / 10.0); + variC[2] = SQR(lp.noisecf / 10.0); + variC[3] = SQR(lp.noisecf / 10.0); + + variCb[0] = SQR(lp.noisecf / 10.0); + variCb[1] = SQR(lp.noisecf / 10.0); + variCb[2] = SQR(lp.noisecf / 10.0); + variCb[3] = SQR(lp.noisecf / 10.0); + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + variC[0] = SQR(slida[0]); + variC[1] = SQR(slida[1]); + variC[2] = SQR(slida[2]); + variC[3] = SQR(slida[3]); + variC[4] = SQR(slida[4]); + variC[5] = SQR(slida[5]); + variC[6] = SQR(slida[6]); + float maxc01 = rtengine::max(slida[0], slida[1]); + float maxc23 = rtengine::max(slida[2], slida[3]); + float max03 = rtengine::max(maxc01, maxc23); + float maxrf = rtengine::max(max03, slida[4]); + float maxrc = rtengine::max(slida[5], slida[6]); + + variCb[0] = SQR(slidb[0]); + variCb[1] = SQR(slidb[1]); + variCb[2] = SQR(slidb[2]); + variCb[3] = SQR(slidb[3]); + variCb[4] = SQR(slidb[4]); + variCb[5] = SQR(slidb[5]); + variCb[6] = SQR(slidb[6]); + float maxb01 = rtengine::max(slidb[0], slidb[1]); + float maxb23 = rtengine::max(slidb[2], slidb[3]); + float maxb03 = rtengine::max(maxb01, maxb23); + float maxbf = rtengine::max(maxb03, slidb[4]); + maxcfine = rtengine::max(maxrf, maxbf); + + float maxbc = rtengine::max(slidb[5], slidb[6]); + maxccoarse = rtengine::max(maxrc, maxbc); + + } + + { + float minic = 0.000001f; + + if (noiscfactiv) { + minic = 0.1f;//only for artifact shape detection + } + + float k1 = 0.f; + float k2 = 0.f; + float k3 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noisecf < 0.2f && aut == 0) || (maxcfine < 0.2f && aut == 1)) { + k1 = 0.05f; + k2 = 0.f; + k3 = 0.f; + } else if ((lp.noisecf < 0.3f && aut == 0) || (maxcfine < 0.3f && aut == 1)) { + k1 = 0.1f; + k2 = 0.0f; + k3 = 0.f; + } else if ((lp.noisecf < 0.5f && aut == 0) || (maxcfine < 0.5f && aut == 1)) { + k1 = 0.2f; + k2 = 0.1f; + k3 = 0.f; + } else if ((lp.noisecf < 0.8f && aut == 0) || (maxcfine < 0.8f && aut == 1)) { + k1 = 0.3f; + k2 = 0.25f; + k3 = 0.f; + } else if ((lp.noisecf < 1.f && aut == 0) || (maxcfine < 1.f && aut == 1)) { + k1 = 0.4f; + k2 = 0.25f; + k3 = 0.1f; + } else if ((lp.noisecf < 2.f && aut == 0) || (maxcfine < 2.f && aut == 1)) { + k1 = 0.5f; + k2 = 0.3f; + k3 = 0.15f; + } else if ((lp.noisecf < 3.f && aut == 0) || (maxcfine < 3.f && aut == 1)) { + k1 = 0.6f; + k2 = 0.45f; + k3 = 0.3f; + } else if ((lp.noisecf < 4.f && aut == 0) || (maxcfine < 4.f && aut == 1)) { + k1 = 0.7f; + k2 = 0.5f; + k3 = 0.4f; + } else if ((lp.noisecf < 5.f && aut == 0) || (maxcfine < 5.f && aut == 1)) { + k1 = 0.8f; + k2 = 0.6f; + k3 = 0.5f; + } else if ((lp.noisecf < 6.f && aut == 0) || (maxcfine < 10.f && aut == 1)) { + k1 = 0.85f; + k2 = 0.7f; + k3 = 0.6f; + } else if ((lp.noisecf < 8.f && aut == 0) || (maxcfine < 20.f && aut == 1)) { + k1 = 0.9f; + k2 = 0.8f; + k3 = 0.7f; + } else if ((lp.noisecf < 10.f && aut == 0) || (maxcfine < 50.f && aut == 1)) { + k1 = 1.f; + k2 = 1.f; + k3 = 0.9f; + + } else { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + } else if (aut == 2) { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + + + variC[0] = rtengine::max(minic, variC[0]); + variC[1] = rtengine::max(minic, k1 * variC[1]); + variC[2] = rtengine::max(minic, k2 * variC[2]); + variC[3] = rtengine::max(minic, k3 * variC[3]); + + variCb[0] = rtengine::max(minic, variCb[0]); + variCb[1] = rtengine::max(minic, k1 * variCb[1]); + variCb[2] = rtengine::max(minic, k2 * variCb[2]); + variCb[3] = rtengine::max(minic, k3 * variCb[3]); + + if (levred == 7) { + float k4 = 0.f; + float k5 = 0.f; + float k6 = 0.f; + + if ((lp.noisecc < 0.2f && aut == 0) || (maxccoarse < 0.2f && aut == 1)) { + k4 = 0.1f; + k5 = 0.02f; + } else if ((lp.noisecc < 0.5f && aut == 0) || (maxccoarse < 0.5f && aut == 1)) { + k4 = 0.15f; + k5 = 0.05f; + } else if ((lp.noisecc < 1.f && aut == 0) || (maxccoarse < 1.f && aut == 1)) { + k4 = 0.15f; + k5 = 0.1f; + } else if ((lp.noisecc < 3.f && aut == 0) || (maxccoarse < 3.f && aut == 1)) { + k4 = 0.3f; + k5 = 0.15f; + } else if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k4 = 0.6f; + k5 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k4 = 0.8f; + k5 = 0.6f; + } else { + k4 = 1.f; + k5 = 1.f; + } + + variC[4] = rtengine::max(0.000001f, k4 * variC[4]); + variC[5] = rtengine::max(0.000001f, k5 * variC[5]); + variCb[4] = rtengine::max(0.000001f, k4 * variCb[4]); + variCb[5] = rtengine::max(0.000001f, k5 * variCb[5]); + + if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 4.f && aut == 1)) { + k6 = 0.f; + } else if ((lp.noisecc < 5.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k6 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k6 = 0.7f; + } else { + k6 = 1.f; + } + + variC[6] = rtengine::max(0.00001f, k6 * variC[6]); + variCb[6] = rtengine::max(0.00001f, k6 * variCb[6]); + + } + + float* noisevarchrom = new float[GH * GW]; + //noisevarchrom in function chroma + int GW2 = (GW + 1) / 2; + float nvch = 0.6f;//high value + float nvcl = 0.1f;//low value + + if ((lp.noisecf > 100.f && aut == 0) || (maxcfine > 100.f && (aut == 1 || aut == 2))) { + nvch = 0.8f; + nvcl = 0.4f; + } + + float seuil = 4000.f;//low + float seuil2 = 15000.f;//high + //ac and bc for transition + float ac = (nvch - nvcl) / (seuil - seuil2); + float bc = nvch - seuil * ac; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + float cN = std::sqrt(SQR(tmp1.a[ir][jr]) + SQR(tmp1.b[ir][jr])); + + if (cN < seuil) { + noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvch; + } else if (cN < seuil2) { + noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = ac * cN + bc; + } else { + noisevarchrom[(ir >> 1)*GW2 + (jr >> 1)] = nvcl; + } + } + + + float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); + + if ((lp.noisecc < 2.f && aut == 0) || (maxccoarse < 0.1f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } else { + WaveletDenoiseAll_BiShrinkAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + + WaveletDenoiseAll_BiShrinkAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } + + delete[] noisevarchrom; + + } + } + + if (!Ldecomp.memory_allocation_failed()) { + Lin = new array2D(GW, GH); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Lin)[i][j] = tmp1.L[i][j]; + } + } + + Ldecomp.reconstruct(tmp1.L[0]); + } + + if (!Ldecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noiself >= 0.01f || lp.noiself0 >= 0.01f || lp.noiself2 >= 0.01f || lp.noiselc >= 0.01f) && levred == 7 && lp.noiseldetail != 100.f) { + fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.L, Lin, numThreads, lp, 0); + } + } + + if (!adecomp.memory_allocation_failed()) { + Ain = new array2D(GW, GH); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Ain)[i][j] = tmp1.a[i][j]; + } + } + + adecomp.reconstruct(tmp1.a[0]); + } + + + if (!adecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.01f || lp.noisecc >= 0.01f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.a, Ain, numThreads, lp, 1); + } + } + + + if (!bdecomp.memory_allocation_failed()) { + + Bin = new array2D(GW, GH); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < GH; ++i) { + for (int j = 0; j < GW; ++j) { + (*Bin)[i][j] = tmp1.b[i][j]; + } + } + + bdecomp.reconstruct(tmp1.b[0]); + } + + + if (!bdecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.01f || lp.noisecc >= 0.01f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(GW, GH, max_numblox_W, min_numblox_W, tmp1.b, Bin, numThreads, lp, 1); + } + + } + + // DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); + if(lp.smasktyp != 0) { + DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); + } else { + DeNoise_Local(call, lp, original, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, tmp1, cx, cy, sk); + } + + } else if (call == 2) { //simpleprocess + + int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone + int bfw = int (lp.lx + lp.lxL) + del; + + if (bfh >= mDEN && bfw >= mDEN) { + LabImage bufwv(bfw, bfh); + bufwv.clear(true); + array2D *Lin = nullptr; + array2D *Ain = nullptr; + array2D *Bin = nullptr; + + int max_numblox_W = ceil((static_cast(bfw)) / offset) + 2; + // calculate min size of numblox_W. + int min_numblox_W = ceil((static_cast(bfw)) / offset) + 2; + // these are needed only for creation of the plans and will be freed before entering the parallel loop + + + int begy = lp.yc - lp.lyT; + int begx = lp.xc - lp.lxL; + int yEn = lp.yc + lp.ly; + int xEn = lp.xc + lp.lx; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) //{ + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int loy = cy + y; + + if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { + bufwv.L[loy - begy][lox - begx] = original->L[y][x]; + bufwv.a[loy - begy][lox - begx] = original->a[y][x]; + bufwv.b[loy - begy][lox - begx] = original->b[y][x]; + } + + } + + // int DaubLen = 6; + + int levwavL = levred; + int skip = 1; + wavelet_decomposition Ldecomp(bufwv.L[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition adecomp(bufwv.a[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); + wavelet_decomposition bdecomp(bufwv.b[0], bufwv.W, bufwv.H, levwavL, 1, skip, numThreads, lp.daubLen); + float madL[10][3]; + int edge = 2; + + if (!Ldecomp.memory_allocation_failed()) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) if (multiThread) +#endif + for (int lvl = 0; lvl < levred; lvl++) { + for (int dir = 1; dir < 4; dir++) { + int Wlvl_L = Ldecomp.level_W(lvl); + int Hlvl_L = Ldecomp.level_H(lvl); + + const float* const* WavCoeffs_L = Ldecomp.level_coeffs(lvl); + + madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); + } + } + + float vari[levred]; + float mxsl = 0.f; + // float mxsfl = 0.f; + + if (aut == 0) { + if (levred == 7) { + edge = 2; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiself2 / 125.0) * (1.0 + lp.noiself2 / 25.0)); + + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[4] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[5] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[6] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + } else if (levred == 4) { + edge = 3; + vari[0] = 0.8f * SQR((lp.noiself0 / 125.0) * (1.0 + lp.noiself0 / 25.0)); + vari[1] = 0.8f * SQR((lp.noiself / 125.0) * (1.0 + lp.noiself / 25.0)); + vari[2] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + vari[3] = 0.8f * SQR((lp.noiselc / 125.0) * (1.0 + lp.noiselc / 25.0)); + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + vari[0] = SQR(slidL[0]); + vari[1] = SQR(slidL[1]); + vari[2] = SQR(slidL[2]); + vari[3] = SQR(slidL[3]); + vari[4] = SQR(slidL[4]); + vari[5] = SQR(slidL[5]); + vari[6] = SQR(slidL[6]); + float mxslid34 = rtengine::max(slidL[3], slidL[4]); + float mxslid56 = rtengine::max(slidL[5], slidL[6]); + mxsl = rtengine::max(mxslid34, mxslid56); + + } + + { + float kr3 = 0.f; + float kr4 = 0.f; + float kr5 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noiselc < 30.f && aut == 0) || (mxsl < 30.f && aut == 1)) { + kr3 = 0.f; + kr4 = 0.f; + kr5 = 0.f; + } else if ((lp.noiselc < 50.f && aut == 0) || (mxsl < 50.f && aut == 1)) { + kr3 = 0.5f; + kr4 = 0.3f; + kr5 = 0.2f; + } else if ((lp.noiselc < 70.f && aut == 0) || (mxsl < 70.f && aut == 1)) { + kr3 = 0.7f; + kr4 = 0.5f; + kr5 = 0.3f; + } else { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + } + } else if (aut == 2) { + kr3 = 1.f; + kr4 = 1.f; + kr5 = 1.f; + + } + + vari[0] = rtengine::max(0.000001f, vari[0]); + vari[1] = rtengine::max(0.000001f, vari[1]); + vari[2] = rtengine::max(0.000001f, vari[2]); + vari[3] = rtengine::max(0.000001f, kr3 * vari[3]); + + if (levred == 7) { + vari[4] = rtengine::max(0.000001f, kr4 * vari[4]); + vari[5] = rtengine::max(0.000001f, kr5 * vari[5]); + vari[6] = rtengine::max(0.000001f, kr5 * vari[6]); + } + + // float* noisevarlum = nullptr; // we need a dummy to pass it to WaveletDenoiseAllL + float* noisevarlum = new float[bfh * bfw]; + int bfw2 = (bfw + 1) / 2; + + float nvlh[13] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 0.7f, 0.5f}; //high value + float nvll[13] = {0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.7f, 0.8f, 1.f, 1.f, 1.f}; //low value + + float seuillow = 3000.f;//low + float seuilhigh = 18000.f;//high + int i = 10 - lp.noiselequal; + float ac = (nvlh[i] - nvll[i]) / (seuillow - seuilhigh); + float bc = nvlh[i] - seuillow * ac; + //ac and bc for transition +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + float lN = bufwv.L[ir][jr]; + + if (lN < seuillow) { + noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = nvlh[i]; + } else if (lN < seuilhigh) { + noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = ac * lN + bc; + } else { + noisevarlum[(ir >> 1)*bfw2 + (jr >> 1)] = nvll[i]; + } + } + + + if ((lp.noiselc < 0.02f && aut == 0) || (mxsl < 1.f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + } else { + WaveletDenoiseAll_BiShrinkL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + WaveletDenoiseAllL(Ldecomp, noisevarlum, madL, vari, edge, numThreads); + } + + delete [] noisevarlum; + + } + } + + + float variC[levred]; + float variCb[levred]; + + float noisecfr = lp.noisecf; + float noiseccr = lp.noisecc; + + if (lp.adjch > 0.f) { + noisecfr = lp.noisecf + 0.1f * lp.adjch; + noiseccr = lp.noisecc + 0.1f * lp.adjch; + } + + float noisecfb = lp.noisecf; + float noiseccb = lp.noisecc; + + if (lp.adjch < 0.f) { + noisecfb = lp.noisecf - 0.1f * lp.adjch; + noiseccb = lp.noisecc - 0.1f * lp.adjch; + } + + + if (noisecfr < 0.f) { + noisecfr = 0.00001f; + } + + if (noiseccr < 0.f) { + noiseccr = 0.00001f; + } + + if (noisecfb < 0.f) { + noisecfb = 0.00001f; + } + + if (noiseccb < 0.f) { + noiseccb = 0.00001f; + } + + + if (!adecomp.memory_allocation_failed() && !bdecomp.memory_allocation_failed()) { + float maxcfine = 0.f; + float maxccoarse = 0.f; + + if (aut == 0) { + + if (levred == 7) { + edge = 2; + variC[0] = SQR(noisecfr); + variC[1] = SQR(noisecfr); + variC[2] = SQR(noisecfr); + + variC[3] = SQR(noisecfr); + variC[4] = SQR(noisecfr); + variC[5] = SQR(noiseccr); + variC[6] = SQR(noiseccr); + + variCb[0] = SQR(noisecfb); + variCb[1] = SQR(noisecfb); + variCb[2] = SQR(noisecfb); + + variCb[3] = SQR(noisecfb); + variCb[4] = SQR(noisecfb); + variCb[5] = SQR(noiseccb); + variCb[6] = SQR(noiseccb); + + } else if (levred == 4) { + edge = 3; + variC[0] = SQR(lp.noisecf / 10.0); + variC[1] = SQR(lp.noisecf / 10.0); + variC[2] = SQR(lp.noisecf / 10.0); + variC[3] = SQR(lp.noisecf / 10.0); + + variCb[0] = SQR(lp.noisecf / 10.0); + variCb[1] = SQR(lp.noisecf / 10.0); + variCb[2] = SQR(lp.noisecf / 10.0); + variCb[3] = SQR(lp.noisecf / 10.0); + + + } + } else if (aut == 1 || aut == 2) { + edge = 2; + variC[0] = SQR(slida[0]); + variC[1] = SQR(slida[1]); + variC[2] = SQR(slida[2]); + variC[3] = SQR(slida[3]); + variC[4] = SQR(slida[4]); + variC[5] = SQR(slida[5]); + variC[6] = SQR(slida[6]); + float maxc01 = rtengine::max(slida[0], slida[1]); + float maxc23 = rtengine::max(slida[2], slida[3]); + float max03 = rtengine::max(maxc01, maxc23); + float maxrf = rtengine::max(max03, slida[4]); + float maxrc = rtengine::max(slida[5], slida[6]); + + variCb[0] = SQR(slidb[0]); + variCb[1] = SQR(slidb[1]); + variCb[2] = SQR(slidb[2]); + variCb[3] = SQR(slidb[3]); + variCb[4] = SQR(slidb[4]); + variCb[5] = SQR(slidb[5]); + variCb[6] = SQR(slidb[6]); + float maxb01 = rtengine::max(slidb[0], slidb[1]); + float maxb23 = rtengine::max(slidb[2], slidb[3]); + float maxb03 = rtengine::max(maxb01, maxb23); + float maxbf = rtengine::max(maxb03, slidb[4]); + maxcfine = rtengine::max(maxrf, maxbf); + + float maxbc = rtengine::max(slidb[5], slidb[6]); + maxccoarse = rtengine::max(maxrc, maxbc); + + } + + { + float minic = 0.000001f; + + if (noiscfactiv) { + minic = 0.1f;//only for artifact shape detection + } + + float k1 = 0.f; + float k2 = 0.f; + float k3 = 0.f; + + if (aut == 0 || aut == 1) { + if ((lp.noisecf < 0.2f && aut == 0) || (maxcfine < 0.2f && aut == 1)) { + k1 = 0.05f; + k2 = 0.f; + k3 = 0.f; + } else if ((lp.noisecf < 0.3f && aut == 0) || (maxcfine < 0.3f && aut == 1)) { + k1 = 0.1f; + k2 = 0.0f; + k3 = 0.f; + } else if ((lp.noisecf < 0.5f && aut == 0) || (maxcfine < 0.5f && aut == 1)) { + k1 = 0.2f; + k2 = 0.1f; + k3 = 0.f; + } else if ((lp.noisecf < 0.8f && aut == 0) || (maxcfine < 0.8f && aut == 1)) { + k1 = 0.3f; + k2 = 0.25f; + k3 = 0.f; + } else if ((lp.noisecf < 1.f && aut == 0) || (maxcfine < 1.f && aut == 1)) { + k1 = 0.4f; + k2 = 0.25f; + k3 = 0.1f; + } else if ((lp.noisecf < 2.f && aut == 0) || (maxcfine < 2.f && aut == 1)) { + k1 = 0.5f; + k2 = 0.3f; + k3 = 0.15f; + } else if ((lp.noisecf < 3.f && aut == 0) || (maxcfine < 3.f && aut == 1)) { + k1 = 0.6f; + k2 = 0.45f; + k3 = 0.3f; + } else if ((lp.noisecf < 4.f && aut == 0) || (maxcfine < 4.f && aut == 1)) { + k1 = 0.7f; + k2 = 0.5f; + k3 = 0.4f; + } else if ((lp.noisecf < 5.f && aut == 0) || (maxcfine < 5.f && aut == 1)) { + k1 = 0.8f; + k2 = 0.6f; + k3 = 0.5f; + } else if ((lp.noisecf < 6.f && aut == 0) || (maxcfine < 10.f && aut == 1)) { + k1 = 0.85f; + k2 = 0.7f; + k3 = 0.6f; + } else if ((lp.noisecf < 8.f && aut == 0) || (maxcfine < 20.f && aut == 1)) { + k1 = 0.9f; + k2 = 0.8f; + k3 = 0.7f; + } else if ((lp.noisecf < 10.f && aut == 0) || (maxcfine < 50.f && aut == 1)) { + k1 = 1.f; + k2 = 1.f; + k3 = 0.9f; + + } else { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + } else if (aut == 2) { + k1 = 1.f; + k2 = 1.f; + k3 = 1.f; + } + + variC[0] = rtengine::max(minic, variC[0]); + variC[1] = rtengine::max(minic, k1 * variC[1]); + variC[2] = rtengine::max(minic, k2 * variC[2]); + variC[3] = rtengine::max(minic, k3 * variC[3]); + + variCb[0] = rtengine::max(minic, variCb[0]); + variCb[1] = rtengine::max(minic, k1 * variCb[1]); + variCb[2] = rtengine::max(minic, k2 * variCb[2]); + variCb[3] = rtengine::max(minic, k3 * variCb[3]); + + if (levred == 7) { + float k4 = 0.f; + float k5 = 0.f; + float k6 = 0.f; + + if ((lp.noisecc < 0.2f && aut == 0) || (maxccoarse < 0.2f && aut == 1)) { + k4 = 0.1f; + k5 = 0.02f; + } else if ((lp.noisecc < 0.5f && aut == 0) || (maxccoarse < 0.5f && aut == 1)) { + k4 = 0.15f; + k5 = 0.05f; + } else if ((lp.noisecc < 1.f && aut == 0) || (maxccoarse < 1.f && aut == 1)) { + k4 = 0.15f; + k5 = 0.1f; + } else if ((lp.noisecc < 3.f && aut == 0) || (maxccoarse < 3.f && aut == 1)) { + k4 = 0.3f; + k5 = 0.15f; + } else if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k4 = 0.6f; + k5 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k4 = 0.8f; + k5 = 0.6f; + } else { + k4 = 1.f; + k5 = 1.f; + } + + + variC[4] = rtengine::max(0.000001f, k4 * variC[4]); + variC[5] = rtengine::max(0.000001f, k5 * variC[5]); + variCb[4] = rtengine::max(0.000001f, k4 * variCb[4]); + variCb[5] = rtengine::max(0.000001f, k5 * variCb[5]); + + if ((lp.noisecc < 4.f && aut == 0) || (maxccoarse < 4.f && aut == 1)) { + k6 = 0.f; + } else if ((lp.noisecc < 5.f && aut == 0) || (maxccoarse < 5.f && aut == 1)) { + k6 = 0.4f; + } else if ((lp.noisecc < 6.f && aut == 0) || (maxccoarse < 6.f && aut == 1)) { + k6 = 0.7f; + } else { + k6 = 1.f; + } + + variC[6] = rtengine::max(0.00001f, k6 * variC[6]); + variCb[6] = rtengine::max(0.00001f, k6 * variCb[6]); + } + + float* noisevarchrom = new float[bfh * bfw]; + int bfw2 = (bfw + 1) / 2; + float nvch = 0.6f;//high value + float nvcl = 0.1f;//low value + + if ((lp.noisecf > 30.f && aut == 0) || (maxcfine > 100.f && (aut == 1 || aut == 2))) { + nvch = 0.8f; + nvcl = 0.4f; + } + + float seuil = 4000.f;//low + float seuil2 = 15000.f;//high + //ac and bc for transition + float ac = (nvch - nvcl) / (seuil - seuil2); + float bc = nvch - seuil * ac; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + float cN = std::sqrt(SQR(bufwv.a[ir][jr]) + SQR(bufwv.b[ir][jr])); + + if (cN < seuil) { + noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = nvch; + } else if (cN < seuil2) { + noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = ac * cN + bc; + } else { + noisevarchrom[(ir >> 1)*bfw2 + (jr >> 1)] = nvcl; + } + } + + float noisevarab_r = 100.f; //SQR(lp.noisecc / 10.0); + + if ((lp.noisecc < 0.02f && aut == 0) || (maxccoarse < 0.1f && (aut == 1 || aut == 2))) { + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } else { + WaveletDenoiseAll_BiShrinkAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, numThreads); + + WaveletDenoiseAll_BiShrinkAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + WaveletDenoiseAllAB(Ldecomp, bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, numThreads); + } + + delete[] noisevarchrom; + } + } + + if (!Ldecomp.memory_allocation_failed()) { + Lin = new array2D(bfw, bfh); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Lin)[i][j] = bufwv.L[i][j]; + } + } + + Ldecomp.reconstruct(bufwv.L[0]); + } + + + if (!Ldecomp.memory_allocation_failed() && aut == 0) { + + + if ((lp.noiself >= 0.01f || lp.noiself0 >= 0.01f || lp.noiself2 >= 0.01f || lp.noiselc >= 0.01f) && levred == 7 && lp.noiseldetail != 100.f) { + fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.L, Lin, numThreads, lp, 0); + } + } + + + if (!adecomp.memory_allocation_failed()) { + Ain = new array2D(bfw, bfh); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Ain)[i][j] = bufwv.a[i][j]; + } + } + + adecomp.reconstruct(bufwv.a[0]); + } + + if (!adecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.001f || lp.noisecc >= 0.001f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.a, Ain, numThreads, lp, 1); + } + } + + + if (!bdecomp.memory_allocation_failed()) { + Bin = new array2D(bfw, bfh); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < bfh; ++i) { + for (int j = 0; j < bfw; ++j) { + (*Bin)[i][j] = bufwv.b[i][j]; + } + } + + bdecomp.reconstruct(bufwv.b[0]); + } + + if (!bdecomp.memory_allocation_failed() && aut == 0) { + if ((lp.noisecf >= 0.001f || lp.noisecc >= 0.001f) && levred == 7 && lp.noisechrodetail != 100.f) { + fftw_denoise(bfw, bfh, max_numblox_W, min_numblox_W, bufwv.b, Bin, numThreads, lp, 1); + } + } + + if(lp.smasktyp != 0) { + DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); + } else { + DeNoise_Local(call, lp, original, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); + } + + // DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, bufwv, cx, cy, sk); + } + } + } + +} + +float triangle(float a, float a1, float b) +{ + 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; +} + +void rgbtone(float& maxval, float& medval, float& minval, const LUTf& lutToneCurve) +{ + float minvalold = minval, medvalold = medval, maxvalold = maxval; + + maxval = lutToneCurve[maxvalold]; + minval = lutToneCurve[minvalold]; + medval = minval + ((maxval - minval) * (medvalold - minvalold) / (maxvalold - minvalold)); +} + +void clarimerge(struct local_params& lp, float &mL, float &mC, bool &exec, LabImage *tmpresid, int wavelet_level, int sk, int numThreads) +{ + if (mL != 0.f && mC == 0.f) { + mC = 0.0001f; + exec = true; + } + + if (mC != 0.f && mL == 0.f) { + mL = 0.0001f; + exec = true; + } + + if (mL != 0.f && mC != 0.f) { + exec = true; + } + + if (mL != 0.f) { + + wavelet_decomposition *wdspotresid = new wavelet_decomposition(tmpresid->L[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotresid->memory_allocation_failed()) { + return; + } + + int maxlvlresid = wdspotresid->maxlevel(); + + if (maxlvlresid > 4) {//Clarity +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = 0; level < maxlvlresid; ++level) { + int W_L = wdspotresid->level_W(level); + int H_L = wdspotresid->level_H(level); + float* const* wav_Lresid = wdspotresid->level_coeffs(level); + + for (int i = 0; i < W_L * H_L; i++) { + wav_Lresid[dir][i] = 0.f; + } + } + } + } else {//Sharp + float *wav_L0resid = wdspotresid->get_coeff0(); + int W_L = wdspotresid->level_W(0); + int H_L = wdspotresid->level_H(0); + + for (int i = 0; i < W_L * H_L; i++) { + wav_L0resid[i] = 0.f; + } + } + + wdspotresid->reconstruct(tmpresid->L[0], 1.f); + delete wdspotresid; + } + + + if (mC != 0.f) { + + wavelet_decomposition *wdspotresida = new wavelet_decomposition(tmpresid->a[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotresida->memory_allocation_failed()) { + return; + } + + int maxlvlresid = wdspotresida->maxlevel(); + + if (maxlvlresid > 4) {//Clarity +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = 0; level < maxlvlresid; ++level) { + int W_L = wdspotresida->level_W(level); + int H_L = wdspotresida->level_H(level); + float* const* wav_Lresida = wdspotresida->level_coeffs(level); + + for (int i = 0; i < W_L * H_L; i++) { + wav_Lresida[dir][i] = 0.f; + } + } + } + } else {//Sharp + float *wav_L0resida = wdspotresida->get_coeff0(); + int W_L = wdspotresida->level_W(0); + int H_L = wdspotresida->level_H(0); + + for (int i = 0; i < W_L * H_L; i++) { + wav_L0resida[i] = 0.f; + } + } + + wdspotresida->reconstruct(tmpresid->a[0], 1.f); + delete wdspotresida; + + wavelet_decomposition *wdspotresidb = new wavelet_decomposition(tmpresid->b[0], tmpresid->W, tmpresid->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotresidb->memory_allocation_failed()) { + return; + } + + maxlvlresid = wdspotresidb->maxlevel(); + + if (maxlvlresid > 4) {//Clarity +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic) collapse(2) +#endif + for (int dir = 1; dir < 4; dir++) { + for (int level = 0; level < maxlvlresid; ++level) { + int W_L = wdspotresidb->level_W(level); + int H_L = wdspotresidb->level_H(level); + float* const* wav_Lresidb = wdspotresidb->level_coeffs(level); + + for (int i = 0; i < W_L * H_L; i++) { + wav_Lresidb[dir][i] = 0.f; + } + } + } + } else {//Sharp + float *wav_L0residb = wdspotresidb->get_coeff0(); + int W_L = wdspotresidb->level_W(0); + int H_L = wdspotresidb->level_H(0); + + for (int i = 0; i < W_L * H_L; i++) { + wav_L0residb[i] = 0.f; + } + } + + wdspotresidb->reconstruct(tmpresid->b[0], 1.f); + delete wdspotresidb; + } +} + +void ImProcFunctions::Lab_Local( + int call, int sp, float** shbuffer, LabImage * original, LabImage * transformed, LabImage * reserved, LabImage * lastorig, int cx, int cy, int oW, int oH, int sk, + const LocretigainCurve& locRETgainCcurve, const LocretitransCurve& locRETtransCcurve, + const LUTf& lllocalcurve, bool locallutili, + const LUTf& cllocalcurve, bool localclutili, + const LUTf& lclocalcurve, bool locallcutili, + const LocLHCurve& loclhCurve, const LocHHCurve& lochhCurve, + const LUTf& lmasklocalcurve, bool localmaskutili, + const LUTf& lmaskexplocalcurve, bool localmaskexputili, + const LUTf& lmaskSHlocalcurve, bool localmaskSHutili, + const LUTf& lmaskviblocalcurve, bool localmaskvibutili, + const LUTf& lmasktmlocalcurve, bool localmasktmutili, + LUTf& lmaskretilocalcurve, bool localmaskretiutili, + const LUTf& lmaskcblocalcurve, bool localmaskcbutili, + const LUTf& lmaskbllocalcurve, bool localmaskblutili, + const LUTf& lmasklclocalcurve, bool localmasklcutili, + const LocCCmaskCurve& locccmasCurve, bool lcmasutili, const LocLLmaskCurve& locllmasCurve, bool llmasutili, const LocHHmaskCurve& lochhmasCurve, bool lhmasutili, const LocHHmaskCurve& lochhhmasCurve, bool lhhmasutili, + const LocCCmaskCurve& locccmasexpCurve, bool lcmasexputili, const LocLLmaskCurve& locllmasexpCurve, bool llmasexputili, const LocHHmaskCurve& lochhmasexpCurve, bool lhmasexputili, + const LocCCmaskCurve& locccmasSHCurve, bool lcmasSHutili, const LocLLmaskCurve& locllmasSHCurve, bool llmasSHutili, const LocHHmaskCurve& lochhmasSHCurve, bool lhmasSHutili, + const LocCCmaskCurve& locccmasvibCurve, bool lcmasvibutili, const LocLLmaskCurve& locllmasvibCurve, bool llmasvibutili, const LocHHmaskCurve& lochhmasvibCurve, bool lhmasvibutili, + const LocCCmaskCurve& locccmascbCurve, bool lcmascbutili, const LocLLmaskCurve& locllmascbCurve, bool llmascbutili, const LocHHmaskCurve& lochhmascbCurve, bool lhmascbutili, + const LocCCmaskCurve& locccmasretiCurve, bool lcmasretiutili, const LocLLmaskCurve& locllmasretiCurve, bool llmasretiutili, const LocHHmaskCurve& lochhmasretiCurve, bool lhmasretiutili, + const LocCCmaskCurve& locccmastmCurve, bool lcmastmutili, const LocLLmaskCurve& locllmastmCurve, bool llmastmutili, const LocHHmaskCurve& lochhmastmCurve, bool lhmastmutili, + const LocCCmaskCurve& locccmasblCurve, bool lcmasblutili, const LocLLmaskCurve& locllmasblCurve, bool llmasblutili, const LocHHmaskCurve& lochhmasblCurve, bool lhmasblutili, + const LocCCmaskCurve& locccmaslcCurve, bool lcmaslcutili, const LocLLmaskCurve& locllmaslcCurve, bool llmaslcutili, const LocHHmaskCurve& lochhmaslcCurve, bool lhmaslcutili, + const LocwavCurve& loclmasCurveblwav, bool lmasutiliblwav, + const LocwavCurve& loclmasCurvecolwav, bool lmasutilicolwav, + const LocwavCurve& locwavCurve, bool locwavutili, + const LocwavCurve& loclevwavCurve, bool loclevwavutili, + const LocwavCurve& locconwavCurve, bool locconwavutili, + const LocwavCurve& loccompwavCurve, bool loccompwavutili, + const LocwavCurve& loccomprewavCurve, bool loccomprewavutili, + const LocwavCurve& locwavCurveden, bool locwavdenutili, + const LocwavCurve& locedgwavCurve, bool locedgwavutili, + bool LHutili, bool HHutili, const LUTf& cclocalcurve, bool localcutili, const LUTf& rgblocalcurve, bool localrgbutili, bool localexutili, const LUTf& exlocalcurve, const LUTf& hltonecurveloc, const LUTf& shtonecurveloc, const LUTf& tonecurveloc, const LUTf& lightCurveloc, + double& huerefblur, double& chromarefblur, double& lumarefblur, double& hueref, double& chromaref, double& lumaref, double& sobelref, int &lastsav, + bool prevDeltaE, int llColorMask, int llColorMaskinv, int llExpMask, int llExpMaskinv, int llSHMask, int llSHMaskinv, int llvibMask, int lllcMask, int llsharMask, int llcbMask, int llretiMask, int llsoftMask, int lltmMask, int llblMask, + float& minCD, float& maxCD, float& mini, float& maxi, float& Tmean, float& Tsigma, float& Tmin, float& Tmax + ) +{ + //general call of others functions : important return hueref, chromaref, lumaref + if (!params->locallab.enabled) { + return; + } + + BENCHFUN + + constexpr int del = 3; // to avoid crash with [loy - begy] and [lox - begx] and bfh bfw // with gtk2 [loy - begy-1] [lox - begx -1 ] and del = 1 + struct local_params lp; + calcLocalParams(sp, oW, oH, params->locallab, lp, prevDeltaE, llColorMask, llColorMaskinv, llExpMask, llExpMaskinv, llSHMask, llSHMaskinv, llvibMask, lllcMask, llsharMask, llcbMask, llretiMask, llsoftMask, lltmMask, llblMask, locwavCurveden, locwavdenutili); + + const float radius = lp.rad / (sk * 1.4f); //0 to 70 ==> see skip + int levred; + bool noiscfactiv; + + if (lp.qualmet == 2) { //suppress artifacts with quality enhanced + levred = 4; + noiscfactiv = true; + } else { + levred = 7; + noiscfactiv = false; + } + +//lastsav for save restore image + lastsav = 0; + + if (lp.excmet == 1 && call <= 3) {//exclude + const int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone + const int bfw = int (lp.lx + lp.lxL) + del; + const int begy = lp.yc - lp.lyT; + const int begx = lp.xc - lp.lxL; + const int yEn = lp.yc + lp.ly; + const int xEn = lp.xc + lp.lx; + LabImage bufreserv(bfw, bfh); + array2D bufsob(bfw, bfh); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if(multiThread) +#endif + for (int y = rtengine::max(begy - cy, 0); y < rtengine::min(yEn - cy, original->H); y++) { + const int loy = cy + y; + + for (int x = rtengine::max(begx - cx, 0); x < rtengine::min(xEn - cx, original->W); x++) { + const int lox = cx + x; + + bufsob[loy - begy][lox - begx] = bufreserv.L[loy - begy][lox - begx] = reserved->L[y][x]; + bufreserv.a[loy - begy][lox - begx] = reserved->a[y][x]; + bufreserv.b[loy - begy][lox - begx] = reserved->b[y][x]; + } + } + + array2D ble(bfw, bfh); + const float radiussob = 1.f / (sk * 1.4f); + SobelCannyLuma(ble, bufsob, bfw, bfh, radiussob); + array2D &guid = bufsob; + +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + ble[ir][jr] /= 32768.f; + guid[ir][jr] /= 32768.f; + } + + + const float blur = 25 / sk * (2.f + 2.5f * lp.struexp); + + rtengine::guidedFilter(guid, ble, ble, blur, 0.0001, multiThread); + +// const float blur = 25 / sk * (10.f + 0.8f * lp.struexp); + +// rtengine::guidedFilter(guid, ble, ble, blur, 0.001, multiThread); + + double sombel = 0.f; + const int ncsobel = bfh * bfw; + + array2D &deltasobelL = guid; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:sombel) if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float val = ble[ir][jr] * 32768.f; + sombel += val; + deltasobelL[ir][jr] = val; + } + } + + const float meansob = sombel / ncsobel; + Exclude_Local(deltasobelL, hueref, chromaref, lumaref, sobelref, meansob, lp, original, transformed, &bufreserv, reserved, cx, cy, sk); + } + +//encoding lab at the beginning + if (lp.logena) { + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + if (bfh >= mSP && bfw >= mSP) { + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); //buffer for data in zone limit + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); //buffer for data in zone limit + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if(multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); + lab2rgb(*bufexpfin, *tmpImage, params->icm.workingProfile); + log_encode(tmpImage.get(), lp, multiThread, bfw, bfh); + rgb2lab(*(tmpImage.get()), *bufexpfin, params->icm.workingProfile); + tmpImage.reset(); + + //here begin graduated filter + //first solution "easy" but we can do other with log_encode...to see the results + if (lp.strlog != 0.f) { + struct grad_params gplog; + calclocalGradientParams(lp, gplog, ystart, xstart, bfw, bfh, 11); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if(multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufexpfin->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gplog, jr, ir); + } + } + } + + //end graduated + transit_shapedetect2(call, 11, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + +//Prepare mask for Blur and noise and Denoise + bool denoiz = false; + + if ((lp.noiself > 0.f || lp.noiself0 > 0.f || lp.noiself2 > 0.f || lp.noiselc > 0.f || lp.noisecf > 0.f || lp.noisecc > 0.f || lp.bilat > 0.f) && lp.denoiena) { + denoiz = true; + } + + bool blurz = false; + bool delt = params->locallab.spots.at(sp).deltae; + bool astool = params->locallab.spots.at(sp).toolbl; + + if (((radius > 1.5 * GAUSS_SKIP) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 1 || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.blurena) { + blurz = true; + } + + const int GW = transformed->W; + const int GH = transformed->H; + + LabImage * originalmaskbl = nullptr; + std::unique_ptr bufmaskorigbl; + std::unique_ptr bufmaskblurbl; + std::unique_ptr bufgb; + std::unique_ptr bufprov(new LabImage(GW, GH)); + + if (denoiz || blurz || lp.denoiena || lp.blurena) { + bufgb.reset(new LabImage(GW, GH)); + + if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { + bufmaskorigbl.reset(new LabImage(GW, GH)); + bufmaskblurbl.reset(new LabImage(GW, GH)); + originalmaskbl = new LabImage(GW, GH); + } + + array2D ble(GW, GH); + array2D blechro(GW, GH); + array2D hue(GW, GH); + array2D guid(GW, GH); + float meanfab, fab; + mean_fab(0, 0, GW, GH, bufgb.get(), original, fab, meanfab, lp.chromabl, multiThread); + float chromult = 1.f - 0.01f * lp.chromabl; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH; y++) { + for (int x = 0; x < GW; x++) { + bufgb->L[y][x] = original->L[y][x]; + bufgb->a[y][x] = original->a[y][x]; + bufgb->b[y][x] = original->b[y][x]; + } + } + + const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskbl; + JaggedArray blendstru(GW, GH); + + if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { + if (strumask > 0.f) { + float delstrumask = 4.1f - strumask;//4.1 = 2 * max slider strumask + 0.1 + buildBlendMask(bufgb->L, blendstru, GW, GH, delstrumask); + const float radblur = 0.02f * 0.1f * std::fabs(lp.radmabl); + const float rm = radblur / sk; + + if (rm > 0) { +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(blendstru, blendstru, GW, GH, rm); + } + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + float kmaskLexp = 0.f; + float kmaskCH = 0.f; + float kmasstru = 0.f; + + if (strumask > 0.f && !astool) { + kmasstru = bufgb->L[ir][jr] * blendstru[ir][jr]; + } + + if (locllmasblCurve && llmasblutili) { + const float ligh = bufgb->L[ir][jr] / 32768.f; + kmaskLexp = 32768.f * LIM01(1.f - locllmasblCurve[500.f * ligh]); + } + + if (lp.showmaskblmet != 4) { + if (locccmasblCurve && lcmasblutili) { + const float chromask = 0.0001f + std::sqrt(SQR((bufgb->a[ir][jr]) / fab) + SQR((bufgb->b[ir][jr]) / fab)); + kmaskCH = LIM01(1.f - locccmasblCurve[500.f * chromask]); + } + } + + if (lochhmasblCurve && lhmasblutili) { + const float huema = xatan2f(bufgb->b[ir][jr], bufgb->a[ir][jr]); + float h = Color::huelab_to_huehsv2(huema); + h += 1.f / 6.f; + + if (h > 1.f) { + h -= 1.f; + } + + const float valHH = LIM01(1.f - lochhmasblCurve[500.f * h]); + + if (lp.showmaskblmet != 4) { + kmaskCH += chromult * valHH; + } + + kmaskLexp += 32768.f * valHH; + } + + bufmaskblurbl->L[ir][jr] = clipLoc(kmaskLexp + kmasstru); + bufmaskblurbl->a[ir][jr] = kmaskCH; + bufmaskblurbl->b[ir][jr] = kmaskCH; + ble[ir][jr] = bufmaskblurbl->L[ir][jr] / 32768.f; + hue[ir][jr] = xatan2f(bufmaskblurbl->b[ir][jr], bufmaskblurbl->a[ir][jr]); + const float chromah = std::sqrt(SQR(bufmaskblurbl->b[ir][jr]) + SQR(bufmaskblurbl->a[ir][jr])); + blechro[ir][jr] = chromah / 32768.f; + guid[ir][jr] = Color::L2Y(bufgb->L[ir][jr]) / 32768.f; + } + } + + const std::unique_ptr bufprov(new LabImage(GW, GH)); + + bufprov->CopyFrom(bufmaskblurbl.get(), multiThread); + + if (lp.radmabl != 0.f) { + float blur = lp.radmabl; + blur = blur < 0.f ? -1.f / blur : 1.f + blur; + const int r1 = rtengine::max(4 / sk * blur + 0.5f, 1); + const int r2 = rtengine::max(25 / sk * blur + 0.5f, 1); + + constexpr float epsilmax = 0.0005f; + constexpr float epsilmin = 0.00001f; + + const float aepsil = (epsilmax - epsilmin) / 90.f; + const float bepsil = epsilmax - 100.f * aepsil; + const float epsil = lp.radmabl < 0.f ? 0.001f : aepsil * lp.radmabl + bepsil; + + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2 * epsil, multiThread); + + // guidedFilter(guid, ble, ble, lp.radmabl * 10.f / sk, 0.001, multiThread, 4); + } + + LUTf lutTonemaskbl(65536); + calcGammaLut(lp.gammabl, lp.slomabl, lutTonemaskbl); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + const float2 sincosval = xsincosf(hue[ir][jr]); + bufmaskblurbl->L[ir][jr] = LIM01(ble[ir][jr]) * 32768.f; + const float L_ = 2.f * bufmaskblurbl->L[ir][jr]; + bufmaskblurbl->L[ir][jr] = lutTonemaskbl[L_]; + bufmaskblurbl->a[ir][jr] = 32768.f * sincosval.y * blechro[ir][jr]; + bufmaskblurbl->b[ir][jr] = 32768.f * sincosval.x * blechro[ir][jr]; + } + } + } + + if (strumask > 0.f && astool && (lp.enablMask || lp.showmaskblmet == 3)) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + bufmaskblurbl->L[ir][jr] *= (1.f + blendstru[ir][jr]); + } + } + } + + if (lmaskbllocalcurve && localmaskblutili && (lp.enablMask || lp.showmaskblmet == 3)) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) + for (int jr = 0; jr < GW; jr++) { + bufmaskblurbl->L[ir][jr] = 0.5f * lmaskbllocalcurve[2.f * bufmaskblurbl->L[ir][jr]]; + } + } + + const int highli = params->locallab.spots.at(sp).shadmaskbl; + + if (highli > 0 && (lp.enablMask || lp.showmaskblmet == 3)) { + ImProcFunctions::shadowsHighlights(bufmaskblurbl.get(), true, 1, highli, 0, 40, sk, 50, 0); + } + + const int shado = params->locallab.spots.at(sp).shadmaskblsha; + + if (shado > 0 && (lp.enablMask || lp.showmaskblmet == 3)) { + ImProcFunctions::shadowsHighlights(bufmaskblurbl.get(), true, 1, 0, shado, 40, sk, 0, 60); + } + + int wavelet_level = params->locallab.spots.at(sp).shadmaskbl; + int maxlvl = wavelet_level; + + int minwin = rtengine::min(GW, GH); + int maxlevelspot = 9; + + while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { + --maxlevelspot ; + } + + wavelet_level = rtengine::min(wavelet_level, maxlevelspot); + bool wavcurvemask = false; + + if (loclmasCurveblwav && lmasutiliblwav && (lp.enablMask || lp.showmaskblmet == 3)) { + for (int i = 0; i < 500; i++) { + if (loclmasCurveblwav[i] != 0.5) { + wavcurvemask = true; + } + } + } + + if (wavcurvemask && (lp.enablMask || lp.showmaskblmet == 3)) { + const int level_bl = params->locallab.spots.at(sp).csthresholdblur.getBottomLeft(); + const int level_hl = params->locallab.spots.at(sp).csthresholdblur.getTopLeft(); + const int level_br = params->locallab.spots.at(sp).csthresholdblur.getBottomRight(); + const int level_hr = params->locallab.spots.at(sp).csthresholdblur.getTopRight(); + +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + + wavelet_decomposition *wdspotbl = new wavelet_decomposition(bufmaskblurbl->L[0], GW, GH, maxlvl, 1, sk, numThreads, lp.daubLen); + if (wdspotbl->memory_allocation_failed()) { + return; + } + + + float mean[10]; + float meanN[10]; + float sigma[10]; + float sigmaN[10]; + float MaxP[10]; + float MaxN[10]; + + Evaluate2(*wdspotbl, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); + float alow = 1.f; + float blow = 0.f; + if (level_hl != level_bl) { + alow = 1.f / (level_hl - level_bl); + blow = -alow * level_bl; + } + + float ahigh = 1.f; + float bhigh = 0.f; + + if (level_hr != level_br) { + ahigh = 1.f / (level_hr - level_br); + bhigh = -ahigh * level_br; + } + + for (int dir = 1; dir < 4; dir++) { + for (int level = level_bl; level < maxlvl; ++level) { + int W_L = wdspotbl->level_W(level); + int H_L = wdspotbl->level_H(level); + float* const *wav_L = wdspotbl->level_coeffs(level); + + if (MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { + float insigma = 0.666f; //SD + float logmax = log(MaxP[level]); //log Max + float rapX = (mean[level] + sigma[level]) / MaxP[level]; //rapport between sD / max + float inx = log(insigma); + float iny = log(rapX); + float rap = inx / iny; //koef + float asig = 0.166f / (sigma[level]); + float bsig = 0.5f - asig * mean[level]; + float amean = 0.5f / mean[level]; + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_L * H_L; i++) { + if(loclmasCurveblwav && lmasutiliblwav) { + float absciss; + float &val = wav_L[dir][i]; + + if (fabsf(val) >= (mean[level] + sigma[level])) { //for max + float valcour = xlogf(fabsf(val)); + float valc = valcour - logmax; + float vald = valc * rap; + absciss = xexpf(vald); + } else if (fabsf(val) >= mean[level]) { + absciss = asig * fabsf(val) + bsig; + } else { + absciss = amean * fabsf(val); + } + + float klev = 1.f; + if (level >= level_hl && level <= level_hr) { + klev = 1.f; + } + + if (level_hl != level_bl) { + if (level >= level_bl && level < level_hl) { + klev = alow * level + blow; + } + } + + if (level_hr != level_br) { + if (level > level_hr && level <= level_br) { + klev = ahigh * level + bhigh; + } + } + float kc = klev * (loclmasCurveblwav[absciss * 500.f] - 0.5f); + float amplieffect = kc <= 0.f ? 1.f : 4.f; + + float kinterm = 1.f + amplieffect * kc; + kinterm = kinterm <= 0.f ? 0.01f : kinterm; + + val *= kinterm; + + } + } + } + + } + } + wdspotbl->reconstruct(bufmaskblurbl->L[0], 1.f); + delete wdspotbl; + + } + + + // deltae Mask with scope + int sco = params->locallab.spots.at(sp).scopemask; + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + + if (delt && lp.blurmet == 0 && (lp.enablMask || lp.showmaskblmet == 3)) { + JaggedArray rdE(GW, GH); + deltaEforMask(rdE, GW, GH, bufgb.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco, lp.balance, lp.balanceh); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) { + for (int jr = 0; jr < GW; jr++) { + bufmaskblurbl->L[ir][jr] = bufprov->L[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->L[ir][jr] - bufprov->L[ir][jr]); + bufmaskblurbl->a[ir][jr] = bufprov->a[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->a[ir][jr] - bufprov->a[ir][jr]); + bufmaskblurbl->b[ir][jr] = bufprov->b[ir][jr] + rdE[ir][jr] * (bufmaskblurbl->b[ir][jr] - bufprov->b[ir][jr]); + } + } + } + + const float lap = params->locallab.spots.at(sp).lapmaskbl; + const bool pde = params->locallab.spots.at(sp).laplac; + const float lumask = params->locallab.spots.at(sp).lumask; + + if (lap > 0.f && (lp.enablMask || lp.showmaskblmet == 3)) { + const float *datain = bufmaskblurbl->L[0]; + const std::unique_ptr data_tmp(new float[GH * GW]); + + if (!pde) { + ImProcFunctions::discrete_laplacian_threshold(data_tmp.get(), datain, GW, GH, 200.f * lap); + } else { + ImProcFunctions::retinex_pde(datain, data_tmp.get(), GW, GH, 12.f * lap, 1.f, nullptr, 0, 0, 1); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < GH; y++) { + for (int x = 0; x < GW; x++) { + bufmaskblurbl->L[y][x] = data_tmp[y * GW + x]; + } + } + } + + const float radiusb = 1.f / sk; + + if (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) { + const int invers = lp.blurmet == 1 ? 1 : 0; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufmaskblurbl->L, bufmaskorigbl->L, GW, GH, radiusb); + gaussianBlur(bufmaskblurbl->a, bufmaskorigbl->a, GW, GH, 1.f + (0.005f * lp.radmabl) / sk); + gaussianBlur(bufmaskblurbl->b, bufmaskorigbl->b, GW, GH, 1.f + (0.005f * lp.radmabl) / sk); + } + + if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { + blendmask(lp, 0, 0, cx, cy, GW, GH, bufgb.get(), original, bufmaskorigbl.get(), originalmaskbl, lp.blendmabl, invers); + } else if (lp.showmaskblmet == 3) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufgb.get(), transformed, bufmaskorigbl.get(), invers); + return; + } + } + +//end mask + } + + bool execmaskblur = (lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.smasktyp != 1; + if (((radius > 1.5 * GAUSS_SKIP && lp.rad > 1.6) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 0 || execmaskblur) && lp.blurena) { // radius < GAUSS_SKIP means no gauss, just copy of original image + // if (((radius > 1.5 * GAUSS_SKIP && lp.rad > 1.6) || lp.stren > 0.1 || lp.blmet == 1 || lp.guidb > 0 || lp.showmaskblmet == 2 || lp.enablMask || lp.showmaskblmet == 3 || lp.showmaskblmet == 4) && lp.blurena) { // radius < GAUSS_SKIP means no gauss, just copy of original image + std::unique_ptr tmp1; + std::unique_ptr tmp2; + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + int bfhr = bfh; + int bfwr = bfw; + + bool fft = params->locallab.spots.at(sp).fftwbl; + int isogr = params->locallab.spots.at(sp).isogr; + int strengr = params->locallab.spots.at(sp).strengr; + int scalegr = params->locallab.spots.at(sp).scalegr; + + + + if (bfw >= mSP && bfh >= mSP) { + if (lp.blurmet == 0 && (fft || lp.rad > 30.f)) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + const std::unique_ptr bufgbi(new LabImage(GW, GH)); + + //here mask is used with plain image for normal and inverse + //if it is possible to optimize with maskcalccol(), I don't to preserve visibility + if (lp.showmaskblmet == 0 || lp.showmaskblmet == 1 || lp.showmaskblmet == 2 || lp.showmaskblmet == 4 || lp.enablMask) { + + if (lp.blurmet == 0) { + if (bfw > 0 && bfh > 0) { + tmp1.reset(new LabImage(bfw, bfh)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + tmp1->L[y - ystart][x - xstart] = original->L[y][x]; + tmp1->a[y - ystart][x - xstart] = original->a[y][x]; + tmp1->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + } + } else if (lp.blurmet == 1) { + tmp1.reset(new LabImage(transformed->W, transformed->H)); + tmp2.reset(new LabImage(transformed->W, transformed->H)); + + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + tmp2->L[y][x] = original->L[y][x]; + tmp2->a[y][x] = original->a[y][x]; + tmp2->b[y][x] = original->b[y][x]; + bufgbi->L[y][x] = original->L[y][x]; + bufgbi->a[y][x] = original->a[y][x]; + bufgbi->b[y][x] = original->b[y][x]; + } + } + + } + + + if (lp.blurmet == 0 && lp.blmet == 0 && radius > (1.5 * GAUSS_SKIP) && lp.rad > 1.6) { + if (fft || lp.rad > 30.f) { + if (lp.chromet == 0) { + ImProcFunctions::fftw_convol_blur2(tmp1->L, tmp1->L, bfwr, bfhr, radius, 0, 0); + } else if (lp.chromet == 1) { + ImProcFunctions::fftw_convol_blur2(tmp1->a, tmp1->a, bfwr, bfhr, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(tmp1->b, tmp1->b, bfwr, bfhr, radius, 0, 0); + } else if (lp.chromet == 2) { + ImProcFunctions::fftw_convol_blur2(tmp1->L, tmp1->L, bfwr, bfhr, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(tmp1->a, tmp1->a, bfwr, bfhr, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(tmp1->b, tmp1->b, bfwr, bfhr, radius, 0, 0); + } + } else { + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + if (lp.chromet == 0) + { + gaussianBlur(tmp1->L, tmp1->L, bfw, bfh, radius); + } + + else if (lp.chromet == 1) + { + gaussianBlur(tmp1->a, tmp1->a, bfw, bfh, radius); + gaussianBlur(tmp1->b, tmp1->b, bfw, bfh, radius); + } else if (lp.chromet == 2) + { + gaussianBlur(tmp1->L, tmp1->L, bfw, bfh, radius); + gaussianBlur(tmp1->a, tmp1->a, bfw, bfh, radius); + gaussianBlur(tmp1->b, tmp1->b, bfw, bfh, radius); + } + } + } + + } else if (lp.blurmet == 1 && lp.blmet == 0 && radius > (1.5 * GAUSS_SKIP) && lp.rad > 1.6) { + if (fft || lp.rad > 30.f) { + if (lp.chromet == 0) { + ImProcFunctions::fftw_convol_blur2(original->L, tmp1->L, GW, GH, radius, 0, 0); + } + + else if (lp.chromet == 1) { + ImProcFunctions::fftw_convol_blur2(original->a, tmp1->a, GW, GH, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(original->b, tmp1->b, GW, GH, radius, 0, 0); + } else if (lp.chromet == 2) { + ImProcFunctions::fftw_convol_blur2(original->L, tmp1->L, GW, GH, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(original->a, tmp1->a, GW, GH, radius, 0, 0); + ImProcFunctions::fftw_convol_blur2(original->b, tmp1->b, GW, GH, radius, 0, 0); + } + + } else { + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + if (lp.chromet == 0) + { + gaussianBlur(original->L, tmp1->L, GW, GH, radius); + } else if (lp.chromet == 1) + + { + gaussianBlur(original->a, tmp1->a, GW, GH, radius); + gaussianBlur(original->b, tmp1->b, GW, GH, radius); + } else if (lp.chromet == 2) + + { + gaussianBlur(original->L, tmp1->L, GW, GH, radius); + gaussianBlur(original->a, tmp1->a, GW, GH, radius); + gaussianBlur(original->b, tmp1->b, GW, GH, radius); + } + } + } + } + + + //add noise + if (tmp1.get() && lp.stren > 0.1f && lp.blmet == 0) { + float mean = 0.f;//0 best result + float variance = lp.stren ; + addGaNoise(tmp1.get(), tmp1.get(), mean, variance, sk) ; + } + + //add grain + if (lp.blmet == 0 && strengr > 0) { + int wi = bfw; + int he = bfh; + + if (lp.blurmet == 1) { + wi = GW; + he = GH; + } + + if (tmp1.get()) { + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(wi, he); + + for (int y = 0; y < he ; y++) { + for (int x = 0; x < wi; x++) { + tmpImage->g(y, x) = tmp1->L[y][x]; + tmpImage->r(y, x) = tmp1->a[y][x]; + tmpImage->b(y, x) = tmp1->b[y][x]; + } + } + + + filmGrain(tmpImage, isogr, strengr, scalegr, wi, he); + + for (int y = 0; y < he ; y++) { + for (int x = 0; x < wi; x++) { + tmp1->L[y][x] = tmpImage->g(y, x); + tmp1->a[y][x] = tmpImage->r(y, x); + tmp1->b[y][x] = tmpImage->b(y, x); + } + } + + delete tmpImage; + } + } + + Median medianTypeL = Median::TYPE_3X3_STRONG; + Median medianTypeAB = Median::TYPE_3X3_STRONG; + + if (lp.medmet == 0) { + medianTypeL = medianTypeAB = Median::TYPE_3X3_STRONG; + } else if (lp.medmet == 1) { + medianTypeL = medianTypeAB = Median::TYPE_5X5_STRONG; + } else if (lp.medmet == 2) { + medianTypeL = medianTypeAB = Median::TYPE_7X7; + } else if (lp.medmet == 3) { + medianTypeL = medianTypeAB = Median::TYPE_9X9; + } + + if (lp.blurmet == 0 && lp.blmet == 1 && lp.medmet != -1) { + float** tmL; + int wid = bfw; + int hei = bfh; + tmL = new float*[hei]; + + for (int i = 0; i < hei; ++i) { + tmL[i] = new float[wid]; + } + + if (lp.chromet == 0) { + Median_Denoise(tmp1->L, tmp1->L, bfw, bfh, medianTypeL, lp.it, multiThread, tmL); + } + + else if (lp.chromet == 1) { + Median_Denoise(tmp1->a, tmp1->a, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp1->b, tmp1->b, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + } else if (lp.chromet == 2) { + Median_Denoise(tmp1->L, tmp1->L, bfw, bfh, medianTypeL, lp.it, multiThread, tmL); + Median_Denoise(tmp1->a, tmp1->a, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp1->b, tmp1->b, bfw, bfh, medianTypeAB, lp.it, multiThread, tmL); + } + + for (int i = 0; i < hei; ++i) { + delete[] tmL[i]; + } + + delete[] tmL; + + } else if (lp.blurmet == 1 && lp.blmet == 1) { + float** tmL; + int wid = GW; + int hei = GH; + tmL = new float*[hei]; + + for (int i = 0; i < hei; ++i) { + tmL[i] = new float[wid]; + } + + if (lp.chromet == 0) { + Median_Denoise(tmp2->L, tmp1->L, GW, GH, medianTypeL, lp.it, multiThread, tmL); + } else if (lp.chromet == 1) { + Median_Denoise(tmp2->a, tmp1->a, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp2->b, tmp1->b, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + } else if (lp.chromet == 2) { + Median_Denoise(tmp2->L, tmp1->L, GW, GH, medianTypeL, lp.it, multiThread, tmL); + Median_Denoise(tmp2->a, tmp1->a, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + Median_Denoise(tmp2->b, tmp1->b, GW, GH, medianTypeAB, lp.it, multiThread, tmL); + } + + for (int i = 0; i < hei; ++i) { + delete[] tmL[i]; + } + + delete[] tmL; + } + + if (lp.blurmet == 0 && lp.blmet == 2) { + + if (lp.guidb > 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + tmp1->L[y - ystart][x - xstart] = original->L[y][x]; + tmp1->a[y - ystart][x - xstart] = original->a[y][x]; + tmp1->b[y - ystart][x - xstart] = original->b[y][x]; + bufgb->L[y - ystart][x - xstart] = original->L[y][x]; + } + } + + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(bfw, bfh); + lab2rgb(*tmp1, *tmpImage, params->icm.workingProfile); + array2D LL(bfw, bfh); + array2D rr(bfw, bfh); + array2D gg(bfw, bfh); + array2D bb(bfw, bfh); + array2D guide(bfw, bfh); + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + LL[y][x] = tmp1->L[y][x]; + float ll = LL[y][x] / 32768.f; + guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f); + rr[y][x] = tmpImage->r(y, x); + gg[y][x] = tmpImage->g(y, x); + bb[y][x] = tmpImage->b(y, x); + + } + } + array2D iR(bfw, bfh, rr, 0); + array2D iG(bfw, bfh, gg, 0); + array2D iB(bfw, bfh, bb, 0); + array2D iL(bfw, bfh, LL, 0); + + int r = rtengine::max(int(lp.guidb / sk), 1); + + const float epsil = 0.001f * std::pow(2, - lp.epsb); + + if (lp.chromet == 0) { + rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiThread); + } else if (lp.chromet == 1) { + rtengine::guidedFilterLog(guide, 10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(guide, 10.f, bb, r, epsil, multiThread); + } else if (lp.chromet == 2) { + rtengine::guidedFilterLog(10.f, gg, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, bb, r, epsil, multiThread); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + rr[y][x] = intp(lp.strbl, rr[y][x] , iR[y][x]); + gg[y][x] = intp(lp.strbl, gg[y][x] , iG[y][x]); + bb[y][x] = intp(lp.strbl, bb[y][x] , iB[y][x]); + tmpImage->r(y, x) = rr[y][x]; + tmpImage->g(y, x) = gg[y][x]; + tmpImage->b(y, x) = bb[y][x]; + + } + } + + rgb2lab(*tmpImage, *tmp1, params->icm.workingProfile); + + if (lp.chromet == 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + LL[y][x] = intp(lp.strbl, LL[y][x] , iL[y][x]); + tmp1->L[y][x] = LL[y][x]; + } + } + } + + delete tmpImage; + } + + } else if (lp.blurmet == 1 && lp.blmet == 2) { + + if (lp.guidb > 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + tmp1->L[y][x] = original->L[y][x]; + tmp1->a[y][x] = original->a[y][x]; + tmp1->b[y][x] = original->b[y][x]; + tmp2->L[y][x] = original->L[y][x]; + } + } + + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(GW, GH); + lab2rgb(*tmp1, *tmpImage, params->icm.workingProfile); + array2D LL(GW, GH); + array2D rr(GW, GH); + array2D gg(GW, GH); + array2D bb(GW, GH); + array2D guide(GW, GH); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + LL[y][x] = tmp1->L[y][x]; + float ll = LL[y][x] / 32768.f; + guide[y][x] = xlin2log(rtengine::max(ll, 0.f), 10.f); + rr[y][x] = tmpImage->r(y, x); + gg[y][x] = tmpImage->g(y, x); + bb[y][x] = tmpImage->b(y, x); + + } + } + + array2D iR(GW, GH, rr, 0); + array2D iG(GW, GH, gg, 0); + array2D iB(GW, GH, bb, 0); + array2D iL(GW, GH, LL, 0); + + int r = rtengine::max(int(lp.guidb / sk), 1); + + const float epsil = 0.001f * std::pow(2, - lp.epsb); + + if (lp.chromet == 0) { + rtengine::guidedFilterLog(guide, 10.f, LL, r, epsil, multiThread); + } else if (lp.chromet == 1) { + rtengine::guidedFilterLog(guide, 10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(guide, 10.f, bb, r, epsil, multiThread); + } else if (lp.chromet == 2) { + rtengine::guidedFilterLog(10.f, gg, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, rr, r, epsil, multiThread); + rtengine::guidedFilterLog(10.f, bb, r, epsil, multiThread); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + rr[y][x] = intp(lp.strbl, rr[y][x] , iR[y][x]); + gg[y][x] = intp(lp.strbl, gg[y][x] , iG[y][x]); + bb[y][x] = intp(lp.strbl, bb[y][x] , iB[y][x]); + tmpImage->r(y, x) = rr[y][x]; + tmpImage->g(y, x) = gg[y][x]; + tmpImage->b(y, x) = bb[y][x]; + + } + } + + rgb2lab(*tmpImage, *tmp1, params->icm.workingProfile); + + if (lp.chromet == 0) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + LL[y][x] = intp(lp.strbl, LL[y][x] , iL[y][x]); + tmp1->L[y][x] = LL[y][x]; + } + } + } + delete tmpImage; + } + } + + if (tmp1.get()) { + JaggedArray bufchro(lp.blurmet == 1 ? GW : bfw, lp.blurmet == 1 ? GH : bfh); + float minC = std::sqrt(SQR(tmp1->a[0][0]) + SQR(tmp1->b[0][0])) - std::sqrt(SQR(bufgb->a[0][0]) + SQR(bufgb->b[0][0])); + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufchro[ir][jr] = std::sqrt(SQR(tmp1->a[ir][jr]) + SQR(tmp1->b[ir][jr])) - std::sqrt(SQR(bufgb->a[ir][jr]) + SQR(bufgb->b[ir][jr])); + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufchro[y][x] *= coefC; + } + } + } + + if (lp.blurmet == 0) { //blur and noise (center) +// BlurNoise_Local(tmp1.get(), originalmaskbl, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if(lp.smasktyp != 1) { + BlurNoise_Local(tmp1.get(), originalmaskbl, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + } else { + BlurNoise_Local(tmp1.get(), original, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } else if (lp.blurmet == 1) { + // InverseBlurNoise_Local(originalmaskbl, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); + if(lp.smasktyp != 1) { + InverseBlurNoise_Local(originalmaskbl, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); + } else { + InverseBlurNoise_Local(original, bufchro, lp, hueref, chromaref, lumaref, original, transformed, tmp1.get(), cx, cy, sk); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + } + } + + //local impulse + if ((lp.bilat > 0.f) && lp.denoiena) { + const int bfh = int (lp.ly + lp.lyT) + del; //bfw bfh real size of square zone + const int bfw = int (lp.lx + lp.lxL) + del; + + std::unique_ptr bufwv; + + if (call == 2) {//simpleprocess + bufwv.reset(new LabImage(bfw, bfh)); //buffer for data in zone limit + + const int begy = lp.yc - lp.lyT; + const int begx = lp.xc - lp.lxL; + const int yEn = lp.yc + lp.ly; + const int xEn = lp.xc + lp.lx; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = rtengine::max(0, begy - cy); y < rtengine::min(transformed->H, yEn - cy); y++) { + const int loy = cy + y; + + for (int x = rtengine::max(0, begx - cx); x < rtengine::min(transformed->W, xEn - cx); x++) { + const int lox = cx + x; + bufwv->L[loy - begy][lox - begx] = original->L[y][x]; + bufwv->a[loy - begy][lox - begx] = original->a[y][x]; + bufwv->b[loy - begy][lox - begx] = original->b[y][x]; + } + } + } else {//dcrop.cc + bufwv.reset(new LabImage(transformed->W, transformed->H)); + bufwv->CopyFrom(original, multiThread); + } //end dcrop + + const double threshold = lp.bilat / 20.0; + + if (bfh > 8 && bfw > 8) { + ImProcFunctions::impulse_nr(bufwv.get(), threshold); + } + + DeNoise_Local(call, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, *(bufwv.get()), cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + +//local denoise + + if (lp.denoiena) { + float slidL[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float slida[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + float slidb[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + constexpr int aut = 0; + DeNoise(call, del, slidL, slida, slidb, aut, noiscfactiv, lp, originalmaskbl, levred, huerefblur, lumarefblur, chromarefblur, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + if (denoiz || blurz || lp.denoiena || lp.blurena) { + delete originalmaskbl; + } + +//begin cbdl + if ((lp.mulloc[0] != 1.f || lp.mulloc[1] != 1.f || lp.mulloc[2] != 1.f || lp.mulloc[3] != 1.f || lp.mulloc[4] != 1.f || lp.mulloc[5] != 1.f || lp.clarityml != 0.f || lp.contresid != 0.f || lp.enacbMask || lp.showmaskcbmet == 2 || lp.showmaskcbmet == 3 || lp.showmaskcbmet == 4 || lp.prevdE) && lp.cbdlena) { + if (call <= 3) { //call from simpleprocess dcrop improcc + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + + if (bfw > 65 && bfh > 65) { + array2D bufsh(bfw, bfh); + JaggedArray bufchrom(bfw, bfh, true); + const std::unique_ptr loctemp(new LabImage(bfw, bfh)); + const std::unique_ptr origcbdl(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigcb; + std::unique_ptr bufmaskblurcb; + std::unique_ptr originalmaskcb; + + if (lp.showmaskcbmet == 2 || lp.enacbMask || lp.showmaskcbmet == 3 || lp.showmaskcbmet == 4) { + bufmaskorigcb.reset(new LabImage(bfw, bfh)); + bufmaskblurcb.reset(new LabImage(bfw, bfh)); + originalmaskcb.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + loctemp->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskcbmet == 3) { + showmaske = true; + } + + if (lp.enacbMask) { + enaMask = true; + } + + if (lp.showmaskcbmet == 4) { + deltaE = true; + } + + if (lp.showmaskcbmet == 2) { + modmask = true; + } + + if (lp.showmaskcbmet == 1) { + modif = true; + } + + if (lp.showmaskcbmet == 0) { + zero = true; + } + + float chrom = lp.chromacbm;; + float rad = lp.radmacb; + float gamma = lp.gammacb; + float slope = lp.slomacb; + float blendm = lp.blendmacb; + float lap = params->locallab.spots.at(sp).lapmaskcb; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int lumask = params->locallab.spots.at(sp).lumask; + int shado = 0; + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + bool lmasutilicolwav = false; + float amountcd = 0.f; + float anchorcd = 50.f; + int shortcu = 0; //lp.mergemet; //params->locallab.spots.at(sp).shortc; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, loctemp.get(), bufmaskorigcb.get(), originalmaskcb.get(), original, reserved, inv, lp, + 0.f, false, + locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskcblocalcurve, localmaskcbutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskcbmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, loctemp.get(), transformed, bufmaskorigcb.get(), 0); + + return; + } + + constexpr float b_l = -5.f; + constexpr float t_l = 25.f; + constexpr float t_r = 120.f; + constexpr float b_r = 170.f; + constexpr double skinprot = 0.; + int choice = 0; + + if (lp.showmaskcbmet == 0 || lp.showmaskcbmet == 1 || lp.showmaskcbmet == 2 || lp.showmaskcbmet == 4 || lp.enacbMask) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufsh[y - ystart][x - xstart] = origcbdl->L[y - ystart][x - xstart] = original->L[y][x]; + loctemp->a[y - ystart][x - xstart] = origcbdl->a[y - ystart][x - xstart] = original->a[y][x]; + loctemp->b[y - ystart][x - xstart] = origcbdl->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + if (lp.clarityml != 0.f && lp.mulloc[5] == 1.0) { //enabled last level to retrieve level 5 and residual image in case user not select level 5 + lp.mulloc[5] = 1.001f; + } + + if (lp.contresid != 0.f && lp.mulloc[5] == 1.0) { //enabled last level to retrieve level 5 and residual image in case user not select level 5 + lp.mulloc[5] = 1.001f; + } + + ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, lp.mulloc, 1.f, lp.threshol, lp.clarityml, lp.contresid, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); + + if (lp.softradiuscb > 0.f) { + softproc(origcbdl.get(), loctemp.get(), lp.softradiuscb, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + } + + } + + transit_shapedetect(6, loctemp.get(), originalmaskcb.get(), bufchrom, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + bool nochroma = (lp.showmaskcbmet == 2 || lp.showmaskcbmet == 1); + + //chroma CBDL begin here + if (lp.chromacb > 0.f && !nochroma) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufsh[ir][jr] = std::sqrt(SQR(loctemp->a[ir][jr]) + SQR(loctemp->b[ir][jr])); + } + } + + float multc[6]; + float clarich = 0.5f * lp.clarityml; + + if (clarich > 0.f && lp.mulloc[0] == 1.f) { //to enabled in case of user select only clarity + lp.mulloc[0] = 1.01f; + } + + if (lp.contresid != 0.f && lp.mulloc[0] == 1.f) { //to enabled in case of user select only clarity + lp.mulloc[0] = 1.01f; + } + + for (int lv = 0; lv < 6; lv++) { + multc[lv] = rtengine::max((lp.chromacb * (lp.mulloc[lv] - 1.f)) + 1.f, 0.01f); + } + + choice = 1; + ImProcFunctions::cbdl_local_temp(bufsh, loctemp->L, bfw, bfh, multc, rtengine::max(lp.chromacb, 1.f), lp.threshol, clarich, 0.f, lp.blurcbdl, skinprot, false, b_l, t_l, t_r, b_r, choice, sk, multiThread); + + + float minC = loctemp->L[0][0] - std::sqrt(SQR(loctemp->a[0][0]) + SQR(loctemp->b[0][0])); + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufchrom[ir][jr] = (loctemp->L[ir][jr] - std::sqrt(SQR(loctemp->a[ir][jr]) + SQR(loctemp->b[ir][jr]))); + minC = rtengine::min(minC, bufchrom[ir][jr]); + maxC = rtengine::max(maxC, bufchrom[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufchrom[ir][jr] *= coefC; + } + } + } + + transit_shapedetect(7, loctemp.get(), nullptr, bufchrom, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + bufsh.free(); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + } + + +//end cbdl_Local + +//vibrance + + if (lp.expvib && (lp.past != 0.f || lp.satur != 0.f || lp.strvib != 0.f || lp.war != 0 || lp.strvibab != 0.f || lp.strvibh != 0.f || lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 3 || lp.showmaskvibmet == 4 || lp.prevdE) && lp.vibena) { //interior ellipse renforced lightness and chroma //locallutili + if (call <= 3) { //simpleprocess, dcrop, improccoordinator + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + if (bfw >= mSP && bfh >= mSP) { + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigvib; + std::unique_ptr bufmaskblurvib; + std::unique_ptr originalmaskvib; + + if (lp.showmaskvibmet == 2 || lp.enavibMask || lp.showmaskvibmet == 3 || lp.showmaskvibmet == 4) { + bufmaskorigvib.reset(new LabImage(bfw, bfh)); + bufmaskblurvib.reset(new LabImage(bfw, bfh)); + originalmaskvib.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskvibmet == 3) { + showmaske = true; + } + + if (lp.enavibMask) { + enaMask = true; + } + + if (lp.showmaskvibmet == 4) { + deltaE = true; + } + + if (lp.showmaskvibmet == 2) { + modmask = true; + } + + if (lp.showmaskvibmet == 1) { + modif = true; + } + + if (lp.showmaskvibmet == 0) { + zero = true; + } + + float chrom = lp.chromavib; + float rad = lp.radmavib; + float gamma = lp.gammavib; + float slope = lp.slomavib; + float blendm = lp.blendmavib; + float lap = params->locallab.spots.at(sp).lapmaskvib; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + float amountcd = 0.f; + float anchorcd = 50.f; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigvib.get(), originalmaskvib.get(), original, reserved, inv, lp, + 0.f, false, + locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskviblocalcurve, localmaskvibutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskvibmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskorigvib.get(), 0); + + return; + } + + if (lp.showmaskvibmet == 0 || lp.showmaskvibmet == 1 || lp.showmaskvibmet == 2 || lp.showmaskvibmet == 4 || lp.enavibMask) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + VibranceParams vibranceParams; + vibranceParams.enabled = params->locallab.spots.at(sp).expvibrance; + vibranceParams.pastels = params->locallab.spots.at(sp).pastels; + vibranceParams.saturated = params->locallab.spots.at(sp).saturated; + vibranceParams.psthreshold = params->locallab.spots.at(sp).psthreshold; + vibranceParams.protectskins = params->locallab.spots.at(sp).protectskins; + vibranceParams.avoidcolorshift = params->locallab.spots.at(sp).avoidcolorshift; + vibranceParams.pastsattog = params->locallab.spots.at(sp).pastsattog; + vibranceParams.skintonescurve = params->locallab.spots.at(sp).skintonescurve; + + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + + if (lp.strvibh != 0.f) { + struct grad_params gph; + calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 9); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gph, jr, ir); + float aa = bufexpfin->a[ir][jr]; + float bb = bufexpfin->b[ir][jr]; + float chrm = std::sqrt(SQR(aa) + SQR(bb)); + float HH = xatan2f(bb, aa); + + float newhr = 0.f; + float cor = 0.f; + + if (factor < 1.f) { + cor = - 2.5f * (1.f - factor); + } else if (factor > 1.f) { + cor = 0.03f * (factor - 1.f); + } + + newhr = HH + cor; + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + + float2 sincosval = xsincosf(newhr); + bufexpfin->a[ir][jr] = clipC(chrm * sincosval.y); + bufexpfin->b[ir][jr] = clipC(chrm * sincosval.x); + } + } + + if (lp.strvib != 0.f) { + struct grad_params gp; + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 7); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufexpfin->L[ir][jr] *= factor; + } + } + + if (lp.strvibab != 0.f) { + struct grad_params gpab; + calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 8); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); + bufexpfin->a[ir][jr] *= factor; + bufexpfin->b[ir][jr] *= factor; + } + } + + ImProcFunctions::vibrance(bufexpfin.get(), vibranceParams, params->toneCurve.hrenabled, params->icm.workingProfile); + + if (params->locallab.spots.at(sp).warm != 0) { + ImProcFunctions::ciecamloc_02float(sp, bufexpfin.get()); + } + + + transit_shapedetect2(call, 2, bufexporig.get(), bufexpfin.get(), originalmaskvib.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + + } + + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + + +//Tone mapping + + if ((lp.strengt != 0.f || lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4 || lp.prevdE) && lp.tonemapena && !params->epd.enabled) { + if (call <= 3) { //simpleprocess dcrop improcc + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + if (bfw >= mSP && bfh >= mSP) { + array2D buflight(bfw, bfh); + JaggedArray bufchro(bfw, bfh); + std::unique_ptr bufgb(new LabImage(bfw, bfh)); + const std::unique_ptr tmp1(new LabImage(bfw, bfh)); + const std::unique_ptr bufgbm(new LabImage(bfw, bfh)); + const std::unique_ptr tmp1m(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigtm; + std::unique_ptr bufmaskblurtm; + std::unique_ptr originalmasktm; + + // if (lp.showmasktmmet == 0 || lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4) { + if (lp.showmasktmmet == 2 || lp.enatmMask || lp.showmasktmmet == 3 || lp.showmasktmmet == 4) { + bufmaskorigtm.reset(new LabImage(bfw, bfh)); + bufmaskblurtm.reset(new LabImage(bfw, bfh)); + originalmasktm.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufgb->L[y - ystart][x - xstart] = original->L[y][x]; + bufgb->a[y - ystart][x - xstart] = original->a[y][x]; + bufgb->b[y - ystart][x - xstart] = original->b[y][x]; + bufgbm->L[y - ystart][x - xstart] = original->L[y][x]; + bufgbm->a[y - ystart][x - xstart] = original->a[y][x]; + bufgbm->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmasktmmet == 3) { + showmaske = true; + } + + if (lp.enatmMask) { + enaMask = true; + } + + if (lp.showmasktmmet == 4) { + deltaE = true; + } + + if (lp.showmasktmmet == 2) { + modmask = true; + } + + if (lp.showmasktmmet == 1) { + modif = true; + } + + if (lp.showmasktmmet == 0) { + zero = true; + } + + float chrom = lp.chromatm;; + float rad = lp.radmatm; + float gamma = lp.gammatm; + float slope = lp.slomatm; + float blendm = lp.blendmatm; + float lap = params->locallab.spots.at(sp).lapmasktm; + bool pde = params->locallab.spots.at(sp).laplac; + int lumask = params->locallab.spots.at(sp).lumask; + + if (!params->locallab.spots.at(sp).enatmMaskaft) { + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0; //lp.mergemet;// params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = 0.f; + float anchorcd = 50.f; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgbm.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, + 0.f, false, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmasktmmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufgbm.get(), transformed, bufmaskorigtm.get(), 0); + + return; + } + } + + if (lp.showmasktmmet == 0 || lp.showmasktmmet == 1 || lp.showmasktmmet == 2 || lp.showmasktmmet == 4 || lp.showmasktmmet == 3 || lp.enatmMask) { + constexpr int itera = 0; + ImProcFunctions::EPDToneMaplocal(sp, bufgb.get(), tmp1.get(), itera, sk);//iterate to 0 calculate with edgstopping, improve result, call=1 dcrop we can put iterate to 5 + + tmp1m->CopyFrom(tmp1.get(), multiThread); //save current result + bool enatmMasktmap = params->locallab.spots.at(sp).enatmMaskaft; + + if (enatmMasktmap) { + //calculate new values for original, originalmasktm, bufmaskorigtm...in function of tmp1 + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + int lumask = params->locallab.spots.at(sp).lumask; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = 0.f; + float anchorcd = 50.f; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, tmp1.get(), bufmaskorigtm.get(), originalmasktm.get(), original, reserved, inv, lp, + 0.f, false, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasktmlocalcurve, localmasktmutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmasktmmet == 3) {//display mask + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, tmp1.get(), transformed, bufmaskorigtm.get(), 0); + + return; + } + + } + + tmp1->CopyFrom(tmp1m.get(), multiThread); //restore current result + + + float minL = tmp1->L[0][0] - bufgb->L[0][0]; + float maxL = minL; + float minC = std::sqrt(SQR(tmp1->a[0][0]) + SQR(tmp1->b[0][0])) - std::sqrt(SQR(bufgb->a[0][0]) + SQR(bufgb->b[0][0])); + float maxC = minC; + +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxL) reduction(min:minL) reduction(max:maxC) reduction(min:minC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + buflight[ir][jr] = tmp1->L[ir][jr] - bufgb->L[ir][jr]; + minL = rtengine::min(minL, buflight[ir][jr]); + maxL = rtengine::max(maxL, buflight[ir][jr]); + bufchro[ir][jr] = std::sqrt(SQR(tmp1->a[ir][jr]) + SQR(tmp1->b[ir][jr])) - std::sqrt(SQR(bufgb->a[ir][jr]) + SQR(bufgb->b[ir][jr])); + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coef == 0.f) { + coef = 1.f; + } else { + coef = 1.f / coef; + } + + if (coefC == 0.f) { + coefC = 1.f; + } else { + coefC = 1.f / coefC; + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + buflight[y][x] *= coef; + bufchro[y][x] *= coefC; + } + } + + // transit_shapedetect_retinex(call, 4, bufgb.get(),bufmaskorigtm.get(), originalmasktm.get(), buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + transit_shapedetect2(call, 8, bufgb.get(), tmp1.get(), originalmasktm.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + // transit_shapedetect(8, tmp1.get(), originalmasktm.get(), bufchro, false, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + bufgb.reset(); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } + } + +//end TM + + +//shadow highlight + bool tonequ = false; + + if (lp.mullocsh[0] != 0 || lp.mullocsh[1] != 0 || lp.mullocsh[2] != 0 || lp.mullocsh[3] != 0 || lp.mullocsh[4] != 0) { + tonequ = true; + } + + bool tonecurv = false; + + if (params->locallab.spots.at(sp).gamSH != 2.4 || params->locallab.spots.at(sp).sloSH != 12.92) { + tonecurv = true; + } + + if (! lp.invsh && (lp.highlihs > 0.f || lp.shadowhs > 0.f || tonequ || tonecurv || lp.strSH != 0.f || lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 3 || lp.showmaskSHmet == 4 || lp.prevdE) && call < 3 && lp.hsena) { + const int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + const int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + const int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + const int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + const int bfh = yend - ystart; + const int bfw = xend - xstart; + + + if (bfw >= mSP && bfh >= mSP) { + + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + std::unique_ptr bufmaskorigSH; + std::unique_ptr bufmaskblurSH; + std::unique_ptr originalmaskSH; + + if (lp.showmaskSHmet == 2 || lp.enaSHMask || lp.showmaskSHmet == 3 || lp.showmaskSHmet == 4) { + bufmaskorigSH.reset(new LabImage(bfw, bfh)); + bufmaskblurSH.reset(new LabImage(bfw, bfh)); + originalmaskSH.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskSHmet == 3) { + showmaske = true; + } + + if (lp.enaSHMask) { + enaMask = true; + } + + if (lp.showmaskSHmet == 4) { + deltaE = true; + } + + if (lp.showmaskSHmet == 2) { + modmask = true; + } + + if (lp.showmaskSHmet == 1) { + modif = true; + } + + if (lp.showmaskSHmet == 0) { + zero = true; + } + + float chrom = lp.chromaSH; + float rad = lp.radmaSH; + float gamma = lp.gammaSH; + float slope = lp.slomaSH; + float blendm = lp.blendmaSH; + float lap = params->locallab.spots.at(sp).lapmaskSH; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = params->locallab.spots.at(sp).fatamountSH; + float anchorcd = params->locallab.spots.at(sp).fatanchorSH; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskorigSH.get(), originalmaskSH.get(), original, reserved, inv, lp, + 0.f, false, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskSHmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskorigSH.get(), 0); + + return; + } + + if (lp.showmaskSHmet == 0 || lp.showmaskSHmet == 1 || lp.showmaskSHmet == 2 || lp.showmaskSHmet == 4 || lp.enaSHMask) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufexporig->L[y][x] = original->L[y + ystart][x + xstart]; + bufexporig->a[y][x] = original->a[y + ystart][x + xstart]; + bufexporig->b[y][x] = original->b[y + ystart][x + xstart]; + bufexpfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufexpfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufexpfin->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + if (lp.shmeth == 0) { + ImProcFunctions::shadowsHighlights(bufexpfin.get(), lp.hsena, 1, lp.highlihs, lp.shadowhs, lp.radiushs, sk, lp.hltonalhs, lp.shtonalhs); + } + +//gradient + struct grad_params gp; + + if (lp.strSH != 0.f) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 2); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + double factor = 1.0; + factor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufexpfin->L[ir][jr] *= factor; + } + } + + if (lp.shmeth == 1) { + double scal = (double)(sk); + Imagefloat *tmpImage = nullptr; + tmpImage = new Imagefloat(bfw, bfh); + lab2rgb(*bufexpfin, *tmpImage, params->icm.workingProfile); + + if (tonecurv) { //Tone response curve : does nothing if gamma=2.4 and slope=12.92 ==> gamma sRGB + float gamtone = params->locallab.spots.at(sp).gamSH; + float slotone = params->locallab.spots.at(sp).sloSH; + cmsHTRANSFORM dummy = nullptr; + workingtrc(tmpImage, tmpImage, bfw, bfh, -5, params->icm.workingProfile, 2.4, 12.92310, dummy, true, false, false); + workingtrc(tmpImage, tmpImage, bfw, bfh, 5, params->icm.workingProfile, gamtone, slotone, dummy, false, true, true); + } + + if (tonequ) { + tmpImage->normalizeFloatTo1(); + array2D Rtemp(bfw, bfh, tmpImage->r.ptrs, ARRAY2D_BYREFERENCE); + array2D Gtemp(bfw, bfh, tmpImage->g.ptrs, ARRAY2D_BYREFERENCE); + array2D Btemp(bfw, bfh, tmpImage->b.ptrs, ARRAY2D_BYREFERENCE); + tone_eq(Rtemp, Gtemp, Btemp, lp, params->icm.workingProfile, scal, multiThread); + tmpImage->normalizeFloatTo65535(); + } + + rgb2lab(*tmpImage, *bufexpfin, params->icm.workingProfile); + + delete tmpImage; + } + } + + transit_shapedetect2(call, 9, bufexporig.get(), bufexpfin.get(), originalmaskSH.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } else if (lp.invsh && (lp.highlihs > 0.f || lp.shadowhs > 0.f || tonequ || tonecurv || lp.showmaskSHmetinv == 1 || lp.enaSHMaskinv) && call < 3 && lp.hsena) { + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskSH; + const std::unique_ptr bufcolorig(new LabImage(GW, GH)); + + if (lp.enaSHMaskinv || lp.showmaskSHmetinv == 1) { + bufmaskblurcol.reset(new LabImage(GW, GH, true)); + originalmaskSH.reset(new LabImage(GW, GH)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + bufcolorig->L[y][x] = original->L[y][x]; + } + } + + int inv = 1; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskSHmetinv == 1) { + showmaske = true; + } + + if (lp.enaSHMaskinv) { + enaMask = true; + } + + if (lp.showmaskSHmetinv == 0) { + zero = true; + } + + float chrom = lp.chromaSH; + float rad = lp.radmaSH; + float gamma = lp.gammaSH; + float slope = lp.slomaSH; + float blendm = lp.blendmaSH; + float lap = params->locallab.spots.at(sp).lapmaskSH; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + // bool delt = params->locallab.spots.at(sp).deltae; + bool delt = false; + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + int shado = 0; + float amountcd = params->locallab.spots.at(sp).fatamountSH; + float anchorcd = params->locallab.spots.at(sp).fatanchorSH; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskSH.get(), original, reserved, inv, lp, + 0.f, false, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskSHlocalcurve, localmaskSHutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + + if (lp.showmaskSHmetinv == 1) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufcolorig.get(), transformed, bufmaskblurcol.get(), inv); + + return; + } + + float adjustr = 2.f; + InverseColorLight_Local(tonequ, tonecurv, sp, 2, lp, originalmaskSH.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + +// soft light and retinex_pde + if (lp.strng > 0.f && call <= 3 && lp.sfena) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + //variable for fast FFTW + int bfhr = bfh; + int bfwr = bfw; + + if (bfw >= mSP && bfh >= mSP) { + + if (lp.softmet == 1) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + SoftLightParams softLightParams; + softLightParams.enabled = true; + softLightParams.strength = lp.strng; + + if (lp.softmet == 0) { + ImProcFunctions::softLight(bufexpfin.get(), softLightParams); + } else if (lp.softmet == 1) { + + const std::unique_ptr datain(new float[bfwr * bfhr]); + const std::unique_ptr dataout(new float[bfwr * bfhr]); + const std::unique_ptr dE(new float[bfwr * bfhr]); + + deltaEforLaplace(dE.get(), lp.lap, bfwr, bfhr, bufexpfin.get(), hueref, chromaref, lumaref); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + datain[y * bfwr + x] = bufexpfin->L[y][x]; + } + } + + const int showorig = lp.showmasksoftmet >= 5 ? 0 : lp.showmasksoftmet; + MyMutex::MyLock lock(*fftwMutex); + ImProcFunctions::retinex_pde(datain.get(), dataout.get(), bfwr, bfhr, 8.f * lp.strng, 1.f, dE.get(), showorig, 1, 1); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + bufexpfin->L[y][x] = dataout[y * bfwr + x]; + } + } + } + + transit_shapedetect2(call, 3, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + //local contrast + bool wavcurve = false; + bool wavcurvelev = false; + bool wavcurvecon = false; + bool wavcurvecomp = false; + bool wavcurvecompre = false; + + if (lp.locmet == 1) { + if (locwavCurve && locwavutili) { + for (int i = 0; i < 500; i++) { + if (locwavCurve[i] != 0.5) { + wavcurve = true; + break; + } + } + } + if (loclevwavCurve && loclevwavutili) { + for (int i = 0; i < 500; i++) { + if (loclevwavCurve[i] != 0.) { + wavcurvelev = true; + break; + } + } + } + if (locconwavCurve && locconwavutili) { + for (int i = 0; i < 500; i++) { + if (locconwavCurve[i] != 0.5) { + wavcurvecon = true; + break; + } + } + } + if (loccompwavCurve && loccompwavutili) { + for (int i = 0; i < 500; i++) { + if (loccompwavCurve[i] != 0.) { + wavcurvecomp = true; + break; + } + } + } + if (loccomprewavCurve && loccomprewavutili) { + for (int i = 0; i < 500; i++) { + if (loccomprewavCurve[i] != 0.75) { + wavcurvecompre = true; + break; + } + } + } + } + + if ((lp.lcamount > 0.f || wavcurve || lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 3 || lp.showmasklcmet == 4 || lp.prevdE || lp.strwav != 0.f || wavcurvelev || wavcurvecon || wavcurvecomp || wavcurvecompre || lp.edgwena || params->locallab.spots.at(sp).residblur > 0.f || params->locallab.spots.at(sp).levelblur > 0.f || params->locallab.spots.at(sp).residcont != 0.f || params->locallab.spots.at(sp).clarilres != 0.f || params->locallab.spots.at(sp).claricres != 0.f) && call < 3 && lp.lcena) { + + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + int bfhr = bfh; + int bfwr = bfw; + + if (bfw >= mSPwav && bfh >= mSPwav) {//avoid too small spot for wavelet + if (lp.ftwlc) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + std::unique_ptr bufmaskblurlc; + std::unique_ptr originalmasklc; + std::unique_ptr bufmaskoriglc; + + if (lp.showmasklcmet == 2 || lp.enalcMask || lp.showmasklcmet == 3 || lp.showmasklcmet == 4) { + bufmaskblurlc.reset(new LabImage(bfw, bfh)); + originalmasklc.reset(new LabImage(bfw, bfh)); + bufmaskoriglc.reset(new LabImage(bfw, bfh)); + } + + array2D buflight(bfw, bfh); + JaggedArray bufchro(bfw, bfh); + const std::unique_ptr bufgb(new LabImage(bfw, bfh)); + std::unique_ptr tmp1(new LabImage(bfw, bfh)); + const std::unique_ptr tmpresid(new LabImage(bfw, bfh)); + const std::unique_ptr tmpres(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufgb->L[y - ystart][x - xstart] = original->L[y][x]; + bufgb->a[y - ystart][x - xstart] = original->a[y][x]; + bufgb->b[y - ystart][x - xstart] = original->b[y][x]; + tmp1->L[y - ystart][x - xstart] = original->L[y][x]; + tmp1->a[y - ystart][x - xstart] = original->a[y][x]; + tmp1->b[y - ystart][x - xstart] = original->b[y][x]; + tmpresid->L[y - ystart][x - xstart] = original->L[y][x]; + tmpresid->a[y - ystart][x - xstart] = original->a[y][x]; + tmpresid->b[y - ystart][x - xstart] = original->b[y][x]; + tmpres->L[y - ystart][x - xstart] = original->L[y][x]; + tmpres->a[y - ystart][x - xstart] = original->a[y][x]; + tmpres->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufgb->L[y][x] = original->L[y + ystart][x + xstart]; + } + } + + int inv = 0; + bool showmaske = false; + bool enaMask = false; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmasklcmet == 3) { + showmaske = true; + } + + if (lp.enalcMask) { + enaMask = true; + } + + if (lp.showmasklcmet == 4) { + deltaE = true; + } + + if (lp.showmasklcmet == 2) { + modmask = true; + } + + if (lp.showmasklcmet == 1) { + modif = true; + } + + if (lp.showmasklcmet == 0) { + zero = true; + } + + + float chrom = lp.chromalc; + float rad = lp.radmalc; + float blendm = lp.blendmalc; + float gamma = 1.f; + float slope = 0.f; + float lap = 0.f; //params->locallab.spots.at(sp).lapmaskexp; + bool pde = false; //params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shado = 0; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + float amountcd = 0.f; + float anchorcd = 50.f; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufgb.get(), bufmaskoriglc.get(), originalmasklc.get(), original, reserved, inv, lp, + 0.f, false, + locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklclocalcurve, localmasklcutili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmasklcmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufgb.get(), transformed, bufmaskoriglc.get(), 0); + + return; + } + + if (lp.showmasklcmet == 0 || lp.showmasklcmet == 1 || lp.showmasklcmet == 2 || lp.showmasklcmet == 4 || lp.enalcMask) { + + if (lp.locmet == 0) { + LocalContrastParams localContrastParams; + LocallabParams locallabparams; + localContrastParams.enabled = true; + localContrastParams.radius = params->locallab.spots.at(sp).lcradius; + localContrastParams.amount = params->locallab.spots.at(sp).lcamount; + localContrastParams.darkness = params->locallab.spots.at(sp).lcdarkness; + localContrastParams.lightness = params->locallab.spots.at(sp).lightness; + bool fftwlc = false; + + if (!lp.ftwlc) { // || (lp.ftwlc && call != 2)) { + ImProcFunctions::localContrast(tmp1.get(), tmp1->L, localContrastParams, fftwlc, sk); + } else { + const std::unique_ptr tmpfftw(new LabImage(bfwr, bfhr)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + tmpfftw->L[y][x] = tmp1->L[y][x]; + tmpfftw->a[y][x] = tmp1->a[y][x]; + tmpfftw->b[y][x] = tmp1->b[y][x]; + } + } + + fftwlc = true; + ImProcFunctions::localContrast(tmpfftw.get(), tmpfftw->L, localContrastParams, fftwlc, sk); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + tmp1->L[y][x] = tmpfftw->L[y][x]; + tmp1->a[y][x] = tmpfftw->a[y][x]; + tmp1->b[y][x] = tmpfftw->b[y][x]; + } + } + + } + } else if (lp.locmet == 1) { //wavelet && sk ==1 + int wavelet_level = 1 + params->locallab.spots.at(sp).csthreshold.getBottomRight();//retrieve with +1 maximum wavelet_level + float mL = params->locallab.spots.at(sp).clarilres / 100.f; + float mC = params->locallab.spots.at(sp).claricres / 100.f; + float softr = params->locallab.spots.at(sp).clarisoft; + float mL0 = 0.f; + float mC0 = 0.f; +#ifdef _OPENMP + const int numThreads = omp_get_max_threads(); +#else + const int numThreads = 1; + +#endif + // adap maximum level wavelet to size of RT-spot + int minwin = rtengine::min(bfw, bfh); + int maxlevelspot = 10;//maximum possible + + // adap maximum level wavelet to size of crop + while ((1 << maxlevelspot) >= (minwin * sk) && maxlevelspot > 1) { + --maxlevelspot ; + } + + // printf("minwin=%i maxlevelavant=%i maxlespot=%i\n", minwin, wavelet_level, maxlevelspot); + + wavelet_level = rtengine::min(wavelet_level, maxlevelspot); + // printf("maxlevel=%i\n", wavelet_level); + bool exec = false; + bool origlc = params->locallab.spots.at(sp).origlc; + + if (origlc) {//merge only with original + clarimerge(lp, mL, mC, exec, tmpresid.get(), wavelet_level, sk, numThreads); + } + + int maxlvl = wavelet_level; + const float contrast = params->locallab.spots.at(sp).residcont; + int level_bl = params->locallab.spots.at(sp).csthreshold.getBottomLeft(); + int level_hl = params->locallab.spots.at(sp).csthreshold.getTopLeft(); + int level_br = params->locallab.spots.at(sp).csthreshold.getBottomRight(); + int level_hr = params->locallab.spots.at(sp).csthreshold.getTopRight(); + const float radblur = (params->locallab.spots.at(sp).residblur) / sk; + const bool blurlc = params->locallab.spots.at(sp).blurlc; + const float radlevblur = (params->locallab.spots.at(sp).levelblur) / sk; + const float sigma = params->locallab.spots.at(sp).sigma; + const float offs = params->locallab.spots.at(sp).offset; + const float sigmadc = params->locallab.spots.at(sp).sigmadc; + const float deltad = params->locallab.spots.at(sp).deltad; + // const float fatres = params->locallab.spots.at(sp).fatres; + const float chrol = params->locallab.spots.at(sp).chromalev; + const float chrobl = params->locallab.spots.at(sp).chromablu; + const bool blurena = params->locallab.spots.at(sp).wavblur; + const bool levelena = params->locallab.spots.at(sp).wavcont; + const bool comprena = params->locallab.spots.at(sp).wavcomp; + const bool compreena = params->locallab.spots.at(sp).wavcompre; + const float compress = params->locallab.spots.at(sp).residcomp; + const float thres = params->locallab.spots.at(sp).threswav; + + wavcontrast4(lp, tmp1->L, tmp1->a, tmp1->b, contrast, radblur, radlevblur, tmp1->W, tmp1->H, level_bl, level_hl, level_br, level_hr, sk, numThreads, locwavCurve, locwavutili, wavcurve, loclevwavCurve, loclevwavutili, wavcurvelev, locconwavCurve, locconwavutili, wavcurvecon, loccompwavCurve, loccompwavutili, wavcurvecomp, loccomprewavCurve, loccomprewavutili, wavcurvecompre, locedgwavCurve, locedgwavutili, sigma, offs, maxlvl, sigmadc, deltad, chrol, chrobl, blurlc, blurena, levelena, comprena, compreena, compress, thres); + + const float satur = params->locallab.spots.at(sp).residchro; + + + if (satur != 0.f || radblur > 0.f) {//blur residual a and satur + + wavelet_decomposition *wdspota = new wavelet_decomposition(tmp1->a[0], tmp1->W, tmp1->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspota->memory_allocation_failed()) { + return; + } + + float *wav_ab0a = wdspota->get_coeff0(); + // int maxlvla = wdspota->maxlevel(); + int W_La = wdspota->level_W(0); + int H_La = wdspota->level_H(0); + + if (radblur > 0.f && !blurlc && blurena) { + array2D bufa(W_La, H_La); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_La; y++) { + for (int x = 0; x < W_La; x++) { + bufa[y][x] = wav_ab0a [y * W_La + x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufa, bufa, W_La, H_La, radblur); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_La; y++) { + for (int x = 0; x < W_La; x++) { + wav_ab0a[y * W_La + x] = bufa[y][x]; + } + } + + } + + if (satur != 0.f) { +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_La * H_La; i++) { + wav_ab0a[i] *= (1.f + sin(rtengine::RT_PI * (satur / 200.f)));//more progressive than linear + wav_ab0a[i] = clipC(wav_ab0a[i]); + } + } + + wdspota->reconstruct(tmp1->a[0], 1.f); + delete wdspota; + + wavelet_decomposition *wdspotb = new wavelet_decomposition(tmp1->b[0], tmp1->W, tmp1->H, wavelet_level, 1, sk, numThreads, lp.daubLen); + + if (wdspotb->memory_allocation_failed()) { + return; + } + + float *wav_ab0b = wdspotb->get_coeff0(); + int W_Lb = wdspotb->level_W(0); + int H_Lb = wdspotb->level_H(0); + + if (radblur > 0.f && !blurlc && blurena) { + array2D bufb(W_Lb, H_Lb); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_Lb; y++) { + for (int x = 0; x < W_Lb; x++) { + bufb[y][x] = wav_ab0b [y * W_Lb + x]; + } + } + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(bufb, bufb, W_Lb, H_Lb, radblur); + } + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < H_Lb; y++) { + for (int x = 0; x < W_Lb; x++) { + wav_ab0b[y * W_Lb + x] = bufb[y][x]; + } + } + + } + + if (satur != 0.f) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int i = 0; i < W_Lb * H_Lb; i++) { + wav_ab0b[i] *= (1.f + sin(rtengine::RT_PI * (satur / 200.f))); + wav_ab0b[i] = clipC(wav_ab0b[i]); + } + } + + wdspotb->reconstruct(tmp1->b[0], 1.f); + delete wdspotb; + } + + if (!origlc) {//merge all files + exec = false; + //copy previous calculation in merge possibilities +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + tmpresid->L[y][x] = tmp1->L[y][x]; + tmpresid->a[y][x] = tmp1->a[y][x]; + tmpresid->b[y][x] = tmp1->b[y][x]; + } + } + clarimerge(lp, mL, mC, exec, tmpresid.get(), wavelet_level, sk, numThreads); + } + + float thr = 0.001f; + int flag = 0; + + if (maxlvl <= 4) { + mL0 = 0.f; + mC0 = 0.f; + mL = -1.5f * mL;//increase only for sharpen + mC = -mC; + thr = 1.f; + flag = 0; + + } else { + mL0 = mL; + mC0 = mC; + thr = 1.f; + flag = 1; + } + + if (exec || compreena || comprena || levelena || blurena || lp.wavgradl || locwavCurve || lp.edgwena) { + LabImage *mergfile = tmp1.get(); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int x = 0; x < bfh; x++) + for (int y = 0; y < bfw; y++) { + tmp1->L[x][y] = clipLoc((1.f + mL0) * mergfile->L[x][y] - mL * tmpresid->L[x][y]); + tmp1->a[x][y] = clipC((1.f + mC0) * mergfile->a[x][y] - mC * tmpresid->a[x][y]); + tmp1->b[x][y] = clipC((1.f + mC0) * mergfile->b[x][y] - mC * tmpresid->b[x][y]); + } + + if (softr != 0.f && (compreena || locwavCurve || comprena || blurena || levelena || lp.wavgradl || lp.edgwena || std::fabs(mL) > 0.001f)) { + softproc(tmpres.get(), tmp1.get(), softr, bfh, bfw, 0.0001, 0.00001, thr, sk, multiThread, flag); + } + } + } + + + transit_shapedetect2(call, 10, bufgb.get(), tmp1.get(), originalmasklc.get(), hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + tmp1.reset(); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + if (!lp.invshar && lp.shrad > 0.42 && call < 3 && lp.sharpena && sk == 1) { //interior ellipse for sharpening, call = 1 and 2 only with Dcrop and simpleprocess + int bfh = call == 2 ? int (lp.ly + lp.lyT) + del : original->H; //bfw bfh real size of square zone + int bfw = call == 2 ? int (lp.lx + lp.lxL) + del : original->W; + JaggedArray loctemp(bfw, bfh); + + if (call == 2) { //call from simpleprocess + // printf("bfw=%i bfh=%i\n", bfw, bfh); + + if (bfw < mSPsharp || bfh < mSPsharp) { + printf("too small RT-spot - minimum size 39 * 39\n"); + return; + } + + JaggedArray bufsh(bfw, bfh, true); + JaggedArray hbuffer(bfw, bfh); + int begy = lp.yc - lp.lyT; + int begx = lp.xc - lp.lxL; + int yEn = lp.yc + lp.ly; + int xEn = lp.xc + lp.lx; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) { + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int loy = cy + y; + + if (lox >= begx && lox < xEn && loy >= begy && loy < yEn) { + bufsh[loy - begy][lox - begx] = original->L[y][x]; + } + } + } + + //sharpen only square area instead of all image + ImProcFunctions::deconvsharpeningloc(bufsh, hbuffer, bfw, bfh, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, 1); + } else { //call from dcrop.cc + ImProcFunctions::deconvsharpeningloc(original->L, shbuffer, bfw, bfh, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, sk); + } + + //sharpen ellipse and transition + Sharp_Local(call, loctemp, 0, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + } else if (lp.invshar && lp.shrad > 0.42 && call < 3 && lp.sharpena && sk == 1) { + int GW = original->W; + int GH = original->H; + JaggedArray loctemp(GW, GH); + + ImProcFunctions::deconvsharpeningloc(original->L, shbuffer, GW, GH, loctemp, params->locallab.spots.at(sp).shardamping, (double)params->locallab.spots.at(sp).sharradius, params->locallab.spots.at(sp).shariter, params->locallab.spots.at(sp).sharamount, params->locallab.spots.at(sp).sharcontrast, (double)params->locallab.spots.at(sp).sharblur, sk); + + InverseSharp_Local(loctemp, hueref, lumaref, chromaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + if (lp.dehaze != 0 && lp.retiena) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + + if (bfh >= mSP && bfw >= mSP) { + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); //buffer for data in zone limit + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); //buffer for data in zone limit + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + bufexporig->a[y - ystart][x - xstart] = original->a[y][x]; + bufexporig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + + bufexpfin->CopyFrom(bufexporig.get(), multiThread); + //calc dehaze + const std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); + + DehazeParams dehazeParams; + dehazeParams.enabled = true; + dehazeParams.strength = lp.dehaze; + dehazeParams.showDepthMap = false; + dehazeParams.depth = lp.depth; + dehazeParams.luminance = params->locallab.spots.at(sp).lumonly; + lab2rgb(*bufexpfin, *tmpImage.get(), params->icm.workingProfile); + dehazeloc(tmpImage.get(), dehazeParams); + rgb2lab(*tmpImage.get(), *bufexpfin, params->icm.workingProfile); + + transit_shapedetect2(call, 30, bufexporig.get(), bufexpfin.get(), nullptr, hueref, chromaref, lumaref, sobelref, 0.f, nullptr, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + lp.invret = false;//always disabled inverse RETI too complex todo !! + + if (lp.str >= 0.2f && lp.retiena && call != 2) { + LabImage *bufreti = nullptr; + LabImage *bufmask = nullptr; + LabImage *buforig = nullptr; + LabImage *buforigmas = nullptr; + + if (GW >= mSP && GH >= mSP) + + { + + array2D buflight(GW, GH); + JaggedArray bufchro(GW, GH); + + int Hd, Wd; + Hd = GH; + Wd = GW; + + bufreti = new LabImage(GW, GH); + bufmask = new LabImage(GW, GH); + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig = new LabImage(GW, GH); + buforigmas = new LabImage(GW, GH); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < GH; ir++) //fill with 0 + for (int jr = 0; jr < GW; jr++) { + bufreti->L[ir][jr] = 0.f; + bufreti->a[ir][jr] = 0.f; + bufreti->b[ir][jr] = 0.f; + buflight[ir][jr] = 0.f; + bufchro[ir][jr] = 0.f; + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) //{ + for (int x = 0; x < transformed->W; x++) { + bufreti->L[y][x] = original->L[y][x]; + bufreti->a[y][x] = original->a[y][x]; + bufreti->b[y][x] = original->b[y][x]; + bufmask->L[y][x] = original->L[y][x]; + bufmask->a[y][x] = original->a[y][x]; + bufmask->b[y][x] = original->b[y][x]; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig->L[y][x] = original->L[y][x]; + buforig->a[y][x] = original->a[y][x]; + buforig->b[y][x] = original->b[y][x]; + } + + } + + float raddE = params->locallab.spots.at(sp).softradiusret; + + //calc dE and reduction to use in MSR to reduce artifacts + const float mindE = 4.f + MINSCOPE * lp.sensh * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + + const std::unique_ptr> reducDEBuffer(new JaggedArray(Wd, Hd)); + float** reducDE = *(reducDEBuffer.get()); + + float ade = 0.01f * raddE; + float bde = 100.f - raddE; + float sensibefore = ade * lp.sensh + bde;//we can change sensitivity 0.1 90 or 0.3 70 or 0.4 60 +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < transformed->H ; y++) + for (int x = 0; x < transformed->W; x++) { + float dE = std::sqrt(SQR(refa - bufreti->a[y][x] / 327.68f) + SQR(refb - bufreti->b[y][x] / 327.68f) + SQR(lumaref - bufreti->b[y][x] / 327.68f)); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sensibefore); + reducDE[y][x] = clipDE(reducdE); + } + + const std::unique_ptr> origBuffer(new JaggedArray(Wd, Hd)); + float** orig = *(origBuffer.get()); + + const std::unique_ptr> origBuffer1(new JaggedArray(Wd, Hd)); + float** orig1 = *(origBuffer1.get()); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + orig[ir][jr] = bufreti->L[ir][jr]; + orig1[ir][jr] = bufreti->L[ir][jr]; + } + + LabImage *tmpl = new LabImage(Wd, Hd); + + // float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; + bool fftw = lp.ftwreti; + //fftw = false; + //for Retinex Mask are incorporated in MSR + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + float lumask = params->locallab.spots.at(sp).lumask; + + const float mindE2 = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE2 = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim2 = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim2 = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + ImProcFunctions::MSRLocal(call, sp, fftw, 1, reducDE, bufreti, bufmask, buforig, buforigmas, orig, orig1, + Wd, Hd, Wd, Hd, params->locallab, sk, locRETgainCcurve, locRETtransCcurve, 0, 4, 1.f, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, + lmaskretilocalcurve, localmaskretiutili, + transformed, lp.enaretiMasktmap, lp.enaretiMask, + delt, hueref, chromaref, lumaref, + maxdE2, mindE2, maxdElim2, mindElim2, lp.iterat, limscope, sco, lp.balance, lp.balanceh, lumask); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = orig[ir][jr]; + } + } + + if (lp.equret) { //equilibrate luminance before / after MSR + float *datain = new float[Hd * Wd]; + float *data = new float[Hd * Wd]; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + datain[ir * Wd + jr] = orig1[ir][jr]; + data[ir * Wd + jr] = orig[ir][jr]; + } + + normalize_mean_dt(data, datain, Hd * Wd, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = data[ir * Wd + jr]; + } + + delete [] datain; + delete [] data; + } + + + float minL = tmpl->L[0][0] - bufreti->L[0][0]; + float maxL = minL; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minL) reduction(max:maxL) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] = tmpl->L[ir][jr] - bufreti->L[ir][jr]; + minL = rtengine::min(minL, buflight[ir][jr]); + maxL = rtengine::max(maxL, buflight[ir][jr]); + } + } + + const float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); + + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] /= coef; + } + } + + transit_shapedetect_retinex(call, 4, bufreti, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + if (params->locallab.spots.at(sp).chrrt > 0) { + + if (call == 1) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + + orig[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + orig1[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + } + + } + + float maxChro = orig1[0][0]; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxChro) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + maxChro = rtengine::max(maxChro, orig1[ir][jr]); + } + } + + float divchro = maxChro; + + //first step change saturation without Retinex ==> gain of time and memory + float satreal = lp.str * params->locallab.spots.at(sp).chrrt / 100.f; + + if (params->locallab.spots.at(sp).chrrt <= 0.2f) { + satreal /= 10.f; + } + + DiagonalCurve reti_satur({ + DCT_NURBS, + 0, 0, + 0.2, 0.2 + satreal / 250.0, + 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), + 1, 1 + }); + + if (call == 1) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + const float Chprov = orig1[ir][jr]; + float2 sincosval; + sincosval.y = Chprov == 0.0f ? 1.f : bufreti->a[ir][jr] / Chprov; + sincosval.x = Chprov == 0.0f ? 0.f : bufreti->b[ir][jr] / Chprov; + + if (params->locallab.spots.at(sp).chrrt <= 100.f) { //first step + float buf = LIM01(orig[ir][jr] / divchro); + buf = reti_satur.getVal(buf); + buf *= divchro; + orig[ir][jr] = buf; + } + + tmpl->a[ir][jr] = orig[ir][jr] * sincosval.y; + tmpl->b[ir][jr] = orig[ir][jr] * sincosval.x; + } + + float minC = std::sqrt(SQR(tmpl->a[0][0]) + SQR(tmpl->b[0][0])) - orig1[0][0]; + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minC) reduction(max:maxC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] = std::sqrt(SQR(tmpl->a[ir][jr]) + SQR(tmpl->b[ir][jr])) - orig1[ir][jr]; + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] *= coefC; + } + } + } + } + + transit_shapedetect_retinex(call, 5, tmpl, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + delete tmpl; + delete bufmask; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + if (buforig) { + delete buforig; + } + + if (buforigmas) { + delete buforigmas; + } + } + delete bufreti; + } + } + + + + if (lp.str >= 0.2f && lp.retiena && call == 2) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + + LabImage *bufreti = nullptr; + LabImage *bufmask = nullptr; + LabImage *buforig = nullptr; + LabImage *buforigmas = nullptr; + int bfhr = bfh; + int bfwr = bfw; + + if (bfw >= mSP && bfh > mSP) { + if (lp.ftwreti) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + array2D buflight(bfw, bfh); + JaggedArray bufchro(bfw, bfh); + + int Hd, Wd; + Hd = GH; + Wd = GW; + + if (!lp.invret && call == 2) { + + Hd = bfh; + Wd = bfw; + bufreti = new LabImage(bfw, bfh); + bufmask = new LabImage(bfw, bfh); + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig = new LabImage(bfw, bfh); + buforigmas = new LabImage(bfw, bfh); + } + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) //fill with 0 + for (int jr = 0; jr < bfw; jr++) { + bufreti->L[ir][jr] = 0.f; + bufreti->a[ir][jr] = 0.f; + bufreti->b[ir][jr] = 0.f; + buflight[ir][jr] = 0.f; + bufchro[ir][jr] = 0.f; + } + + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufreti->L[y - ystart][x - xstart] = original->L[y][x]; + bufreti->a[y - ystart][x - xstart] = original->a[y][x]; + bufreti->b[y - ystart][x - xstart] = original->b[y][x]; + bufmask->L[y - ystart][x - xstart] = original->L[y][x]; + bufmask->a[y - ystart][x - xstart] = original->a[y][x]; + bufmask->b[y - ystart][x - xstart] = original->b[y][x]; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + buforig->L[y - ystart][x - xstart] = original->L[y][x]; + buforig->a[y - ystart][x - xstart] = original->a[y][x]; + buforig->b[y - ystart][x - xstart] = original->b[y][x]; + } + } + } + } + + float raddE = params->locallab.spots.at(sp).softradiusret; + + //calc dE and reduction to use in MSR to reduce artifacts + const float mindE = 4.f + MINSCOPE * lp.sensh * lp.thr; + const float maxdE = 5.f + MAXSCOPE * lp.sensh * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float refa = chromaref * cos(hueref); + const float refb = chromaref * sin(hueref); + + const std::unique_ptr> reducDEBuffer(new JaggedArray(Wd, Hd)); + float** reducDE = *(reducDEBuffer.get()); + float ade = 0.01f * raddE; + float bde = 100.f - raddE; + float sensibefore = ade * lp.sensh + bde;//we can change sensitivity 0.1 90 or 0.3 70 or 0.4 60 +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + const float dE = std::sqrt(SQR(refa - bufreti->a[y - ystart][x - xstart] / 327.68f) + SQR(refb - bufreti->b[y - ystart][x - xstart] / 327.68f) + SQR(lumaref - bufreti->b[y - ystart][x - xstart] / 327.68f)); + const float reducdE = calcreducdE(dE, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sensibefore); + reducDE[y - ystart][x - xstart] = clipDE(reducdE); + } + } + + const std::unique_ptr> origBuffer(new JaggedArray(Wd, Hd)); + float** orig = *(origBuffer.get()); + + const std::unique_ptr> origBuffer1(new JaggedArray(Wd, Hd)); + float** orig1 = *(origBuffer1.get()); + + LabImage *tmpl = nullptr; + + if (!lp.invret && call == 2) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + orig[ir][jr] = bufreti->L[ir][jr]; + orig1[ir][jr] = bufreti->L[ir][jr]; + } + } + + tmpl = new LabImage(Wd, Hd); + } + + // float minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax; + bool fftw = lp.ftwreti; + //for Retinex Mask are incorporated in MSR + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + float lumask = params->locallab.spots.at(sp).lumask; + + const float mindE2 = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE2 = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim2 = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim2 = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + + ImProcFunctions::MSRLocal(call, sp, fftw, 1, reducDE, bufreti, bufmask, buforig, buforigmas, orig, orig1, + Wd, Hd, bfwr, bfhr, params->locallab, sk, locRETgainCcurve, locRETtransCcurve, 0, 4, 1.f, minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, + lmaskretilocalcurve, localmaskretiutili, + transformed, lp.enaretiMasktmap, lp.enaretiMask, + delt, hueref, chromaref, lumaref, + maxdE2, mindE2, maxdElim2, mindElim2, lp.iterat, limscope, sco, lp.balance, lp.balanceh, lumask); + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = orig[ir][jr]; + } + + + if (lp.equret) { //equilibrate luminance before / after MSR + const std::unique_ptr datain(new float[Hd * Wd]); + const std::unique_ptr data(new float[Hd * Wd]); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + datain[ir * Wd + jr] = orig1[ir][jr]; + data[ir * Wd + jr] = orig[ir][jr]; + } + } + + normalize_mean_dt(data.get(), datain.get(), Hd * Wd, 1.f, 1.f); +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + tmpl->L[ir][jr] = data[ir * Wd + jr]; + } + } + } + + if (!lp.invret) { + float minL = tmpl->L[0][0] - bufreti->L[0][0]; + float maxL = minL; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minL) reduction(max:maxL) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] = tmpl->L[ir][jr] - bufreti->L[ir][jr]; + minL = rtengine::min(minL, buflight[ir][jr]); + maxL = rtengine::max(maxL, buflight[ir][jr]); + } + } + + float coef = 0.01f * rtengine::max(std::fabs(minL), std::fabs(maxL)); + + if (coef > 0.f) { + coef = 1.f / coef; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + buflight[ir][jr] *= coef; + } + } + } + + transit_shapedetect_retinex(call, 4, bufreti, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + + if (params->locallab.spots.at(sp).chrrt > 0) { + if (!lp.invret && call == 2) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + orig[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + orig1[ir][jr] = std::sqrt(SQR(bufreti->a[ir][jr]) + SQR(bufreti->b[ir][jr])); + } + } + } + + float maxChro = orig1[0][0]; +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxChro) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + maxChro = rtengine::max(maxChro, orig1[ir][jr]); + } + } + + //first step change saturation without Retinex ==> gain of time and memory + float satreal = lp.str * params->locallab.spots.at(sp).chrrt / 100.f; + + if (params->locallab.spots.at(sp).chrrt <= 0.2f) { + satreal /= 10.f; + } + + DiagonalCurve reti_satur({ + DCT_NURBS, + 0, 0, + 0.2, 0.2 + satreal / 250.0, + 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), + 1, 1 + }); + + if (!lp.invret && call == 2) { + +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir += 1) { + for (int jr = 0; jr < Wd; jr += 1) { + const float Chprov = orig1[ir][jr]; + float2 sincosval; + sincosval.y = Chprov == 0.0f ? 1.f : bufreti->a[ir][jr] / Chprov; + sincosval.x = Chprov == 0.0f ? 0.f : bufreti->b[ir][jr] / Chprov; + + if (params->locallab.spots.at(sp).chrrt <= 40.f) { //first step + orig[ir][jr] = reti_satur.getVal(LIM01(orig[ir][jr] / maxChro)) * maxChro; + } + + tmpl->a[ir][jr] = orig[ir][jr] * sincosval.y; + tmpl->b[ir][jr] = orig[ir][jr] * sincosval.x; + } + } + + float minC = std::sqrt(SQR(tmpl->a[0][0]) + SQR(tmpl->b[0][0])) - orig1[0][0]; + float maxC = minC; +#ifdef _OPENMP + #pragma omp parallel for reduction(min:minC) reduction(max:maxC) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] = std::sqrt(SQR(tmpl->a[ir][jr]) + SQR(tmpl->b[ir][jr])) - orig1[ir][jr]; + minC = rtengine::min(minC, bufchro[ir][jr]); + maxC = rtengine::max(maxC, bufchro[ir][jr]); + } + } + + float coefC = 0.01f * rtengine::max(std::fabs(minC), std::fabs(maxC)); + + if (coefC > 0.f) { + coefC = 1.f / coefC; +#ifdef _OPENMP + #pragma omp parallel for if (multiThread) +#endif + for (int ir = 0; ir < Hd; ir++) { + for (int jr = 0; jr < Wd; jr++) { + bufchro[ir][jr] *= coefC; + } + } + } + } + + if (!lp.invret) { + transit_shapedetect_retinex(call, 5, tmpl, bufmask, buforigmas, buflight, bufchro, hueref, chromaref, lumaref, lp, original, transformed, cx, cy, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + + delete tmpl; + delete bufmask; + + if (!lp.enaretiMasktmap && lp.enaretiMask) { + if (buforig) { + delete buforig; + } + + if (buforigmas) { + delete buforigmas; + } + } + delete bufreti; + } + } + + bool enablefat = false; + + if (params->locallab.spots.at(sp).fatamount > 1.f) { + enablefat = true;; + } + + bool execex = (lp.exposena && (lp.expcomp != 0.f || lp.blac != 0 || lp.laplacexp > 0.1f || lp.strexp != 0.f || enablefat || lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 4 || lp.showmaskexpmet == 5 || lp.prevdE || (exlocalcurve && localexutili))); + + if (!lp.invex && execex) { + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + //variable for fast FFTW + int bfhr = bfh; + int bfwr = bfw; + + + if (bfw >= mSP && bfh >= mSP) { + + if (lp.expmet == 1) { + optfft(N_fftwsize, bfh, bfw, bfhr, bfwr, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + const std::unique_ptr bufexporig(new LabImage(bfw, bfh)); + const std::unique_ptr bufexpfin(new LabImage(bfw, bfh)); + + std::unique_ptr bufmaskblurexp; + std::unique_ptr originalmaskexp; + + array2D blend2; + + if (call <= 3) { //simpleprocess, dcrop, improccoordinator + if (lp.showmaskexpmet == 2 || lp.enaExpMask || lp.showmaskexpmet == 3 || lp.showmaskexpmet == 5) { + bufmaskblurexp.reset(new LabImage(bfw, bfh)); + originalmaskexp.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend; y++) { + for (int x = xstart; x < xend; x++) { + bufexporig->L[y - ystart][x - xstart] = original->L[y][x]; + } + } + + const int spotSi = rtengine::max(1 + 2 * rtengine::max(1, lp.cir / sk), 5); + + if (bfw > 2 * spotSi && bfh > 2 * spotSi && lp.struexp > 0.f) { + blend2(bfw, bfh); + ImProcFunctions::blendstruc(bfw, bfh, bufexporig.get(), 3.f / (sk * 1.4f), 0.5f * lp.struexp, blend2, sk, multiThread); + + if (lp.showmaskexpmet == 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + const int loy = cy + y; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (zone > 0) { + transformed->L[y][x] = CLIP(blend2[y - ystart][x - xstart]); + transformed->a[y][x] = 0.f; + transformed->b[y][x] = 0.f; + } + } + } + + return; + } + } + + int inv = 0; + bool showmaske = false; + const bool enaMask = lp.enaExpMask; + bool deltaE = false; + bool modmask = false; + bool zero = false; + bool modif = false; + + if (lp.showmaskexpmet == 3) { + showmaske = true; + } else if (lp.showmaskexpmet == 5) { + deltaE = true; + } else if (lp.showmaskexpmet == 2) { + modmask = true; + } else if (lp.showmaskexpmet == 1) { + modif = true; + } else if (lp.showmaskexpmet == 0) { + zero = true; + } + + float chrom = lp.chromaexp; + float rad = lp.radmaexp; + float gamma = lp.gammaexp; + float slope = lp.slomaexp; + float blendm = lp.blendmaexp; + float lap = params->locallab.spots.at(sp).lapmaskexp; + bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + bool lmasutilicolwav = false; + bool delt = params->locallab.spots.at(sp).deltae; + int sco = params->locallab.spots.at(sp).scopemask; + int shado = 0; + int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + float amountcd = 0.f; + float anchorcd = 50.f; + int lumask = params->locallab.spots.at(sp).lumask; + LocHHmaskCurve lochhhmasCurve; + bool lhhmasutili = false; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, + 0.f, false, + locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskexpmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufexporig.get(), transformed, bufmaskblurexp.get(), 0); + + return; + } + + if (lp.showmaskexpmet == 4) { + return; + } + + if (lp.showmaskexpmet == 0 || lp.showmaskexpmet == 1 || lp.showmaskexpmet == 2 || lp.showmaskexpmet == 5 || lp.enaExpMask) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + for (int x = 0; x < bfw; x++) { + bufexpfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufexpfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufexpfin->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + + + if (exlocalcurve && localexutili) {// L=f(L) curve enhanced +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + bufexpfin->L[ir][jr] = 0.5f * exlocalcurve[2.f * bufexporig->L[ir][jr]]; + } + + if (lp.expcomp == 0.f) { + lp.expcomp = 0.011f; // to enabled + } + + ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexpfin.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + + + } else { + + ImProcFunctions::exlabLocal(lp, bfh, bfw, bufexporig.get(), bufexpfin.get(), hltonecurveloc, shtonecurveloc, tonecurveloc); + } + +//gradient + struct grad_params gp; + + if (lp.strexp != 0.f) { + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 1); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + bufexpfin->L[ir][jr] *= ImProcFunctions::calcGradientFactor(gp, jr, ir); + } + } + } + +//exposure_pde + if (lp.expmet == 1) { + if (enablefat) { + const std::unique_ptr datain(new float[bfwr * bfhr]); + const std::unique_ptr dataout(new float[bfwr * bfhr]); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + datain[y * bfwr + x] = bufexpfin->L[y][x]; + } + } + + FattalToneMappingParams fatParams; + fatParams.enabled = true; + fatParams.threshold = params->locallab.spots.at(sp).fatdetail; + fatParams.amount = params->locallab.spots.at(sp).fatamount; + fatParams.anchor = 50.f; //params->locallab.spots.at(sp).fatanchor; + const float sigm = params->locallab.spots.at(sp).fatlevel; + const float mean = params->locallab.spots.at(sp).fatanchor; + const std::unique_ptr tmpImagefat(new Imagefloat(bfwr, bfhr)); + lab2rgb(*bufexpfin, *(tmpImagefat.get()), params->icm.workingProfile); + ToneMapFattal02(tmpImagefat.get(), fatParams, 3, 0, nullptr, 0, 0, 1);//last parameter = 1 ==>ART algorithm + rgb2lab(*(tmpImagefat.get()), *bufexpfin, params->icm.workingProfile); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + dataout[y * bfwr + x] = bufexpfin->L[y][x]; + } + } + + normalize_mean_dt(dataout.get(), datain.get(), bfwr * bfhr, mean, sigm); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + bufexpfin->L[y][x] = dataout[y * bfwr + x]; + } + } + } + + if (lp.laplacexp > 0.1f) { + MyMutex::MyLock lock(*fftwMutex); + std::unique_ptr datain(new float[bfwr * bfhr]); + std::unique_ptr dataout(new float[bfwr * bfhr]); + const float gam = params->locallab.spots.at(sp).gamm; + const float igam = 1.f / gam; + + if (params->locallab.spots.at(sp).exnoiseMethod == "med" || params->locallab.spots.at(sp).exnoiseMethod == "medhi") { + if (lp.blac < -100.f && lp.linear > 0.01f) { + float evnoise = lp.blac - lp.linear * 2000.f; + if (params->locallab.spots.at(sp).exnoiseMethod == "med") { + evnoise *= 0.4f; + } + + //soft denoise, user must use Local Denoise to best result + Median med; + if (evnoise < -18000.f) { + med = Median::TYPE_5X5_STRONG; + } else if (evnoise < -15000.f) { + med = Median::TYPE_5X5_SOFT; + } else if (evnoise < -10000.f) { + med = Median::TYPE_3X3_STRONG; + } else { + med = Median:: TYPE_3X3_SOFT; + } + + Median_Denoise(bufexpfin->L, bufexpfin->L, bfwr, bfhr, med, 1, multiThread); + Median_Denoise(bufexpfin->a, bufexpfin->a, bfwr, bfhr, Median::TYPE_3X3_SOFT, 1, multiThread); + Median_Denoise(bufexpfin->b, bufexpfin->b, bfwr, bfhr, Median::TYPE_3X3_SOFT, 1, multiThread); + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + float L = LIM01(bufexpfin->L[y][x] / 32768.f);//change gamma for Laplacian + datain[y * bfwr + x] = pow_F(L, gam) * 32768.f; + } + } + + //call PDE equation - with Laplacian threshold + ImProcFunctions::exposure_pde(datain.get(), datain.get(), dataout.get(), bfwr, bfhr, 12.f * lp.laplacexp, lp.balanexp); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfhr; y++) { + for (int x = 0; x < bfwr; x++) { + const float Y = dataout[y * bfwr + x] / 32768.f;//inverse Laplacian gamma + bufexpfin->L[y][x] = pow_F(Y, igam) * 32768.f; + } + } + } + } + + //shadows with ipshadowshighlight + if ((lp.expcomp != 0.f && lp.expcomp != 0.01f) || (exlocalcurve && localexutili)) { + if (lp.shadex > 0) { + ImProcFunctions::shadowsHighlights(bufexpfin.get(), true, 1, 0, lp.shadex, 40, sk, 0, lp.shcomp); + } + } + + if (lp.expchroma != 0.f) { + if ((lp.expcomp != 0.f && lp.expcomp != 0.01f) || (exlocalcurve && localexutili) || lp.laplacexp > 0.1f) { + constexpr float ampli = 70.f; + const float ch = (1.f + 0.02f * lp.expchroma); + const float chprosl = ch <= 1.f ? 99.f * ch - 99.f : clipChro(ampli * ch - ampli); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float epsi = bufexporig->L[ir][jr] == 0.f ? 0.001f : 0.f; + const float rapexp = bufexpfin->L[ir][jr] / (bufexporig->L[ir][jr] + epsi); + bufexpfin->a[ir][jr] *= 1.f + chprosl * rapexp; + bufexpfin->b[ir][jr] *= 1.f + chprosl * rapexp; + } + } + } + } + + if (lp.softradiusexp > 0.f && lp.expmet == 0) { + softproc(bufexporig.get(), bufexpfin.get(), lp.softradiusexp, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + } + float meansob = 0.f; + transit_shapedetect2(call, 1, bufexporig.get(), bufexpfin.get(), originalmaskexp.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + } +//inverse + + else if (lp.invex && (lp.expcomp != 0.0 || lp.laplacexp > 0.1f || params->locallab.spots.at(sp).fatamount > 1.f || (exlocalcurve && localexutili) || lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) && lp.exposena) { + constexpr float adjustr = 2.f; + std::unique_ptr bufmaskblurexp; + std::unique_ptr originalmaskexp; + const std::unique_ptr bufexporig(new LabImage(GW, GH)); + + if (lp.enaExpMaskinv || lp.showmaskexpmetinv == 1) { + bufmaskblurexp.reset(new LabImage(GW, GH, true)); + originalmaskexp.reset(new LabImage(GW, GH)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + bufexporig->L[y][x] = original->L[y][x]; + } + } + + constexpr int inv = 1; + const bool showmaske = lp.showmaskexpmetinv == 1; + const bool enaMask = lp.enaExpMaskinv; + constexpr bool deltaE = false; + constexpr bool modmask = false; + const bool zero = lp.showmaskexpmetinv == 0; + constexpr bool modif = false; + const float chrom = lp.chromaexp; + const float rad = lp.radmaexp; + const float gamma = lp.gammaexp; + const float slope = lp.slomaexp; + const float blendm = lp.blendmaexp; + const float lap = params->locallab.spots.at(sp).lapmaskexp; + const bool pde = params->locallab.spots.at(sp).laplac; + LocwavCurve dummy; + const bool lmasutilicolwav = false; + // bool delt = params->locallab.spots.at(sp).deltae; + const bool delt = false; + const int sco = params->locallab.spots.at(sp).scopemask; + constexpr int shado = 0; + constexpr int shortcu = 0;//lp.mergemet; //params->locallab.spots.at(sp).shortc; + const int lumask = params->locallab.spots.at(sp).lumask; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + constexpr float amountcd = 0.f; + constexpr float anchorcd = 50.f; + LocHHmaskCurve lochhhmasCurve; + constexpr bool lhhmasutili = false; + + maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufexporig.get(), bufmaskblurexp.get(), originalmaskexp.get(), original, reserved, inv, lp, + 0.f, false, + locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmaskexplocalcurve, localmaskexputili, dummy, lmasutilicolwav, 1, 1, 5, 5, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskexpmetinv == 1) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufexporig.get(), transformed, bufmaskblurexp.get(), inv); + return; + } + + InverseColorLight_Local(false, false, sp, 1, lp, originalmaskexp.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + +//local color and light + const float factor = LocallabParams::LABGRIDL_CORR_MAX * 3.276f; + const float scaling = LocallabParams::LABGRIDL_CORR_SCALE; + const float scaledirect = LocallabParams::LABGRIDL_DIRECT_SCALE; + const float a_scale = (lp.highA - lp.lowA) / factor / scaling; + const float a_base = lp.lowA / scaling; + const float b_scale = (lp.highB - lp.lowB) / factor / scaling; + const float b_base = lp.lowB / scaling; + const bool ctoning = (a_scale != 0.f || b_scale != 0.f || a_base != 0.f || b_base != 0.f); + const float a_scalemerg = (lp.highAmerg - lp.lowAmerg) / factor / scaling; + const float a_basemerg = lp.lowAmerg / scaling; + const float b_scalemerg = (lp.highBmerg - lp.lowBmerg) / factor / scaling; + const float b_basemerg = lp.lowBmerg / scaling; + const bool ctoningmerg = (a_scalemerg != 0.f || b_scalemerg != 0.f || a_basemerg != 0.f || b_basemerg != 0.f); + + if (!lp.inv && (lp.chro != 0 || lp.ligh != 0.f || lp.cont != 0 || ctoning || lp.mergemet > 0 || lp.strcol != 0.f || lp.strcolab != 0.f || lp.qualcurvemet != 0 || lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 3 || lp.showmaskcolmet == 4 || lp.showmaskcolmet == 5 || lp.prevdE) && lp.colorena) { // || lllocalcurve)) { //interior ellipse renforced lightness and chroma //locallutili + int ystart = rtengine::max(static_cast(lp.yc - lp.lyT) - cy, 0); + int yend = rtengine::min(static_cast(lp.yc + lp.ly) - cy, original->H); + int xstart = rtengine::max(static_cast(lp.xc - lp.lxL) - cx, 0); + int xend = rtengine::min(static_cast(lp.xc + lp.lx) - cx, original->W); + int bfh = yend - ystart; + int bfw = xend - xstart; + const bool spez = params->locallab.spots.at(sp).special; + + if (bfw >= mSP && bfh >= mSP) { + + if (lp.blurcolmask >= 0.25f && lp.fftColorMask && call == 2) { + optfft(N_fftwsize, bfh, bfw, bfh, bfw, lp, original->H, original->W, xstart, ystart, xend, yend, cx, cy); + } + + std::unique_ptr bufcolorig; + std::unique_ptr bufcolfin; + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskcol; + std::unique_ptr bufcolreserv; + std::unique_ptr buftemp; + array2D blend2; + + float adjustr = 1.0f; + + //adapt chroma to working profile + if (params->icm.workingProfile == "ProPhoto") { + adjustr = 1.2f; // 1.2 instead 1.0 because it's very rare to have C>170.. + } else if (params->icm.workingProfile == "Adobe RGB") { + adjustr = 1.8f; + } else if (params->icm.workingProfile == "sRGB") { + adjustr = 2.0f; + } else if (params->icm.workingProfile == "WideGamut") { + adjustr = 1.2f; + } else if (params->icm.workingProfile == "Beta RGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BestRGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BruceRGB") { + adjustr = 1.8f; + } + + if (call <= 3) { //simpleprocess, dcrop, improccoordinator + bufcolorig.reset(new LabImage(bfw, bfh)); + bufcolfin.reset(new LabImage(bfw, bfh)); + buftemp.reset(new LabImage(bfw, bfh)); + + if (lp.showmaskcolmet == 2 || lp.enaColorMask || lp.showmaskcolmet == 3 || lp.showmaskcolmet == 5) { + bufmaskblurcol.reset(new LabImage(bfw, bfh, true)); + originalmaskcol.reset(new LabImage(bfw, bfh)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolorig->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolorig->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolorig->b[y][x] = original->b[y + ystart][x + xstart]; + bufcolfin->L[y][x] = original->L[y + ystart][x + xstart]; + bufcolfin->a[y][x] = original->a[y + ystart][x + xstart]; + bufcolfin->b[y][x] = original->b[y + ystart][x + xstart]; + buftemp->L[y][x] = original->L[y + ystart][x + xstart]; + buftemp->a[y][x] = original->a[y + ystart][x + xstart]; + buftemp->b[y][x] = original->b[y + ystart][x + xstart]; + } + } + + const int spotSi = rtengine::max(1 + 2 * rtengine::max(1, lp.cir / sk), 5); + const bool blends = bfw > 2 * spotSi && bfh > 2 * spotSi && lp.struco > 0.f; + + if (blends) { + blend2(bfw, bfh); + ImProcFunctions::blendstruc(bfw, bfh, bufcolorig.get(), 3.f / (sk * 1.4f), 0.5f * lp.struco, blend2, sk, multiThread); + + if (lp.showmaskcolmet == 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = ystart; y < yend ; y++) { + for (int x = xstart; x < xend; x++) { + const int lox = cx + x; + const int loy = cy + y; + int zone; + float localFactor = 1.f; + const float achm = lp.trans / 100.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (zone > 0) { + transformed->L[y][x] = CLIP(blend2[y - ystart][x - xstart]); + transformed->a[y][x] = 0.f; + transformed->b[y][x] = 0.f; + } + } + } + return; + } + } + + const int inv = 0; + const bool showmaske = lp.showmaskcolmet == 3; + const bool enaMask = lp.enaColorMask; + const bool deltaE = lp.showmaskcolmet == 5; + const bool modmask = lp.showmaskcolmet == 2; + const bool zero = lp.showmaskcolmet == 0; + const bool modif = lp.showmaskcolmet == 1; + const float chrom = lp.chromacol; + const float rad = lp.radmacol; + const float gamma = lp.gammacol; + const float slope = lp.slomacol; + const float blendm = lp.blendmacol; + const float lap = params->locallab.spots.at(sp).lapmaskcol; + const bool pde = params->locallab.spots.at(sp).laplac; + const int shado = params->locallab.spots.at(sp).shadmaskcol; + const int sco = params->locallab.spots.at(sp).scopemask; + const int level_bl = params->locallab.spots.at(sp).csthresholdcol.getBottomLeft(); + const int level_hl = params->locallab.spots.at(sp).csthresholdcol.getTopLeft(); + const int level_br = params->locallab.spots.at(sp).csthresholdcol.getBottomRight(); + const int level_hr = params->locallab.spots.at(sp).csthresholdcol.getTopRight(); + const int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; + const int lumask = params->locallab.spots.at(sp).lumask; + const float strumask = 0.02f * params->locallab.spots.at(sp).strumaskcol; + float conthr = 0.01f * params->locallab.spots.at(sp).conthrcol; + const float mercol = params->locallab.spots.at(sp).mercol; + const float merlucol = params->locallab.spots.at(sp).merlucol; + + int tonemod = 0; + if (params->locallab.spots.at(sp).toneMethod == "one") { + tonemod = 0; + } else if (params->locallab.spots.at(sp).toneMethod == "two") { + tonemod = 1; + } else if (params->locallab.spots.at(sp).toneMethod == "thr") { + tonemod = 2; + } else if (params->locallab.spots.at(sp).toneMethod == "fou") { + tonemod = 3; + } + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + const float amountcd = 0.f; + const float anchorcd = 50.f; + + maskcalccol(false, pde, bfw, bfh, xstart, ystart, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, + strumask, astool, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, + level_bl, level_hl, level_br, level_hr, + shortcu, delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskcolmet == 3) { + showmask(lumask, lp, xstart, ystart, cx, cy, bfw, bfh, bufcolorig.get(), transformed, bufmaskblurcol.get(), 0); + return; + } else if (lp.showmaskcolmet == 4) { + return; + } + + if (lp.showmaskcolmet == 0 || lp.showmaskcolmet == 1 || lp.showmaskcolmet == 2 || lp.showmaskcolmet == 5 || lp.enaColorMask) { + //RGB Curves + bool usergb = false; + + if (rgblocalcurve && localrgbutili && lp.qualcurvemet != 0) { + usergb = true; + const std::unique_ptr tmpImage(new Imagefloat(bfw, bfh)); + + lab2rgb(*buftemp, *(tmpImage.get()), params->icm.workingProfile); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) + for (int x = 0; x < bfw; x++) { + + //std + if (tonemod == 0) { + curves::setLutVal(rgblocalcurve, tmpImage->r(y, x), tmpImage->g(y, x), tmpImage->b(y, x)); + } else { + float r = CLIP(tmpImage->r(y, x)); + float g = CLIP(tmpImage->g(y, x)); + float b = CLIP(tmpImage->b(y, x)); + + if (tonemod == 1) { // weightstd + const float r1 = rgblocalcurve[r]; + const float g1 = triangle(r, r1, g); + const float b1 = triangle(r, r1, b); + + const float g2 = rgblocalcurve[g]; + const float r2 = triangle(g, g2, r); + const float b2 = triangle(g, g2, b); + + const float b3 = rgblocalcurve[b]; + const float r3 = triangle(b, b3, r); + const 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); + } else if (tonemod == 2) { // Luminance + float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; + + const float newLuminance = rgblocalcurve[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); + } else if (tonemod == 3) { // Film like Adobe + if (r >= g) { + if (g > b) { + rgbtone(r, g, b, rgblocalcurve); // Case 1: r >= g > b + } else if (b > r) { + rgbtone(b, r, g, rgblocalcurve); // Case 2: b > r >= g + } else if (b > g) { + rgbtone(r, b, g, rgblocalcurve); // Case 3: r >= b > g + } else { // Case 4: r == g == b + r = rgblocalcurve[r]; + g = rgblocalcurve[g]; + b = g; + } + } else { + if (r >= b) { + rgbtone(g, r, b, rgblocalcurve); // Case 5: g > r >= b + } else if (b > g) { + rgbtone(b, g, r, rgblocalcurve); // Case 6: b > g > r + } else { + rgbtone(g, b, r, rgblocalcurve); // Case 7: g >= b > r + } + } + } + + setUnlessOOG(tmpImage->r(y, x), tmpImage->g(y, x), tmpImage->b(y, x), r, g, b); + } + } + + rgb2lab(*(tmpImage.get()), *buftemp, params->icm.workingProfile); + + // end rgb curves + } + + if (usergb && spez) {//special use of rgb curves ex : negative + const float achm = lp.trans / 100.f; +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh; y++) { + const int loy = y + ystart + cy; + + for (int x = 0; x < bfw; x++) { + const int lox = x + xstart + cx; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, achm, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, achm, lp, zone, localFactor); + } + + if (zone > 0) { + transformed->L[y + ystart][x + xstart] = buftemp->L[y][x] * localFactor + (1.f - localFactor) * original->L[y + ystart][x + xstart]; + transformed->a[y + ystart][x + xstart] = buftemp->a[y][x] * localFactor + (1.f - localFactor) * original->a[y + ystart][x + xstart]; + transformed->b[y + ystart][x + xstart] = buftemp->b[y][x] * localFactor + (1.f - localFactor) * original->b[y + ystart][x + xstart]; + } + } + } + } + + //others curves + + const LabImage *origptr = usergb ? buftemp.get() : bufcolorig.get(); + + bool execcolor = false; + + if (localcutili || HHutili || locallutili || lp.ligh != 0.f || lp.cont != 0 || lp.chro != 0 || LHutili || ctoning) { + execcolor = true; + } + + bool HHcurve = false; + if (lochhCurve && HHutili) { + for (int i = 0; i < 500; i++) { + if (lochhCurve[i] != 0.5) { + HHcurve = true; + break; + } + } + } + + const float kd = 10.f * 0.01f * lp.strengrid;//correction to ctoning + + //chroma slider with curve instead of linear + const float satreal = lp.chro; + + DiagonalCurve color_satur({ + DCT_NURBS, + 0, 0, + 0.2, 0.2 + satreal / 250.0, + 0.6, rtengine::min(1.0, 0.6 + satreal / 250.0), + 1, 1 + }); + + DiagonalCurve color_saturmoins({ + DCT_NURBS, + 0, 0, + 0.1 - satreal / 150., 0.1, + rtengine::min(1.0, 0.7 - satreal / 300.), 0.7, + 1, 1 + }); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + float bufcolcalca = origptr->a[ir][jr]; + float bufcolcalcb = origptr->b[ir][jr]; + float bufcolcalcL = origptr->L[ir][jr]; + + if (lp.chro != 0.f) {//slider chroma with curve DCT_NURBS + float Chprov = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + float2 sincosval; + sincosval.y = Chprov == 0.0f ? 1.f : bufcolcalca / Chprov; + sincosval.x = Chprov == 0.0f ? 0.f : bufcolcalcb / Chprov; + + // 35000 must be globally good, more than 32768...and less than !! to avoid calculation min max + if (lp.chro > 0.f) { + Chprov = color_satur.getVal(LIM01(Chprov / 35000.f)) * 35000.f; + } else { + Chprov = color_saturmoins.getVal(LIM01(Chprov / 35000.f)) * 35000.f; + } + + if (lp.chro == -100.f) { + Chprov = 0.f; + } + + bufcolcalca = Chprov * sincosval.y; + bufcolcalcb = Chprov * sincosval.x; + } + + if (cclocalcurve && lp.qualcurvemet != 0 && localcutili) { // C=f(C) curve + const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + const float ch = cclocalcurve[chromat * adjustr] / ((chromat + 0.00001f) * adjustr); //ch between 0 and 0 50 or more + bufcolcalca *= ch; + bufcolcalcb *= ch; + } + + if (cllocalcurve && lp.qualcurvemet != 0 && localclutili) { // C=f(L) curve + float chromaCfactor = (cllocalcurve[bufcolcalcL * 2.f]) / (bufcolcalcL * 2.f); + bufcolcalca *= chromaCfactor; + bufcolcalcb *= chromaCfactor; + } + + if (lclocalcurve && lp.qualcurvemet != 0 && locallcutili) { // L=f(C) curve + const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + float Lc = lclocalcurve[chromat * adjustr] / ((chromat + 0.00001f) * adjustr); + + if (Lc > 1.f) { + Lc = (Lc - 1.0f) * 0.1f + 1.0f; //reduct action + } else { + Lc = (Lc - 1.0f) * 0.3f + 1.0f; + } + + bufcolcalcL *= Lc; + } + + if (lochhCurve && HHcurve && lp.qualcurvemet != 0 && !ctoning) { // H=f(H) + const float chromat = std::sqrt(SQR(bufcolcalca) + SQR(bufcolcalcb)); + const float hhforcurv = xatan2f(bufcolcalcb, bufcolcalca); + const float valparam = float ((lochhCurve[500.f * Color::huelab_to_huehsv2(hhforcurv)] - 0.5f)); //get H=f(H) + float2 sincosval = xsincosf(valparam); + bufcolcalca = chromat * sincosval.y; + bufcolcalcb = chromat * sincosval.x; + } + + if (lp.ligh != 0.f || lp.cont != 0) {//slider luminance or slider contrast with curve + bufcolcalcL = calclight(bufcolcalcL, lightCurveloc); + } + + if (lllocalcurve && locallutili && lp.qualcurvemet != 0) {// L=f(L) curve + bufcolcalcL = 0.5f * lllocalcurve[bufcolcalcL * 2.f]; + } + + if (loclhCurve && LHutili && lp.qualcurvemet != 0) {//L=f(H) curve + const float rhue = xatan2f(bufcolcalcb, bufcolcalca); + float l_r = bufcolcalcL / 32768.f; //Luminance Lab in 0..1 + const float valparam = loclhCurve[500.f * Color::huelab_to_huehsv2(rhue)] - 0.5f; //get l_r=f(H) + + if (valparam > 0.f) { + l_r = (1.f - valparam) * l_r + valparam * (1.f - SQR(((SQR(1.f - rtengine::min(l_r, 1.0f)))))); + } else { + constexpr float khu = 1.9f; //in reserve in case of! + //for negative + l_r *= (1.f + khu * valparam); + } + + bufcolcalcL = l_r * 32768.f; + + } + + if (ctoning) {//color toning and direct change color + if (lp.gridmet == 0) { + bufcolcalca += kd * bufcolcalcL * a_scale + a_base; + bufcolcalcb += kd * bufcolcalcL * b_scale + b_base; + } else if (lp.gridmet == 1) { + bufcolcalca += kd * scaledirect * a_scale; + bufcolcalcb += kd * scaledirect * b_scale; + } + + bufcolcalca = clipC(bufcolcalca); + bufcolcalcb = clipC(bufcolcalcb); + + } + + bufcolfin->L[ir][jr] = bufcolcalcL; + bufcolfin->a[ir][jr] = bufcolcalca; + bufcolfin->b[ir][jr] = bufcolcalcb; + } + } + + if (HHcurve && ctoning) {//not use ctoning and H(H) simultaneous but priority to ctoning + HHcurve = false; + } + + if (!execcolor) {//if we don't use color and light sliders, curves except RGB +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + bufcolfin->L[ir][jr] = origptr->L[ir][jr]; + bufcolfin->a[ir][jr] = origptr->a[ir][jr]; + bufcolfin->b[ir][jr] = origptr->b[ir][jr]; + } + } + + bool nottransit = false; + if (lp.mergemet >= 2) { //merge result with original + nottransit = true; + bufcolreserv.reset(new LabImage(bfw, bfh)); + JaggedArray lumreserv(bfw, bfh); + const std::unique_ptr bufreser(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + lumreserv[y][x] = 32768.f - reserved->L[y + ystart][x + xstart]; + bufreser->L[y][x] = reserved->L[y + ystart][x + xstart]; + bufreser->a[y][x] = reserved->a[y + ystart][x + xstart]; + bufreser->b[y][x] = reserved->b[y + ystart][x + xstart]; + + if (lp.mergemet == 2) { + bufcolreserv->L[y][x] = reserved->L[y + ystart][x + xstart]; + bufcolreserv->a[y][x] = reserved->a[y + ystart][x + xstart]; + bufcolreserv->b[y][x] = reserved->b[y + ystart][x + xstart]; + } else if (lp.mergemet == 3) { + bufcolreserv->L[y][x] = lastorig->L[y + ystart][x + xstart]; + bufcolreserv->a[y][x] = lastorig->a[y + ystart][x + xstart]; + bufcolreserv->b[y][x] = lastorig->b[y + ystart][x + xstart]; + } else if (lp.mergemet == 4 && ctoningmerg) { + bufcolreserv->L[y][x] = merlucol * 327.68f; + bufcolreserv->a[y][x] = 9.f * scaledirect * a_scalemerg; + bufcolreserv->b[y][x] = 9.f * scaledirect * b_scalemerg; + } + } + } + + if (lp.strcol != 0.f) { + struct grad_params gp; + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 3); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufcolfin->L[ir][jr] *= corrFactor; + } + } + } + + if (lp.strcolab != 0.f) { + struct grad_params gpab; + calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 4); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); + bufcolfin->a[ir][jr] *= corrFactor; + bufcolfin->b[ir][jr] *= corrFactor; + } + } + } + + if (lp.strcolh != 0.f) { + struct grad_params gph; + calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 6); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gph, jr, ir); + const float aa = bufcolfin->a[ir][jr]; + const float bb = bufcolfin->b[ir][jr]; + const float chrm = std::sqrt(SQR(aa) + SQR(bb)); + const float HH = xatan2f(bb, aa); + + float cor = 0.f; + if (corrFactor < 1.f) { + cor = - 2.5f * (1.f - corrFactor); + } else if (corrFactor > 1.f) { + cor = 0.03f * (corrFactor - 1.f); + } + + float newhr = HH + cor; + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + + const float2 sincosval = xsincosf(newhr); + bufcolfin->a[ir][jr] = clipC(chrm * sincosval.y); + bufcolfin->b[ir][jr] = clipC(chrm * sincosval.x); + } + } + } + + JaggedArray blend(bfw, bfh); + buildBlendMask(lumreserv, blend, bfw, bfh, conthr); + const float rm = 20.f / sk; + + if (rm > 0) { + float **mb = blend; +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(mb, mb, bfw, bfh, rm); + } + } + + const std::unique_ptr> rdEBuffer(new JaggedArray(bfw, bfh)); + float** rdE = *(rdEBuffer.get()); + + deltaEforMask(rdE, bfw, bfh, bufreser.get(), hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, mercol, lp.balance, lp.balanceh); + + if (lp.mergecolMethod == 0) { //normal + + if (lp.mergemet == 4) { + bufprov.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + rdE[y][x] *= SQR(rdE[y][x]); + bufprov->L[y][x] = intp(rdE[y][x], bufcolreserv->L[y][x], bufcolfin->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolreserv->a[y][x], bufcolfin->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolreserv->b[y][x], bufcolfin->b[y][x]); + + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolfin->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolfin->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolfin->b[y][x]); + } + } + } else { + bufprov.reset(new LabImage(bfw, bfh)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufprov->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolreserv->b[y][x]); + } + } + } + + if (conthr > 0.f && lp.mergemet != 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufreser->L[y][x]); + bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufreser->a[y][x]); + bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufreser->b[y][x]); + } + } + } + } + + if (lp.mergecolMethod > 16) { //hue sat chroma luma + bufprov.reset(new LabImage(bfw, bfh)); + + if (lp.mergemet == 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + rdE[y][x] *= SQR(rdE[y][x]); + bufprov->L[y][x] = intp(rdE[y][x], bufcolreserv->L[y][x], bufcolfin->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolreserv->a[y][x], bufcolfin->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolreserv->b[y][x], bufcolfin->b[y][x]); + } + } + } else { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufprov->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufprov->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufprov->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + if (lp.mergecolMethod == 17) { + const float huefin = xatan2f(bufprov->b[y][x], bufprov->a[y][x]); + const float2 sincosval1 = xsincosf(huefin); + const float chrores = std::sqrt(SQR(bufcolreserv->a[y][x]) + SQR(bufcolreserv->b[y][x])); + buftemp->a[y][x] = chrores * sincosval1.y; + buftemp->b[y][x] = chrores * sincosval1.x; + buftemp->L[y][x] = bufcolreserv->L[y][x]; + } else if (lp.mergecolMethod == 18) { + const float hueres = xatan2f(bufcolreserv->b[y][x], bufcolreserv->a[y][x]); + const float2 sincosval2 = xsincosf(hueres); + const float chrofin = std::sqrt(SQR(bufprov->a[y][x]) + SQR(bufprov->b[y][x])); + buftemp->a[y][x] = chrofin * sincosval2.y; + buftemp->b[y][x] = chrofin * sincosval2.x; + buftemp->L[y][x] = bufcolreserv->L[y][x]; + } else if (lp.mergecolMethod == 19) { + const float huefin = xatan2f(bufprov->b[y][x], bufprov->a[y][x]); + const float2 sincosval3 = xsincosf(huefin); + const float chrofin = std::sqrt(SQR(bufprov->a[y][x]) + SQR(bufprov->b[y][x])); + buftemp->a[y][x] = chrofin * sincosval3.y; + buftemp->b[y][x] = chrofin * sincosval3.x; + buftemp->L[y][x] = bufcolreserv->L[y][x]; + } else if (lp.mergecolMethod == 20) { + const float hueres = xatan2f(bufcolreserv->b[y][x], bufcolreserv->a[y][x]); + const float2 sincosval4 = xsincosf(hueres); + const float chrores = std::sqrt(SQR(bufcolreserv->a[y][x]) + SQR(bufcolreserv->b[y][x])); + buftemp->a[y][x] = chrores * sincosval4.y; + buftemp->b[y][x] = chrores * sincosval4.x; + buftemp->L[y][x] = bufprov->L[y][x]; + } + + if (lp.mergemet == 4) { + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolfin->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolfin->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolfin->b[y][x]); + } else { + bufcolfin->L[y][x] = intp(lp.opacol, bufprov->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(lp.opacol, bufprov->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(lp.opacol, bufprov->b[y][x], bufcolreserv->b[y][x]); + } + } + } + + if (conthr > 0.f && lp.mergemet != 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + } + } + + + if (lp.mergecolMethod > 0 && lp.mergecolMethod <= 16) { + //first deltaE +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(rdE[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(rdE[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(rdE[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + + //prepare RGB values in 0 1(or more)for current image and reserved + std::unique_ptr tmpImageorig(new Imagefloat(bfw, bfh)); + lab2rgb(*bufcolfin, *(tmpImageorig.get()), params->icm.workingProfile); + tmpImageorig->normalizeFloatTo1(); + + std::unique_ptr tmpImagereserv(new Imagefloat(bfw, bfh)); + lab2rgb(*bufcolreserv, *(tmpImagereserv.get()), params->icm.workingProfile); + tmpImagereserv->normalizeFloatTo1(); + + float minR = tmpImagereserv->r(0, 0); + float maxR = minR; + float minG = tmpImagereserv->g(0, 0); + float maxG = minG; + float minB = tmpImagereserv->b(0, 0); + float maxB = minB; + if (lp.mergecolMethod == 6 || lp.mergecolMethod == 9 || lp.mergecolMethod == 10 || lp.mergecolMethod == 11) { +#ifdef _OPENMP + #pragma omp parallel for reduction(max:maxR,maxG,maxB) reduction(min:minR,minG,minB) schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) { + for (int jr = 0; jr < bfw; jr++) { + minR = rtengine::min(minR, tmpImagereserv->r(ir, jr)); + maxR = rtengine::max(maxR, tmpImagereserv->r(ir, jr)); + minG = rtengine::min(minG, tmpImagereserv->g(ir, jr)); + maxG = rtengine::max(maxG, tmpImagereserv->g(ir, jr)); + minB = rtengine::min(minB, tmpImagereserv->b(ir, jr)); + maxB = rtengine::max(maxB, tmpImagereserv->b(ir, jr)); + } + } + } + + //various combinations subtract, multiply, difference, etc + if (lp.mergecolMethod == 1) { //subtract +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) {//LIM(x 0 2) 2 arbitrary value but limit... + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) - tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) - tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) - tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 2) { //difference +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->r(y, x) - tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->g(y, x) - tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, std::fabs(tmpImageorig->b(y, x) - tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 3) { //multiply +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) * tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) * tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) * tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 4) { //addition +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) + tmpImagereserv->r(y, x), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) + tmpImagereserv->g(y, x), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) + tmpImagereserv->b(y, x), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 5) { //divide +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, tmpImageorig->r(y, x) / (tmpImagereserv->r(y, x) + 0.00001f), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, tmpImageorig->g(y, x) / (tmpImagereserv->g(y, x) + 0.00001f), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, tmpImageorig->b(y, x) / (tmpImagereserv->b(y, x) + 0.00001f), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 6) { //soft light as Photoshop +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, softlig(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), minR, maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, softlig(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), minG, maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, softlig(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), minB, maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 7) { //soft light as illusions.hu +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImageorig->r(y, x))), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImageorig->g(y, x))), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, softlig2(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImageorig->b(y, x))), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 8) { //soft light as W3C +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->r(y, x)), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->g(y, x)), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, softlig3(LIM01(tmpImageorig->b(y, x)), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 9) { //hard light overlay (float &b, float &a) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, overlay(tmpImagereserv->r(y, x), tmpImageorig->r(y, x), minR, maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, overlay(tmpImagereserv->g(y, x), tmpImageorig->g(y, x), minG, maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, overlay(tmpImagereserv->b(y, x), tmpImageorig->b(y, x), minB, maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 10) { //overlay overlay(float &a, float &b) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, overlay(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), minR, maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, overlay(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), minG, maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, overlay(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), minB, maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 11) { //screen screen (float &a, float &b) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, screen(tmpImageorig->r(y, x), tmpImagereserv->r(y, x), maxR), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, screen(tmpImageorig->g(y, x), tmpImagereserv->g(y, x), maxG), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, screen(tmpImageorig->b(y, x), tmpImagereserv->b(y, x), maxB), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 12) { //darken only +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, rtengine::min(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 13) { //lighten only +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, rtengine::max(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 14) { //exclusion exclusion (float &a, float &b) +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, exclusion(tmpImageorig->r(y, x), tmpImagereserv->r(y, x)), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, exclusion(tmpImageorig->g(y, x), tmpImagereserv->g(y, x)), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, exclusion(tmpImageorig->b(y, x), tmpImagereserv->b(y, x)), tmpImageorig->b(y, x)); + } + } + + } else if (lp.mergecolMethod == 15) { //Color burn +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImagereserv->r(y, x))), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImagereserv->g(y, x))), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, colburn(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImagereserv->b(y, x))), tmpImageorig->b(y, x)); + } + } + } else if (lp.mergecolMethod == 16) { //Color dodge +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + tmpImageorig->r(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->r(y, x)), LIM01(tmpImagereserv->r(y, x))), tmpImageorig->r(y, x)); + tmpImageorig->g(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->g(y, x)), LIM01(tmpImagereserv->g(y, x))), tmpImageorig->g(y, x)); + tmpImageorig->b(y, x) = intp(lp.opacol, coldodge(LIM01(tmpImageorig->b(y, x)), LIM01(tmpImagereserv->b(y, x))), tmpImageorig->b(y, x)); + } + } + } + + tmpImageorig->normalizeFloatTo65535(); + rgb2lab(*tmpImageorig, *bufcolfin, params->icm.workingProfile); + + if (conthr > 0.f && lp.mergemet != 4) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < bfh ; y++) { + for (int x = 0; x < bfw; x++) { + bufcolfin->L[y][x] = intp(blend[y][x], bufcolfin->L[y][x], bufcolreserv->L[y][x]); + bufcolfin->a[y][x] = intp(blend[y][x], bufcolfin->a[y][x], bufcolreserv->a[y][x]); + bufcolfin->b[y][x] = intp(blend[y][x], bufcolfin->b[y][x], bufcolreserv->b[y][x]); + } + } + } + } + + if (lp.softradiuscol > 0.f) { + softproc(bufcolreserv.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + } + float meansob = 0.f; + transit_shapedetect2(call, 0, bufcolreserv.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); + } + + if (!nottransit) { +//gradient + if (lp.strcol != 0.f) { + struct grad_params gp; + calclocalGradientParams(lp, gp, ystart, xstart, bfw, bfh, 3); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gp, jr, ir); + bufcolfin->L[ir][jr] *= corrFactor; + } + } + + if (lp.strcolab != 0.f) { + struct grad_params gpab; + calclocalGradientParams(lp, gpab, ystart, xstart, bfw, bfh, 5); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gpab, jr, ir); + bufcolfin->a[ir][jr] *= corrFactor; + bufcolfin->b[ir][jr] *= corrFactor; + } + } + + if (lp.strcolh != 0.f) { + struct grad_params gph; + calclocalGradientParams(lp, gph, ystart, xstart, bfw, bfh, 6); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int ir = 0; ir < bfh; ir++) + for (int jr = 0; jr < bfw; jr++) { + const float corrFactor = ImProcFunctions::calcGradientFactor(gph, jr, ir); + const float aa = bufcolfin->a[ir][jr]; + const float bb = bufcolfin->b[ir][jr]; + const float chrm = std::sqrt(SQR(aa) + SQR(bb)); + const float HH = xatan2f(bb, aa); + + float cor = 0.f; + + if (corrFactor < 1.f) { + cor = - 2.5f * (1.f - corrFactor); + } else if (corrFactor > 1.f) { + cor = 0.03f * (corrFactor - 1.f); + } + + float newhr = HH + cor; + + if (newhr > rtengine::RT_PI_F) { + newhr -= 2 * rtengine::RT_PI_F; + } else if (newhr < -rtengine::RT_PI_F) { + newhr += 2 * rtengine::RT_PI_F; + } + + const float2 sincosval = xsincosf(newhr); + bufcolfin->a[ir][jr] = clipC(chrm * sincosval.y); + bufcolfin->b[ir][jr] = clipC(chrm * sincosval.x); + } + } + + + if (lp.softradiuscol > 0.f) { + softproc(bufcolorig.get(), bufcolfin.get(), lp.softradiuscol, bfh, bfw, 0.0001, 0.00001, 0.1f, sk, multiThread, 1); + } + float meansob = 0.f; + transit_shapedetect2(call, 0, bufcolorig.get(), bufcolfin.get(), originalmaskcol.get(), hueref, chromaref, lumaref, sobelref, meansob, blend2, lp, original, transformed, cx, cy, sk); + } + + } + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + + } + } + } + +//inverse + else if (lp.inv && (lp.chro != 0 || lp.ligh != 0 || exlocalcurve || lp.showmaskcolmetinv == 0 || lp.enaColorMaskinv) && lp.colorena) { + float adjustr = 1.0f; + +//adapt chroma to working profile + if (params->icm.workingProfile == "ProPhoto") { + adjustr = 1.2f; // 1.2 instead 1.0 because it's very rare to have C>170.. + } else if (params->icm.workingProfile == "Adobe RGB") { + adjustr = 1.8f; + } else if (params->icm.workingProfile == "sRGB") { + adjustr = 2.0f; + } else if (params->icm.workingProfile == "WideGamut") { + adjustr = 1.2f; + } else if (params->icm.workingProfile == "Beta RGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BestRGB") { + adjustr = 1.4f; + } else if (params->icm.workingProfile == "BruceRGB") { + adjustr = 1.8f; + } + + std::unique_ptr bufmaskblurcol; + std::unique_ptr originalmaskcol; + const std::unique_ptr bufcolorig(new LabImage(GW, GH)); + + if (lp.enaColorMaskinv || lp.showmaskcolmetinv == 1) { + bufmaskblurcol.reset(new LabImage(GW, GH, true)); + originalmaskcol.reset(new LabImage(GW, GH)); + } + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) if (multiThread) +#endif + for (int y = 0; y < GH ; y++) { + for (int x = 0; x < GW; x++) { + bufcolorig->L[y][x] = original->L[y][x]; + } + } + + constexpr int inv = 1; + const bool showmaske = lp.showmaskcolmetinv == 1; + const bool enaMask = lp.enaColorMaskinv; + constexpr bool deltaE = false; + constexpr bool modmask = false; + const bool zero = lp.showmaskcolmetinv == 0; + constexpr bool modif = false; + + const float chrom = lp.chromacol; + const float rad = lp.radmacol; + const float gamma = lp.gammacol; + const float slope = lp.slomacol; + const float blendm = lp.blendmacol; + const float lap = params->locallab.spots.at(sp).lapmaskcol; + const bool pde = params->locallab.spots.at(sp).laplac; + int shado = params->locallab.spots.at(sp).shadmaskcol; + int level_bl = params->locallab.spots.at(sp).csthresholdcol.getBottomLeft(); + int level_hl = params->locallab.spots.at(sp).csthresholdcol.getTopLeft(); + int level_br = params->locallab.spots.at(sp).csthresholdcol.getBottomRight(); + int level_hr = params->locallab.spots.at(sp).csthresholdcol.getTopRight(); + int sco = params->locallab.spots.at(sp).scopemask; + int shortcu = lp.mergemet; //params->locallab.spots.at(sp).shortc; + int lumask = params->locallab.spots.at(sp).lumask; + float strumask = 0.02f * params->locallab.spots.at(sp).strumaskcol; + + const float mindE = 2.f + MINSCOPE * sco * lp.thr; + const float maxdE = 5.f + MAXSCOPE * sco * (1 + 0.1f * lp.thr); + const float mindElim = 2.f + MINSCOPE * limscope * lp.thr; + const float maxdElim = 5.f + MAXSCOPE * limscope * (1 + 0.1f * lp.thr); + constexpr float amountcd = 0.f; + constexpr float anchorcd = 50.f; + + maskcalccol(false, pde, GW, GH, 0, 0, sk, cx, cy, bufcolorig.get(), bufmaskblurcol.get(), originalmaskcol.get(), original, reserved, inv, lp, + strumask, params->locallab.spots.at(sp).toolcol, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, multiThread, + enaMask, showmaske, deltaE, modmask, zero, modif, chrom, rad, lap, gamma, slope, blendm, shado, amountcd, anchorcd, lmasklocalcurve, localmaskutili, loclmasCurvecolwav, lmasutilicolwav, + level_bl, level_hl, level_br, level_hr, + shortcu, false, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, lp.iterat, limscope, sco + ); + + if (lp.showmaskcolmetinv == 1) { + showmask(lumask, lp, 0, 0, cx, cy, GW, GH, bufcolorig.get(), transformed, bufmaskblurcol.get(), inv); + return; + } + + if (lp.showmaskcolmetinv == 0 || lp.enaColorMaskinv) { + InverseColorLight_Local(false, false, sp, 0, lp, originalmaskcol.get(), lightCurveloc, hltonecurveloc, shtonecurveloc, tonecurveloc, exlocalcurve, cclocalcurve, adjustr, localcutili, lllocalcurve, locallutili, original, transformed, cx, cy, hueref, chromaref, lumaref, sk); + + if (params->locallab.spots.at(sp).recurs) { + original->CopyFrom(transformed, multiThread); + float avge; + calc_ref(sp, original, transformed, 0, 0, original->W, original->H, sk, huerefblur, chromarefblur, lumarefblur, hueref, chromaref, lumaref, sobelref, avge, locwavCurveden, locwavdenutili); + } + } + } + +// Gamut and Munsell control - very important do not deactivated to avoid crash + if (params->locallab.spots.at(sp).avoid) { + const float ach = lp.trans / 100.f; + + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(params->icm.workingProfile); + const float wip[3][3] = { + {static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])}, + {static_cast(wiprof[1][0]), static_cast(wiprof[1][1]), static_cast(wiprof[1][2])}, + {static_cast(wiprof[2][0]), static_cast(wiprof[2][1]), static_cast(wiprof[2][2])} + }; + const bool highlight = params->toneCurve.hrenabled; + const bool needHH = (lp.chro != 0.f); +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { +#ifdef __SSE2__ + float atan2Buffer[transformed->W] ALIGNED16; + float sqrtBuffer[transformed->W] ALIGNED16; + float sincosyBuffer[transformed->W] ALIGNED16; + float sincosxBuffer[transformed->W] ALIGNED16; + vfloat c327d68v = F2V(327.68f); + vfloat onev = F2V(1.f); +#endif + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + for (int y = 0; y < transformed->H; y++) { + const int loy = cy + y; + const bool isZone0 = loy > lp.yc + lp.ly || loy < lp.yc - lp.lyT; // whole line is zone 0 => we can skip a lot of processing + + if (isZone0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + +#ifdef __SSE2__ + int i = 0; + + for (; i < transformed->W - 3; i += 4) { + vfloat av = LVFU(transformed->a[y][i]); + vfloat bv = LVFU(transformed->b[y][i]); + + if (needHH) { // only do expensive atan2 calculation if needed + STVF(atan2Buffer[i], xatan2f(bv, av)); + } + + vfloat Chprov1v = vsqrtf(SQRV(bv) + SQRV(av)); + STVF(sqrtBuffer[i], Chprov1v / c327d68v); + vfloat sincosyv = av / Chprov1v; + vfloat sincosxv = bv / Chprov1v; + vmask selmask = vmaskf_eq(Chprov1v, ZEROV); + sincosyv = vself(selmask, onev, sincosyv); + sincosxv = vselfnotzero(selmask, sincosxv); + STVF(sincosyBuffer[i], sincosyv); + STVF(sincosxBuffer[i], sincosxv); + } + + for (; i < transformed->W; i++) { + float aa = transformed->a[y][i]; + float bb = transformed->b[y][i]; + + if (needHH) { // only do expensive atan2 calculation if needed + atan2Buffer[i] = xatan2f(bb, aa); + } + + float Chprov1 = std::sqrt(SQR(bb) + SQR(aa)); + sqrtBuffer[i] = Chprov1 / 327.68f; + + if (Chprov1 == 0.0f) { + sincosyBuffer[i] = 1.f; + sincosxBuffer[i] = 0.0f; + } else { + sincosyBuffer[i] = aa / Chprov1; + sincosxBuffer[i] = bb / Chprov1; + } + } + +#endif + + for (int x = 0; x < transformed->W; x++) { + int lox = cx + x; + int zone; + float localFactor = 1.f; + + if (lp.shapmet == 0) { + calcTransition(lox, loy, ach, lp, zone, localFactor); + } else /*if (lp.shapmet == 1)*/ { + calcTransitionrect(lox, loy, ach, lp, zone, localFactor); + } + + if (zone == 0) { // outside selection and outside transition zone => no effect, keep original values + continue; + } + + float Lprov1 = transformed->L[y][x] / 327.68f; + float2 sincosval; +#ifdef __SSE2__ + float HH = atan2Buffer[x]; // reading HH from line buffer even if line buffer is not filled is faster than branching + float Chprov1 = sqrtBuffer[x]; + sincosval.y = sincosyBuffer[x]; + sincosval.x = sincosxBuffer[x]; + float chr = 0.f; + +#else + const float aa = transformed->a[y][x]; + const float bb = transformed->b[y][x]; + float HH = 0.f, chr = 0.f; + + if (needHH) { // only do expensive atan2 calculation if needed + HH = xatan2f(bb, aa); + } + + float Chprov1 = std::sqrt(SQR(aa) + SQR(bb)) / 327.68f; + + if (Chprov1 == 0.0f) { + sincosval.y = 1.f; + sincosval.x = 0.0f; + } else { + sincosval.y = aa / (Chprov1 * 327.68f); + sincosval.x = bb / (Chprov1 * 327.68f); + } +#endif + + Color::pregamutlab(Lprov1, HH, chr); + Chprov1 = rtengine::min(Chprov1, chr); + Color::gamutLchonly(sincosval, Lprov1, Chprov1, wip, highlight, 0.15f, 0.92f); + transformed->L[y][x] = Lprov1 * 327.68f; + transformed->a[y][x] = 327.68f * Chprov1 * sincosval.y; + transformed->b[y][x] = 327.68f * Chprov1 * sincosval.x; + + if (needHH) { + const float Lprov2 = original->L[y][x] / 327.68f; + float correctionHue = 0.f; // Munsell's correction + float correctlum = 0.f; + const float memChprov = std::sqrt(SQR(original->a[y][x]) + SQR(original->b[y][x])) / 327.68f; + float Chprov = std::sqrt(SQR(transformed->a[y][x]) + SQR(transformed->b[y][x])) / 327.68f; + Color::AllMunsellLch(true, Lprov1, Lprov2, HH, Chprov, memChprov, correctionHue, correctlum); + + if (std::fabs(correctionHue) < 0.015f) { + HH += correctlum; // correct only if correct Munsell chroma very little. + } + + sincosval = xsincosf(HH + correctionHue); + transformed->a[y][x] = 327.68f * Chprov * sincosval.y; // apply Munsell + transformed->b[y][x] = 327.68f * Chprov * sincosval.x; + } + } + } + } + } + +} + +} diff --git a/rtengine/ipretinex.cc b/rtengine/ipretinex.cc index 6768eca93..02d04c270 100644 --- a/rtengine/ipretinex.cc +++ b/rtengine/ipretinex.cc @@ -45,21 +45,68 @@ #include "curves.h" #include "gauss.h" #include "improcfun.h" -#include "jaggedarray.h" +#include "labimage.h" #include "median.h" #include "opthelper.h" #include "procparams.h" #include "rawimagesource.h" #include "rtengine.h" #include "shmap.h" +#define BENCHMARK #include "StopWatch.h" +#include "guidedfilter.h" + +#define clipretinex( val, minv, maxv ) (( val = (val < minv ? minv : val ) ) > maxv ? maxv : val ) +#define CLIPLOC(x) LIM(x,0.f,32767.f) +#define CLIPC(a) LIM(a, -42000.f, 42000.f) // limit a and b to 130 probably enough ? namespace { -void retinex_scales( float* scales, int nscales, int mode, int s, float high) + +void calcGammaLut(double gamma, double ts, LUTf &gammaLut) { - if ( nscales == 1 ) { - scales[0] = (float)s / 2.f; + double pwr = 1.0 / gamma; + double gamm = gamma; + const double gamm2 = gamma; + rtengine::GammaValues g_a; + + if (gamm2 < 1.0) { + std::swap(pwr, gamm); + } + + rtengine::Color::calcGamma(pwr, ts, 0, g_a); // call to calcGamma with selected gamma and slope + + const double start = gamm2 < 1. ? g_a[2] : g_a[3]; + const double add = g_a[4]; + const double mul = 1.0 + g_a[4]; + + if (gamm2 < 1.) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 1024) +#endif + for (int i = 0; i < 65536; i++) { + const double x = rtengine::Color::igammareti(i / 65535.0, gamm, start, ts, mul, add); + gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values + } + } else { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic, 1024) +#endif + for (int i = 0; i < 65536; i++) { + const double x = rtengine::Color::gammareti(i / 65535.0, gamm, start, ts, mul, add); + gammaLut[i] = 0.5 * rtengine::CLIP(x * 65535.0); // CLIP avoid in some case extra values + } + } +} + +void retinex_scales(float* scales, int nscales, int mode, int s, float high) +{ + if (s < 3) { + s = 3; //to avoid crash in MSRlocal if nei small + } + + if (nscales == 1) { + scales[0] = (float)s / 2.f; } else if (nscales == 2) { scales[1] = (float) s / 2.f; scales[0] = (float) s; @@ -67,32 +114,32 @@ void retinex_scales( float* scales, int nscales, int mode, int s, float high) float size_step = (float) s / (float) nscales; if (mode == 0) { - for (int i = 0; i < nscales; ++i ) { + for (int i = 0; i < nscales; ++i) { scales[nscales - i - 1] = 2.0f + (float)i * size_step; } } else if (mode == 1) { size_step = (float)log(s - 2.0f) / (float) nscales; - for (int i = 0; i < nscales; ++i ) { - scales[nscales - i - 1] = 2.0f + (float)pow (10.f, (i * size_step) / log (10.f)); + for (int i = 0; i < nscales; ++i) { + scales[nscales - i - 1] = 2.0f + (float)pow(10.f, (i * size_step) / log(10.f)); } } else if (mode == 2) { size_step = (float) log(s - 2.0f) / (float) nscales; - for ( int i = 0; i < nscales; ++i ) { - scales[i] = s - (float)pow (10.f, (i * size_step) / log (10.f)); + for (int i = 0; i < nscales; ++i) { + scales[i] = s - (float)pow(10.f, (i * size_step) / log(10.f)); } } else if (mode == 3) { size_step = (float) log(s - 2.0f) / (float) nscales; - for ( int i = 0; i < nscales; ++i ) { - scales[i] = high * s - (float)pow (10.f, (i * size_step) / log (10.f)); + for (int i = 0; i < nscales; ++i) { + scales[i] = high * s - (float)pow(10.f, (i * size_step) / log(10.f)); } } } } -void mean_stddv2( float **dst, float &mean, float &stddv, int W_L, int H_L, float &maxtr, float &mintr) +void mean_stddv2(float **dst, float &mean, float &stddv, int W_L, int H_L, float &maxtr, float &mintr) { // summation using double precision to avoid too large summation error for large pictures double vsquared = 0.f; @@ -108,7 +155,7 @@ void mean_stddv2( float **dst, float &mean, float &stddv, int W_L, int H_L, floa #pragma omp for reduction(+:sum,vsquared) nowait // this leads to differences, but parallel summation is more accurate #endif - for (int i = 0; i < H_L; i++ ) + for (int i = 0; i < H_L; i++) for (int j = 0; j < W_L; j++) { sum += static_cast(dst[i][j]); vsquared += rtengine::SQR(dst[i][j]); @@ -126,7 +173,7 @@ void mean_stddv2( float **dst, float &mean, float &stddv, int W_L, int H_L, floa } } - mean = sum / (double) (W_L * H_L); + mean = sum / (double)(W_L * H_L); vsquared /= (double) W_L * H_L; stddv = vsquared - rtengine::SQR(mean); stddv = std::sqrt(stddv); @@ -140,7 +187,8 @@ namespace rtengine void RawImageSource::MSR(float** luminance, float** originalLuminance, float **exLuminance, const LUTf& mapcurve, bool mapcontlutili, int width, int height, const procparams::RetinexParams &deh, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax) { -BENCHFUN + BENCHFUN + if (!deh.enabled) { return; } @@ -184,7 +232,7 @@ BENCHFUN bool higplus = false ; int moderetinex = 2; // default to 2 ( deh.retinexMethod == "high" ) - if(deh.retinexMethod == "highliplus") { + if (deh.retinexMethod == "highliplus") { higplus = true; moderetinex = 3; } else if (deh.retinexMethod == "uni") { @@ -249,6 +297,7 @@ BENCHFUN //adjust sc in function of choice of scale by user if iterations if (scal < 3) { sc -= 1; + if (sc < 1.f) {//avoid 0 sc = 1.f; } @@ -324,11 +373,11 @@ BENCHFUN int mapmet = 0; - if(deh.mapMethod == "map") { + if (deh.mapMethod == "map") { mapmet = 2; - } else if(deh.mapMethod == "mapT") { + } else if (deh.mapMethod == "mapT") { mapmet = 3; - } else if(deh.mapMethod == "gaus") { + } else if (deh.mapMethod == "gaus") { mapmet = 4; } @@ -336,13 +385,13 @@ BENCHFUN int viewmet = 0; - if(deh.viewMethod == "mask") { + if (deh.viewMethod == "mask") { viewmet = 1; - } else if(deh.viewMethod == "tran") { + } else if (deh.viewMethod == "tran") { viewmet = 2; - } else if(deh.viewMethod == "tran2") { + } else if (deh.viewMethod == "tran2") { viewmet = 3; - } else if(deh.viewMethod == "unsharp") { + } else if (deh.viewMethod == "unsharp") { viewmet = 4; } @@ -365,16 +414,18 @@ BENCHFUN const float logBetaGain = xlogf(16384.f); float pond = logBetaGain / (float) scal; - if(!useHslLin) { + if (!useHslLin) { pond /= log(elogt); } std::unique_ptr shmap; + if (((mapmet == 2 || mapmet == 3 || mapmet == 4) && it == 1)) { shmap.reset(new SHMap(W_L, H_L)); } std::unique_ptr buffer; + if (mapmet > 0) { buffer.reset(new float[W_L * H_L]); } @@ -384,7 +435,7 @@ BENCHFUN gaussianBlur(src, out, W_L, H_L, RetinexScales[scale], true); } else { // reuse result of last iteration // out was modified in last iteration => restore it - if((((mapmet == 2 && scale > 1) || mapmet == 3 || mapmet == 4) || (mapmet > 0 && mapcontlutili)) && it == 1) { + if ((((mapmet == 2 && scale > 1) || mapmet == 3 || mapmet == 4) || (mapmet > 0 && mapcontlutili)) && it == 1) { #ifdef _OPENMP #pragma omp parallel for #endif @@ -411,8 +462,10 @@ BENCHFUN } } } + int h_th = 0; int s_th = 0; + if (((mapmet == 2 && scale > 2) || mapmet == 3 || mapmet == 4) && it == 1) { shmap->updateL(out, shradius, true, 1); h_th = shmap->max_f - deh.htonalwidth * (shmap->max_f - shmap->avg) / 100; @@ -468,7 +521,8 @@ BENCHFUN const vfloat pondv = F2V(pond); const vfloat limMinv = F2V(ilimdx); const vfloat limMaxv = F2V(limdx); - if( useHslLin) { + + if (useHslLin) { for (; j < W_L - 3; j += 4) { STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv)); } @@ -480,7 +534,7 @@ BENCHFUN #endif - if(useHslLin) { + if (useHslLin) { for (; j < W_L; j++) { luminance[i][j] += pond * LIM(src[i][j] / out[i][j], ilimdx, limdx); } @@ -522,9 +576,10 @@ BENCHFUN #pragma omp parallel for schedule(dynamic,16) #endif - for (int i = 0; i < H_L; i++ ) { + for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { //for mintr to maxtr evalate absciss in function of original transmission float absciss; + if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { absciss = asig * luminance[i][j] + bsig; } else if (luminance[i][j] >= mean) { @@ -536,7 +591,7 @@ BENCHFUN //TODO : move multiplication by 4.f and subtraction of 1.f inside the curve luminance[i][j] *= (-1.f + 4.f * dehatransmissionCurve[absciss]); //new transmission - if(viewmet == 3 || viewmet == 2) { + if (viewmet == 3 || viewmet == 2) { tran[i][j] = luminance[i][j]; } } @@ -561,7 +616,7 @@ BENCHFUN #pragma omp parallel for #endif - for (int i = borderL; i < H_L - borderL; i++ ) { + for (int i = borderL; i < H_L - borderL; i++) { for (int j = borderL; j < W_L - borderL; j++) { luminance[i][j] = tmL[i][j]; } @@ -590,7 +645,7 @@ BENCHFUN float delta = maxi - mini; //printf("maxi=%f mini=%f mean=%f std=%f delta=%f maxtr=%f mintr=%f\n", maxi, mini, mean, stddv, delta, maxtr, mintr); - if ( !delta ) { + if (!delta) { delta = 1.0f; } @@ -642,7 +697,7 @@ BENCHFUN #pragma omp parallel for reduction(max:maxCD) reduction(min:minCD) schedule(dynamic, 16) #endif - for ( int i = 0; i < H_L; i ++ ) { + for (int i = 0; i < H_L; i ++) { for (int j = 0; j < W_L; j++) { float gan; @@ -677,7 +732,7 @@ BENCHFUN const float HH = exLuminance[i][j]; float valparam; - if(useHsl || useHslLin) { + if (useHsl || useHslLin) { valparam = shcurve->getVal(HH) - 0.5; } else { valparam = shcurve->getVal(Color::huelab_to_huehsv2(HH)) - 0.5; @@ -698,7 +753,7 @@ BENCHFUN } else if (viewmet == 4) { luminance[i][j] = originalLuminance[i][j] + str * (originalLuminance[i][j] - out[i][j]);//unsharp } else if (viewmet == 2) { - if(tran[i][j] <= mean) { + if (tran[i][j] <= mean) { luminance[i][j] = azb + aza * tran[i][j]; //auto values } else { luminance[i][j] = bzb + bza * tran[i][j]; @@ -722,4 +777,884 @@ BENCHFUN } } + +void ImProcFunctions::maskforretinex(int sp, int before, float ** luminance, float ** out, int W_L, int H_L, int skip, + const LocCCmaskCurve & locccmasretiCurve, bool &lcmasretiutili, const LocLLmaskCurve & locllmasretiCurve, bool &llmasretiutili, const LocHHmaskCurve & lochhmasretiCurve, bool & lhmasretiutili, + int llretiMask, bool retiMasktmap, bool retiMask, float rad, float lap, bool pde, float gamm, float slop, float chro, float blend, + LUTf & lmaskretilocalcurve, bool & localmaskretiutili, + LabImage * bufreti, LabImage * bufmask, LabImage * buforig, LabImage * buforigmas, bool multiThread, + bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh, float lumask) +{ + array2D loctemp(W_L, H_L); + array2D ble(W_L, H_L); + array2D blechro(W_L, H_L); + array2D hue(W_L, H_L); + array2D guid(W_L, H_L); + std::unique_ptr bufmaskblurreti; + bufmaskblurreti.reset(new LabImage(W_L, H_L)); + std::unique_ptr bufmaskorigreti; + bufmaskorigreti.reset(new LabImage(W_L, H_L)); + std::unique_ptr bufprov; + bufprov.reset(new LabImage(W_L, H_L)); + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + if (before == 1 && retiMasktmap) { + loctemp[y][x] = LIM(luminance[y][x], 0.f, 32768.f); + } else if (before == 0 && retiMasktmap) { + loctemp[y][x] = out[y][x]; + } else { + loctemp[y][x] = bufreti->L[y][x]; + } + } + } + + float chromult = 1.f - 0.01f * chro; +//fab + float fab = 50.f; + float meanfab = 0.f; + const int nbfab = W_L * H_L; + + double sumab = 0.0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:sumab) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + sumab += fabs(bufreti->a[y][x]); + sumab += fabs(bufreti->b[y][x]); + } + } + + meanfab = sumab / (2.f * nbfab); + double som = 0.0; + +#ifdef _OPENMP + #pragma omp parallel for reduction(+:som) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + som += SQR(fabs(bufreti->a[y][x]) - meanfab) + SQR(fabs(bufreti->b[y][x]) - meanfab); + } + } + const float multsigma = (chro >= 0.f ? 0.035f : 0.018f) * chro + 1.f; + const float stddv = sqrt(som / nbfab); + fab = meanfab + multsigma * stddv; + + if (fab <= 0.f) { + fab = 50.f; + } +//end fab + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int ir = 0; ir < H_L; ir++) { + for (int jr = 0; jr < W_L; jr++) { + float kmaskLexp = 0; + float kmaskCH = 0; + + if (locllmasretiCurve && llmasretiutili) { + float ligh = loctemp[ir][jr] / 32768.f; + kmaskLexp = 32768.f * LIM01(1.f - locllmasretiCurve[500.f * ligh]); + + } + + + if (locllmasretiCurve && llmasretiutili && retiMasktmap) { + } + + if (llretiMask != 4) { + if (locccmasretiCurve && lcmasretiutili) { + float chromask = 0.0001f + sqrt(SQR((bufreti->a[ir][jr]) / fab) + SQR((bufreti->b[ir][jr]) / fab)); + kmaskCH = LIM01(1.f - locccmasretiCurve[500.f * chromask]); + } + } + + if (lochhmasretiCurve && lhmasretiutili) { + float huema = xatan2f(bufreti->b[ir][jr], bufreti->a[ir][jr]); + float h = Color::huelab_to_huehsv2(huema); + h += 1.f / 6.f; + + if (h > 1.f) { + h -= 1.f; + } + + float valHH = LIM01(1.f - lochhmasretiCurve[500.f * h]); + + if (llretiMask != 4) { + kmaskCH += chromult * valHH; + } + + kmaskLexp += 32768.f * valHH; + } + + bufmaskblurreti->L[ir][jr] = kmaskLexp; + bufmaskblurreti->a[ir][jr] = kmaskCH; + bufmaskblurreti->b[ir][jr] = kmaskCH; + ble[ir][jr] = bufmaskblurreti->L[ir][jr] / 32768.f; + hue[ir][jr] = xatan2f(bufmaskblurreti->b[ir][jr], bufmaskblurreti->a[ir][jr]); + float chromah = sqrt(SQR(bufmaskblurreti->b[ir][jr]) + SQR(bufmaskblurreti->a[ir][jr])); + blechro[ir][jr] = chromah / 32768.f; + guid[ir][jr] = bufreti->L[ir][jr] / 32768.f; + bufprov->L[ir][jr] = bufmaskblurreti->L[ir][jr]; + bufprov->a[ir][jr] = bufmaskblurreti->a[ir][jr]; + bufprov->b[ir][jr] = bufmaskblurreti->b[ir][jr]; + + } + } + + if (rad != 0.f) { +// guidedFilter(guid, ble, ble, rad * 10.f / skip, 0.001, multiThread, 4); + float blur = rad; + blur = blur < 0.f ? -1.f / blur : 1.f + blur; + int r1 = max(int(4 / skip * blur + 0.5), 1); + int r2 = max(int(25 / skip * blur + 0.5), 1); + + double epsilmax = 0.0005; + double epsilmin = 0.00001; + + double aepsil = (epsilmax - epsilmin) / 90.f; + double bepsil = epsilmax - 100.f * aepsil; + double epsil = aepsil * 0.1 * rad + bepsil; + if (rad < 0.f) { + epsil = 0.001; + } + rtengine::guidedFilter(guid, blechro, blechro, r1, epsil, multiThread); + rtengine::guidedFilter(guid, ble, ble, r2, 0.2 * epsil, multiThread); + + } + + LUTf lutTonemaskreti(65536); + calcGammaLut(gamm, slop, lutTonemaskreti); + float radiusb = 1.f / skip; + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int ir = 0; ir < H_L; ir++) + for (int jr = 0; jr < W_L; jr++) { + float L_; + float2 sincosval = xsincosf(hue[ir][jr]); + bufmaskblurreti->L[ir][jr] = LIM01(ble[ir][jr]) * 32768.f; + L_ = 2.f * bufmaskblurreti->L[ir][jr]; + bufmaskblurreti->L[ir][jr] = lutTonemaskreti[L_]; + bufmaskblurreti->a[ir][jr] = 32768.f * sincosval.y * blechro[ir][jr]; + bufmaskblurreti->b[ir][jr] = 32768.f * sincosval.x * blechro[ir][jr]; + } + + if (lmaskretilocalcurve && localmaskretiutili) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int ir = 0; ir < H_L; ir++) + for (int jr = 0; jr < W_L; jr++) { + bufmaskblurreti->L[ir][jr] = 0.5f * lmaskretilocalcurve[2.f * bufmaskblurreti->L[ir][jr]]; + } + } + + + if (delt) { + float *rdE[H_L] ALIGNED16; + float *rdEBuffer = new float[H_L * W_L]; + + for (int i = 0; i < H_L; i++) { + rdE[i] = &rdEBuffer[i * W_L]; + } + + deltaEforMask(rdE, W_L, H_L, bufreti, hueref, chromaref, lumaref, maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, balance, balanceh); + // printf("rde1=%f rde2=%f\n", rdE[1][1], rdE[100][100]); + std::unique_ptr delta(new LabImage(W_L, H_L)); +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int ir = 0; ir < H_L; ir++) + for (int jr = 0; jr < W_L; jr++) { + delta->L[ir][jr] = bufmaskblurreti->L[ir][jr] - bufprov->L[ir][jr]; + delta->a[ir][jr] = bufmaskblurreti->a[ir][jr] - bufprov->a[ir][jr]; + delta->b[ir][jr] = bufmaskblurreti->b[ir][jr] - bufprov->b[ir][jr]; + + bufmaskblurreti->L[ir][jr] = bufprov->L[ir][jr] + rdE[ir][jr] * delta->L[ir][jr]; + bufmaskblurreti->a[ir][jr] = bufprov->a[ir][jr] + rdE[ir][jr] * delta->a[ir][jr]; + bufmaskblurreti->b[ir][jr] = bufprov->b[ir][jr] + rdE[ir][jr] * delta->b[ir][jr]; + } + + delete [] rdEBuffer; + + } + + + if (lap > 0.f) { + float *datain = new float[H_L * W_L]; + float *data_tmp = new float[H_L * W_L]; + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + datain[y * W_L + x] = bufmaskblurreti->L[y][x]; + } + } + + if (!pde) { + ImProcFunctions::discrete_laplacian_threshold(data_tmp, datain, W_L, H_L, 200.f * lap); + } else { + ImProcFunctions::retinex_pde(datain, data_tmp, W_L, H_L, 12.f * lap, 1.f, nullptr, 0, 0, 1); + } + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + bufmaskblurreti->L[y][x] = data_tmp[y * W_L + x]; + } + } + + delete [] datain; + delete [] data_tmp; + + } + +//blend +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(bufmaskblurreti->L, bufmaskorigreti->L, W_L, H_L, radiusb); + gaussianBlur(bufmaskblurreti->a, bufmaskorigreti->a, W_L, H_L, 1.f + (0.5f * rad) / skip); + gaussianBlur(bufmaskblurreti->b, bufmaskorigreti->b, W_L, H_L, 1.f + (0.5f * rad) / skip); + } + + float modr = 0.01f * (float) blend; + + if (llretiMask != 3 && retiMask) { +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + if (before == 0 && retiMasktmap) { + out[y][x] += fabs(modr) * bufmaskorigreti->L[y][x]; + out[y][x] = LIM(out[y][x], 0.f, 100000.f); + } else { + bufreti->L[y][x] += bufmaskorigreti->L[y][x] * modr; + bufreti->L[y][x] = CLIPLOC(bufreti->L[y][x]); + + } + + bufreti->a[y][x] *= (1.f + bufmaskorigreti->a[y][x] * modr * (1.f + 0.01f * chro)); + bufreti->b[y][x] *= (1.f + bufmaskorigreti->b[y][x] * modr * (1.f + 0.01f * chro)); + bufreti->a[y][x] = CLIPC(bufreti->a[y][x]); + bufreti->b[y][x] = CLIPC(bufreti->b[y][x]); + } + } + + + } + + if (!retiMasktmap && retiMask) { //new original blur mask for deltaE +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + + buforig->L[y][x] += (modr * bufmaskorigreti->L[y][x]); + buforig->a[y][x] *= (1.f + modr * bufmaskorigreti->a[y][x]); + buforig->b[y][x] *= (1.f + modr * bufmaskorigreti->b[y][x]); + + buforig->L[y][x] = CLIP(buforig->L[y][x]); + buforig->a[y][x] = CLIPC(buforig->a[y][x]); + buforig->b[y][x] = CLIPC(buforig->b[y][x]); + + buforig->L[y][x] = CLIP(buforig->L[y][x] - bufmaskorigreti->L[y][x]); + buforig->a[y][x] = CLIPC(buforig->a[y][x] * (1.f - bufmaskorigreti->a[y][x])); + buforig->b[y][x] = CLIPC(buforig->b[y][x] * (1.f - bufmaskorigreti->b[y][x])); + } + } + + float radius = 3.f / skip; + +#ifdef _OPENMP + #pragma omp parallel if (multiThread) +#endif + { + gaussianBlur(buforig->L, buforigmas->L, W_L, H_L, radius); + gaussianBlur(buforig->a, buforigmas->a, W_L, H_L, radius); + gaussianBlur(buforig->b, buforigmas->b, W_L, H_L, radius); + } + + } + + + if (llretiMask == 3) { + +#ifdef _OPENMP + #pragma omp parallel for schedule(dynamic,16) +#endif + + for (int y = 0; y < H_L; y++) { + for (int x = 0; x < W_L; x++) { + bufmask->L[y][x] = (lumask * 400.f) + CLIPLOC(bufmaskorigreti->L[y][x]); + bufmask->a[y][x] = CLIPC(bufreti->a[y][x] * bufmaskorigreti->a[y][x]); + bufmask->b[y][x] = CLIPC(bufreti->b[y][x] * bufmaskorigreti->b[y][x]); + } + } + } + +} + + + +void ImProcFunctions::MSRLocal(int call, int sp, bool fftw, int lum, float** reducDE, LabImage * bufreti, LabImage * bufmask, LabImage * buforig, LabImage * buforigmas, float** luminance, const float* const *originalLuminance, + const int width, const int height, int bfwr, int bfhr, const procparams::LocallabParams &loc, const int skip, const LocretigainCurve &locRETgainCcurve, const LocretitransCurve &locRETtransCcurve, + const int chrome, const int scall, const float krad, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax, + const LocCCmaskCurve & locccmasretiCurve, bool &lcmasretiutili, const LocLLmaskCurve & locllmasretiCurve, bool &llmasretiutili, const LocHHmaskCurve & lochhmasretiCurve, bool & lhmasretiutili, int llretiMask, + LUTf & lmaskretilocalcurve, bool & localmaskretiutili, + LabImage * transformed, bool retiMasktmap, bool retiMask, + bool delt, const float hueref, const float chromaref, const float lumaref, + float maxdE, float mindE, float maxdElim, float mindElim, float iterat, float limscope, int scope, float balance, float balanceh, float lumask) + +{ + BENCHFUN + bool py = true; + + if (py) {//enabled + float mean, stddv, maxtr, mintr; + mean = 0.f; + stddv = 0.f; + maxtr = 0.f; + mintr = 0.f; + float delta; + constexpr float eps = 2.f; + bool useHslLin = false; + const float offse = loc.spots.at(sp).offs; + const float chrT = (float)(loc.spots.at(sp).chrrt) / 100.f; + const int scal = (loc.spots.at(sp).scalereti); + float vart = loc.spots.at(sp).vart / 100.f;//variance + const float strength = loc.spots.at(sp).str / 100.f; // Blend with original L channel data + const float dar = loc.spots.at(sp).darkness; + const float lig = loc.spots.at(sp).lightnessreti; + float value = pow(strength, 0.4f); + float value_1 = pow(strength, 0.3f); + bool logli = loc.spots.at(sp).loglin; + float limD = loc.spots.at(sp).limd;//10.f + limD = pow(limD, 1.7f); //about 2500 enough + float ilimD = 1.f / limD; + float threslum = loc.spots.at(sp).limd; + const float elogt = 2.71828f; + + if (!logli) { + useHslLin = true; + } + + //empirical skip evaluation : very difficult because quasi all parameters interfere + //to test on several images + int nei = (int)(krad * loc.spots.at(sp).neigh); + // printf("neigh=%i\n", nei); + //several test to find good values ???!!! + //very difficult to do because 4 factor are correlate with skip and cannot been solved easily + // size of spots + // radius - neigh + // scal + // variance vart + //not too bad proposition + float divsca = 1.f; + + if (scal >= 3) { + divsca = sqrt(scal / 3.f); + } + + if (skip >= 4) { + //nei = (int)(0.1f * nei + 2.f); //not too bad + nei = (int)(nei / (1.5f * skip)) / divsca; + vart *= sqrt(skip); + } else if (skip > 1 && skip < 4) { + //nei = (int)(0.3f * nei + 2.f); + nei = (int)(nei / skip) / divsca; + vart *= sqrt(skip); + } + + int moderetinex = 0; + + if (loc.spots.at(sp).retinexMethod == "uni") { + moderetinex = 0; + } else if (loc.spots.at(sp).retinexMethod == "low") { + moderetinex = 1; + } else if (loc.spots.at(sp).retinexMethod == "high") { + moderetinex = 2; + } + + const float high = 0.f; // Dummy to pass to retinex_scales(...) + + constexpr auto maxRetinexScales = 10; + float RetinexScales[maxRetinexScales]; + + retinex_scales(RetinexScales, scal, moderetinex, nei, high); + + + const int H_L = height; + const int W_L = width; + std::unique_ptr> srcBuffer(new JaggedArray(W_L, H_L)); + float** src = *(srcBuffer.get()); + + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H_L; i++) + for (int j = 0; j < W_L; j++) { + src[i][j] = luminance[i][j] + eps; + luminance[i][j] = 0.f; + } + + JaggedArray out(W_L, H_L); + + float clipt = loc.spots.at(sp).cliptm; + + const float logBetaGain = xlogf(16384.f); + float pond = logBetaGain / (float) scal; + + if (!useHslLin) { + pond /= log(elogt); + } + + float kr = 1.f;//on FFTW + float kg = 1.f;//on Gaussianblur + std::unique_ptr buffer; + buffer.reset(new float[W_L * H_L]); + + for (int scale = scal - 1; scale >= 0; --scale) { + // printf("retscale=%f scale=%i \n", mulradiusfftw * RetinexScales[scale], scale); + //emprical adjustment between FFTW radius and Gaussainblur + //under 50 ==> 10.f + // 400 ==> 1.f + float sigm = 1.f; + + if (settings->fftwsigma == false) { //empirical formula + sigm = RetinexScales[scale]; + float ak = -9.f / 350.f; + float bk = 10.f - 50.f * ak; + kr = ak * sigm + bk; + + if (sigm < 50.f) { + kr = 10.f; + } + + //above 400 at 5000 ==> 20.f + if (sigm > 400.f) { //increase ==> 5000 + float ka = 19.f / 4600.f; + float kb = 1.f - 400 * ka; + kr = ka * sigm + kb; + float kga = -0.14f / 4600.f;//decrease + float kgb = 1.f - 400.f * kga; + kg = kga * sigm + kgb; + + if (sigm > 5000.f) { + kr = ka * 5000.f + kb; + kg = kga * 5000.f + kgb; + } + + } + } else {//sigma *= sigma + kg = 1.f; + kr = sigm; + } + printf("call=%i\n", call); + if (!fftw) { // || (fftw && call != 2)) { + if (scale == scal - 1) { + gaussianBlur(src, out, W_L, H_L, kg * RetinexScales[scale], true); + } else { // reuse result of last iteration + // out was modified in last iteration => restore it + gaussianBlur(out, out, W_L, H_L, sqrtf(SQR(kg * RetinexScales[scale]) - SQR(kg * RetinexScales[scale + 1])), true); + } + } else { + if (scale == scal - 1) { + if (settings->fftwsigma == false) { //empirical formula + ImProcFunctions::fftw_convol_blur2(src, out, bfwr, bfhr, (kr * RetinexScales[scale]), 0, 0); + } else { + ImProcFunctions::fftw_convol_blur2(src, out, bfwr, bfhr, (SQR(RetinexScales[scale])), 0, 0); + } + } else { // reuse result of last iteration + // out was modified in last iteration => restore it + if (settings->fftwsigma == false) { //empirical formula + ImProcFunctions::fftw_convol_blur2(out, out, bfwr, bfhr, sqrtf(SQR(kr * RetinexScales[scale]) - SQR(kr * RetinexScales[scale + 1])), 0, 0); + } else { + ImProcFunctions::fftw_convol_blur2(out, out, bfwr, bfhr, (SQR(RetinexScales[scale]) - SQR(RetinexScales[scale + 1])), 0, 0); + } + } + } + + if (scale == 1) { //equalize last scale with darkness and lightness of course acts on TM! + if (dar != 1.f || lig != 1.f) { + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H_L; ++y) { + for (int x = 0; x < W_L; ++x) { + float buf = (src[y][x] - out[y][x]) * value; + buf *= (buf > 0.f) ? lig : dar; + out[y][x] = LIM(out[y][x] + buf, -100000.f, 100000.f); + } + } + } + + } + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H_L; i++) { + int j = 0; + +#ifdef __SSE2__ + const vfloat pondv = F2V(pond); + const vfloat limMinv = F2V(ilimD); + const vfloat limMaxv = F2V(limD); + + if (useHslLin) { + for (; j < W_L - 3; j += 4) { + STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv)); + } + } else { + for (; j < W_L - 3; j += 4) { + STVFU(luminance[i][j], LVFU(luminance[i][j]) + pondv * xlogf(vclampf(LVFU(src[i][j]) / LVFU(out[i][j]), limMinv, limMaxv))); + } + } + +#endif + + if (useHslLin) { + for (; j < W_L; j++) { + luminance[i][j] += pond * (LIM(src[i][j] / out[i][j], ilimD, limD)); + } + } else { + for (; j < W_L; j++) { + luminance[i][j] += pond * xlogf(LIM(src[i][j] / out[i][j], ilimD, limD)); + } + } + } + + } + +// srcBuffer.reset(); + + + if (scal == 1) {//only if user select scal = 1 + + float kval = 1.f; +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H_L; ++y) { + for (int x = 0; x < W_L; ++x) { + float threslow = threslum * 163.f; + + if (src[y][x] < threslow) { + kval = src[y][x] / threslow; + } + } + } + + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int y = 0; y < H_L; ++y) { + for (int x = 0; x < W_L; ++x) { + float buf = (src[y][x] - out[y][x]) * value_1; + buf *= (buf > 0.f) ? lig : dar; + luminance[y][x] = LIM(src[y][x] + (1.f + kval) * buf, -32768.f, 32768.f); + } + } + + double avg = 0.f; + int ng = 0; + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H_L; i++) { + for (int j = 0; j < W_L; j++) { + avg += luminance[i][j]; + ng++; + } + } + + avg /= ng; + avg /= 32768.f; + avg = LIM01(avg); + float contreal = 0.5f * vart; + DiagonalCurve reti_contrast({ + DCT_NURBS, + 0, 0, + avg - avg * (0.6 - contreal / 250.0), avg - avg * (0.6 + contreal / 250.0), + avg + (1 - avg) * (0.6 - contreal / 250.0), avg + (1 - avg) * (0.6 + contreal / 250.0), + 1, 1 + }); + +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H_L; i++) + for (int j = 0; j < W_L; j++) { + float buf = LIM01(luminance[i][j] / 32768.f); + buf = reti_contrast.getVal(buf); + buf *= 32768.f; + luminance[i][j] = buf; + } + + } + + srcBuffer.reset(); + + float str = strength * (chrome == 0 ? 1.f : 0.8f * (chrT - 0.4f)); + const float maxclip = (chrome == 0 ? 32768.f : 50000.f); + + if (scal != 1) { + mean = 0.f; + stddv = 0.f; + + mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); + // printf("mean=%f std=%f delta=%f maxtr=%f mintr=%f\n", mean, stddv, delta, maxtr, mintr); + + if (locRETtransCcurve && mean != 0.f && stddv != 0.f) { //if curve + float asig = 0.166666f / stddv; + float bsig = 0.5f - asig * mean; + float amax = 0.333333f / (maxtr - mean - stddv); + float bmax = 1.f - amax * maxtr; + float amin = 0.333333f / (mean - stddv - mintr); + float bmin = -amin * mintr; + + asig *= 500.f; + bsig *= 500.f; + amax *= 500.f; + bmax *= 500.f; + amin *= 500.f; + bmin *= 500.f; + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + float absciss; +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + + for (int i = 0; i < H_L; i++) + for (int j = 0; j < W_L; j++) { //for mintr to maxtr evalate absciss in function of original transmission + if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { + absciss = asig * luminance[i][j] + bsig; + } else if (luminance[i][j] >= mean) { + absciss = amax * luminance[i][j] + bmax; + } else { /*if(luminance[i][j] <= mean - stddv)*/ + absciss = amin * luminance[i][j] + bmin; + } + + //TODO : move multiplication by 4.f and subtraction of 1.f inside the curve + luminance[i][j] *= (-1.f + 4.f * locRETtransCcurve[absciss]); //new transmission + + } + } + } + + mean = 0.f; + stddv = 0.f; + mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr);//new calculation of mean... + + float epsil = 0.1f; + + mini = mean - vart * stddv; + + if (mini < mintr) { + mini = mintr + epsil; + } + + maxi = mean + vart * stddv; + + if (maxi > maxtr) { + maxi = maxtr - epsil; + } + + delta = maxi - mini; + // printf("maxi=%f mini=%f mean=%f std=%f delta=%f maxtr=%f mintr=%f\n", maxi, mini, mean, stddv, delta, maxtr, mintr); + + if (!delta) { + delta = 1.0f; + } + + + float *copylum[H_L] ALIGNED16; + float *copylumBuffer = new float[H_L * W_L]; + + for (int i = 0; i < H_L; i++) { + copylum[i] = ©lumBuffer[i * W_L]; + } + + float cdfactor = (clipt * 32768.f) / delta; + maxCD = -9999999.f; + minCD = 9999999.f; + //prepare work for curve gain +#ifdef _OPENMP + #pragma omp parallel for +#endif + + for (int i = 0; i < H_L; i++) { + for (int j = 0; j < W_L; j++) { + luminance[i][j] = luminance[i][j] - mini; + } + } + + mean = 0.f; + stddv = 0.f; + + mean_stddv2(luminance, mean, stddv, W_L, H_L, maxtr, mintr); +// printf("meanun=%f stdun=%f maxtr=%f mintr=%f\n", mean, stddv, maxtr, mintr); + + float asig = 0.f, bsig = 0.f, amax = 0.f, bmax = 0.f, amin = 0.f, bmin = 0.f; + const bool hasRetGainCurve = locRETgainCcurve && mean != 0.f && stddv != 0.f; + + if (hasRetGainCurve) { //if curve + asig = 0.166666f / stddv; + bsig = 0.5f - asig * mean; + amax = 0.333333f / (maxtr - mean - stddv); + bmax = 1.f - amax * maxtr; + amin = 0.333333f / (mean - stddv - mintr); + bmin = -amin * mintr; + + asig *= 500.f; + bsig *= 500.f; + amax *= 500.f; + bmax *= 500.f; + amin *= 500.f; + bmin *= 500.f; + cdfactor *= 2.f; + } + + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + // float absciss; + float cdmax = -999999.f, cdmin = 999999.f; + float gan = 0.5f; + +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + + for (int i = 0; i < H_L; i ++) + for (int j = 0; j < W_L; j++) { + + if (hasRetGainCurve) { + float absciss; + + if (LIKELY(fabsf(luminance[i][j] - mean) < stddv)) { + absciss = asig * luminance[i][j] + bsig; + } else if (luminance[i][j] >= mean) { + absciss = amax * luminance[i][j] + bmax; + } else { + absciss = amin * luminance[i][j] + bmin; + } + + gan = locRETgainCcurve[absciss]; //new gain function transmission + } + + //but we don't update mean stddv for display only... + copylum[i][j] = gan * luminance[i][j];//update data for display + float cd = gan * cdfactor * luminance[i][j] + offse; + + cdmax = cd > cdmax ? cd : cdmax; + cdmin = cd < cdmin ? cd : cdmin; + luminance[i][j] = intp(str * reducDE[i][j], clipretinex(cd, 0.f, maxclip), originalLuminance[i][j]); + } + + + +#ifdef _OPENMP + #pragma omp critical +#endif + { + maxCD = maxCD > cdmax ? maxCD : cdmax; + minCD = minCD < cdmin ? minCD : cdmin; + } + } + mean = 0.f; + stddv = 0.f; + + mean_stddv2(copylum, mean, stddv, W_L, H_L, maxtr, mintr); + delete [] copylumBuffer; + copylumBuffer = nullptr; + +// printf("mean=%f std=%f maxtr=%f mintr=%f\n", mean, stddv, maxtr, mintr); + + } else { +#ifdef _OPENMP + #pragma omp for schedule(dynamic,16) +#endif + + for (int i = 0; i < H_L; i ++) + for (int j = 0; j < W_L; j++) { + luminance[i][j] = LIM(luminance[i][j], 0.f, maxclip) * str + (1.f - str) * originalLuminance[i][j]; + + } + + } + + float rad = loc.spots.at(sp).radmaskreti; + float slop = loc.spots.at(sp).slomaskreti; + float gamm = loc.spots.at(sp).gammaskreti; + float blend = loc.spots.at(sp).blendmaskreti; + float chro = loc.spots.at(sp).chromaskreti; + float lap = loc.spots.at(sp).lapmaskreti; + bool pde = params->locallab.spots.at(sp).laplac; + + if (lum == 1 && (llretiMask == 3 || llretiMask == 0 || llretiMask == 2 || llretiMask == 4)) { //only mask with luminance on last scale + int before = 1; + maskforretinex(sp, before, luminance, nullptr, W_L, H_L, skip, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, llretiMask, retiMasktmap, retiMask, + rad, lap, pde, gamm, slop, chro, blend, + lmaskretilocalcurve, localmaskretiutili, + bufreti, bufmask, buforig, buforigmas, multiThread, + delt, hueref, chromaref, lumaref, + maxdE, mindE, maxdElim, mindElim, iterat, limscope, scope, balance, balanceh, lumask + ); + } + + //mask does not interfered with data displayed + + Tmean = mean; + Tsigma = stddv; + Tmin = mintr; + Tmax = maxtr; + } +} } diff --git a/rtengine/ipshadowshighlights.cc b/rtengine/ipshadowshighlights.cc index 0eb3df9c5..fd2ab3db4 100644 --- a/rtengine/ipshadowshighlights.cc +++ b/rtengine/ipshadowshighlights.cc @@ -38,14 +38,12 @@ void ImProcFunctions::shadowsHighlights(LabImage *lab, bool ena, int labmode, in if (!ena || (!hightli && !shado)){ return; } - const int width = lab->W; const int height = lab->H; const bool lab_mode = labmode; array2D mask(width, height); array2D L(width, height); -// const float radius = params->sh.radius * 10 / scale; const float radius = float(rad) * 10 / scal; LUTf f(lab_mode ? 32768 : 65536); diff --git a/rtengine/ipsharpen.cc b/rtengine/ipsharpen.cc index 0e46cd596..afe6f8aa3 100644 --- a/rtengine/ipsharpen.cc +++ b/rtengine/ipsharpen.cc @@ -244,6 +244,110 @@ BENCHFUN delete blurbuffer; } +void ImProcFunctions::deconvsharpeningloc (float** luminance, float** tmp, int W, int H, float** loctemp, int damp, double radi, int ite, int amo, int contrast, double blurrad, int sk) +{ + // BENCHFUN + + if (amo < 1) { + return; + } + JaggedArray blend(W, H); + float contras = contrast / 100.f; + buildBlendMask(luminance, blend, W, H, contras, 1.f); + + + JaggedArray tmpI(W, H); + + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for (int i = 0; i < H; i++) { + for (int j = 0; j < W; j++) { + tmpI[i][j] = max(luminance[i][j], 0.f); + } + } + + // calculate contrast based blend factors to reduce sharpening in regions with low contrast + + JaggedArray* blurbuffer = nullptr; + + if (blurrad >= 0.25) { + blurbuffer = new JaggedArray(W, H); + JaggedArray &blur = *blurbuffer; +#ifdef _OPENMP + #pragma omp parallel +#endif + { + gaussianBlur(tmpI, blur, W, H, blurrad); +#ifdef _OPENMP + #pragma omp for +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + blur[i][j] = intp(blend[i][j], luminance[i][j], std::max(blur[i][j], 0.0f)); + } + } + } + } + + float damping = (float) damp / 5.0; + bool needdamp = damp > 0; + double sigma = radi / sk; + const float amount = (float) amo / 100.f; + + if (sigma < 0.26f) { + sigma = 0.26f; + } + + int itera = ite; + +#ifdef _OPENMP + #pragma omp parallel +#endif + { + for (int k = 0; k < itera; k++) { + if (!needdamp) { + // apply gaussian blur and divide luminance by result of gaussian blur + // gaussianBlur (tmpI, tmp, W, H, sigma, nullptr, GAUSS_DIV, luminance); + gaussianBlur(tmpI, tmp, W, H, sigma, false, GAUSS_DIV, luminance); + } else { + // apply gaussian blur + damping + gaussianBlur (tmpI, tmp, W, H, sigma); + dcdamping (tmp, luminance, damping, W, H); + } + + gaussianBlur (tmp, tmpI, W, H, sigma, false, GAUSS_MULT); + } // end for + + +#ifdef _OPENMP + #pragma omp for +#endif + + for (int i = 0; i < H; i++) + for (int j = 0; j < W; j++) { + loctemp[i][j] = intp(blend[i][j] * amount, max(tmpI[i][j], 0.0f), luminance[i][j]); + } + + if (blurrad >= 0.25) { + JaggedArray &blur = *blurbuffer; +#ifdef _OPENMP + #pragma omp for +#endif + for (int i = 0; i < H; ++i) { + for (int j = 0; j < W; ++j) { + loctemp[i][j] = intp(blend[i][j], loctemp[i][j], max(blur[i][j], 0.0f)); + } + } + } + + } // end parallel + delete blurbuffer; + + +} + void ImProcFunctions::sharpening (LabImage* lab, const procparams::SharpeningParams &sharpenParam, bool showMask) { diff --git a/rtengine/ipsoftlight.cc b/rtengine/ipsoftlight.cc index 41d0d48bf..7df44702e 100644 --- a/rtengine/ipsoftlight.cc +++ b/rtengine/ipsoftlight.cc @@ -26,7 +26,11 @@ #include "procparams.h" -namespace { +namespace rtengine +{ + +namespace +{ inline float sl(float blend, float x) { @@ -43,8 +47,10 @@ inline float sl(float blend, float x) // using optimized formula (heckflosse67@gmx.de) return rtengine::intp(blend, rtengine::Color::igamma_srgb(v * v * (3.f - 2.f * v) * rtengine::MAXVALF), x); } + return x; } +} // namespace #ifdef __SSE2__ inline vfloat sl(vfloat blend, vfloat x) @@ -54,11 +60,11 @@ inline vfloat sl(vfloat blend, vfloat x) } #endif -} // namespace +//} // namespace -void rtengine::ImProcFunctions::softLight(LabImage *lab) +void ImProcFunctions::softLight(LabImage *lab, const rtengine::procparams::SoftLightParams &softLightParams) { - if (!params->softlight.enabled || !params->softlight.strength) { + if (!softLightParams.enabled || !softLightParams.strength) { return; } @@ -94,30 +100,35 @@ void rtengine::ImProcFunctions::softLight(LabImage *lab) #pragma omp parallel #endif { - const float blend = params->softlight.strength / 100.f; + const float blend = softLightParams.strength / 100.f; #ifdef __SSE2__ const vfloat blendv = F2V(blend); #endif #ifdef _OPENMP #pragma omp for schedule(dynamic,16) #endif + for (int i = 0; i < lab->H; ++i) { int j = 0; #ifdef __SSE2__ + for (; j < lab->W - 3; j += 4) { vfloat Xv, Yv, Zv; vfloat Rv, Gv, Bv; - Color::Lab2XYZ(LVFU(lab->L[i][j]),LVFU (lab->a[i][j]),LVFU (lab->b[i][j]), Xv, Yv, Zv); + Color::Lab2XYZ(LVFU(lab->L[i][j]), LVFU(lab->a[i][j]), LVFU(lab->b[i][j]), Xv, Yv, Zv); Color::xyz2rgb(Xv, Yv, Zv, Rv, Gv, Bv, wipv); Rv = sl(blendv, Rv); Gv = sl(blendv, Gv); Bv = sl(blendv, Bv); Color::rgbxyz(Rv, Gv, Bv, Xv, Yv, Zv, wpv); + for (int k = 0; k < 4; ++k) { - Color::XYZ2Lab(Xv[k], Yv[k], Zv[k], lab->L[i][j + k], lab->a[i][j + k], lab->b[i][j+ k]); + Color::XYZ2Lab(Xv[k], Yv[k], Zv[k], lab->L[i][j + k], lab->a[i][j + k], lab->b[i][j + k]); } } + #endif + for (; j < lab->W; j++) { float X, Y, Z; float R, G, B; @@ -132,3 +143,4 @@ void rtengine::ImProcFunctions::softLight(LabImage *lab) } } } +} \ No newline at end of file diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index b0dc55b2f..f2900fdf1 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -21,6 +21,7 @@ #include "imagefloat.h" #include "improcfun.h" +#include "homogeneouscoordinates.h" #include "procparams.h" #include "rt_math.h" #include "rtengine.h" @@ -334,10 +335,96 @@ namespace rtengine #define CLIPTOC(a,b,c,d) ((a)>=(b)?((a)<=(c)?(a):(d=true,(c))):(d=true,(b))) +/** + * Creates an inverse transformation matrix for camera-geometry-based + * perspective correction. Unless otherwise specified, units are the same as the + * units of the vectors which the matrix will transform. The projection_* + * parameters are applied in the order they appear. + * @param camera_focal_length Camera's focal length. + * @param camera_shift_horiz Camera lens's shift to the right. + * @param camera_shift_vert Camera lens's shift upwards. + * @param camera_roll Camera's roll in radians. Counter-clockwise is positive. + * @param camera_pitch Camera's pitch in radians. Up is positive. + * @param camera_yaw Camera's yaw in radians. Right is positive. + * Up is positive. + * @param projection_shift_horiz Shift of perspective-corrected image to the + * right. + * @param projection_shift_vert Shift of perspective-corrected image upwards. + * @param projection_rotate Rotation of perspective-corrected image + * counter-clockwise in radians. + * @param projection_yaw Yaw in radians of simulated perspective distortion. + * Right is positive. + * @param projection_pitch Pitch in radians of simulated perspective distortion. + * Up is positive. + * @param projection_scale Scale factor of perspective-corrected image. + */ +homogeneous::Matrix perspectiveMatrix(double camera_focal_length, double + camera_shift_horiz, double camera_shift_vert, double camera_roll, double + camera_pitch, double camera_yaw, double projection_yaw, double + projection_pitch, double projection_rotate, double + projection_shift_horiz, double projection_shift_vert, double + projection_scale) +{ + const double projection_scale_inverse = 1.0 / projection_scale; + homogeneous::Vector center; + center[0] = 0; + center[1] = 0; + center[2] = camera_focal_length; + center[3] = 1; + + // Locations of image center after rotations. + const homogeneous::Vector camera_center_yaw_pitch = + homogeneous::rotationMatrix(camera_yaw, homogeneous::Axis::Y) * + homogeneous::rotationMatrix(camera_pitch, homogeneous::Axis::X) * + center; + const homogeneous::Vector projection_center_yaw_pitch = + homogeneous::rotationMatrix(-projection_yaw, homogeneous::Axis::Y) * + homogeneous::rotationMatrix(-projection_pitch, homogeneous::Axis::X) * + center; + + // The following comments refer to the forward transformation. + const homogeneous::Matrix matrix = + // Lens/sensor shift and move to z == camera_focal_length. + homogeneous::translationMatrix(-camera_shift_horiz, + -camera_shift_vert, -camera_focal_length) * + // Camera roll. + homogeneous::rotationMatrix(camera_roll, homogeneous::Axis::Z) * + // Perspective correction. + homogeneous::projectionMatrix(camera_focal_length, homogeneous::Axis::Z) * + homogeneous::rotationMatrix(-camera_pitch, homogeneous::Axis::X) * + homogeneous::rotationMatrix(-camera_yaw, homogeneous::Axis::Y) * + // Re-center after perspective rotation. + homogeneous::translationMatrix(camera_center_yaw_pitch[0], + camera_center_yaw_pitch[1], camera_center_yaw_pitch[2] - camera_focal_length) * + // Translate corrected image. + homogeneous::translationMatrix(-projection_shift_horiz, + -projection_shift_vert, 0) * + // Rotate corrected image. + homogeneous::rotationMatrix(projection_rotate, homogeneous::Axis::Z) * + // Un-center for perspective rotation. + homogeneous::translationMatrix(projection_center_yaw_pitch[0], + projection_center_yaw_pitch[1], camera_focal_length - projection_center_yaw_pitch[2]) * + // Simulate perspective transformation. + homogeneous::projectionMatrix(projection_center_yaw_pitch[2], homogeneous::Axis::Z) * + homogeneous::rotationMatrix(projection_yaw, homogeneous::Axis::Y) * + homogeneous::rotationMatrix(projection_pitch, homogeneous::Axis::X) * + // Move to z == 0. + homogeneous::translationMatrix(0, 0, camera_focal_length) * + // Scale corrected image. + homogeneous::scaleMatrix(projection_scale_inverse, + projection_scale_inverse, 1); + + return matrix; +} + bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef, const LensCorrection *pLCPMap) const { + enum PerspType { NONE, SIMPLE, CAMERA_BASED }; + const PerspType perspectiveType = needsPerspective() ? ( + (params->perspective.method == "camera_based") ? + PerspType::CAMERA_BASED : PerspType::SIMPLE ) : PerspType::NONE; bool clipped = false; red.clear (); @@ -367,19 +454,39 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, double cost = cos (params->rotate.degree * rtengine::RT_PI / 180.0); double sint = sin (params->rotate.degree * rtengine::RT_PI / 180.0); - // auxiliary variables for vertical perspective correction + double ascale = ascaleDef > 0 ? ascaleDef : (params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0); + + // auxiliary variables for perspective correction + // Simple. double vpdeg = params->perspective.vertical / 100.0 * 45.0; double vpalpha = (90.0 - vpdeg) / 180.0 * rtengine::RT_PI; double vpteta = fabs (vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((vpdeg > 0 ? 1.0 : -1.0) * sqrt ((-oW * oW * tan (vpalpha) * tan (vpalpha) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan (vpalpha) * sqrt (16 * maxRadius * maxRadius + oW * oW * tan (vpalpha) * tan (vpalpha))) / (maxRadius * maxRadius * 8))); double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos (vpteta), vptanpt = tan (vpteta); - - // auxiliary variables for horizontal perspective correction double hpdeg = params->perspective.horizontal / 100.0 * 45.0; double hpalpha = (90.0 - hpdeg) / 180.0 * rtengine::RT_PI; double hpteta = fabs (hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos ((hpdeg > 0 ? 1.0 : -1.0) * sqrt ((-oH * oH * tan (hpalpha) * tan (hpalpha) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan (hpalpha) * sqrt (16 * maxRadius * maxRadius + oH * oH * tan (hpalpha) * tan (hpalpha))) / (maxRadius * maxRadius * 8))); double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos (hpteta), hptanpt = tan (hpteta); - - double ascale = ascaleDef > 0 ? ascaleDef : (params->commonTrans.autofill ? getTransformAutoFill (oW, oH, pLCPMap) : 1.0); + // Camera-based. + const double f = + ((params->perspective.camera_focal_length > 0) ? params->perspective.camera_focal_length : 24.0) + * ((params->perspective.camera_crop_factor > 0) ? params->perspective.camera_crop_factor : 1.0) + * (maxRadius / sqrt(18.0*18.0 + 12.0*12.0)); + const double p_camera_yaw = params->perspective.camera_yaw / 180.0 * rtengine::RT_PI; + const double p_camera_pitch = params->perspective.camera_pitch / 180.0 * rtengine::RT_PI; + const double p_camera_roll = params->perspective.camera_roll * rtengine::RT_PI_180; + const double p_camera_shift_horiz = oW / 100.0 * params->perspective.camera_shift_horiz; + const double p_camera_shift_vert = oH / -100.0 * params->perspective.camera_shift_vert; + const double p_projection_shift_horiz = oW / 100.0 * params->perspective.projection_shift_horiz; + const double p_projection_shift_vert = oH / -100.0 * params->perspective.projection_shift_vert; + const double p_projection_rotate = params->perspective.projection_rotate * rtengine::RT_PI_180; + const double p_projection_yaw = -params->perspective.projection_yaw * rtengine::RT_PI_180; + const double p_projection_pitch = -params->perspective.projection_pitch * rtengine::RT_PI_180; + const double p_projection_scale = 1; + const homogeneous::Matrix p_matrix = perspectiveMatrix(f, + p_camera_shift_horiz, p_camera_shift_vert, p_camera_roll, + p_camera_pitch, p_camera_yaw, p_projection_yaw, p_projection_pitch, + p_projection_rotate, p_projection_shift_horiz, + p_projection_shift_vert, p_projection_scale); for (size_t i = 0; i < src.size(); i++) { double x_d = src[i].x, y_d = src[i].y; @@ -394,14 +501,25 @@ bool ImProcFunctions::transCoord (int W, int H, const std::vector &src, x_d += ascale * (0 - w2); // centering x coord & scale y_d += ascale * (0 - h2); // centering y coord & scale - if (needsPerspective()) { - // horizontal perspective transformation - y_d *= maxRadius / (maxRadius + x_d * hptanpt); - x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt); + switch (perspectiveType) { + case PerspType::NONE: + break; + case PerspType::SIMPLE: + // horizontal perspective transformation + y_d *= maxRadius / (maxRadius + x_d * hptanpt); + x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt); - // vertical perspective transformation - x_d *= maxRadius / (maxRadius - y_d * vptanpt); - y_d *= maxRadius * vpcospt / (maxRadius - y_d * vptanpt); + // vertical perspective transformation + x_d *= maxRadius / (maxRadius - y_d * vptanpt); + y_d *= maxRadius * vpcospt / (maxRadius - y_d * vptanpt); + break; + case PerspType::CAMERA_BASED: + const double w = p_matrix[3][0] * x_d + p_matrix[3][1] * y_d + p_matrix[3][3]; + const double xw = p_matrix[0][0] * x_d + p_matrix[0][1] * y_d + p_matrix[0][3]; + const double yw = p_matrix[1][0] * x_d + p_matrix[1][1] * y_d + p_matrix[1][3]; + x_d = xw / w; + y_d = yw / w; + break; } // rotate @@ -699,7 +817,7 @@ static void calcGradientParams (int oW, int oH, const GradientParams& gradient, } } -static float calcGradientFactor (const struct grad_params& gp, int x, int y) +float ImProcFunctions::calcGradientFactor (const struct grad_params& gp, int x, int y) { if (gp.angle_is_zero) { int gy = gp.transpose ? x : y; @@ -978,13 +1096,16 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I { // set up stuff, depending on the mode we are + enum PerspType { NONE, SIMPLE, CAMERA_BASED }; const bool enableLCPDist = pLCPMap && params->lensProf.useDist; const bool enableCA = highQuality && needsCA(); const bool enableGradient = needsGradient(); const bool enablePCVignetting = needsPCVignetting(); const bool enableVignetting = needsVignetting(); - const bool enablePerspective = needsPerspective(); const bool enableDistortion = needsDistortion(); + const PerspType perspectiveType = needsPerspective() ? ( + (params->perspective.method == "camera_based") ? + PerspType::CAMERA_BASED : PerspType::SIMPLE ) : PerspType::NONE; const double w2 = static_cast(oW) / 2.0 - 0.5; const double h2 = static_cast(oH) / 2.0 - 0.5; @@ -1028,21 +1149,41 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I const double cost = cos(params->rotate.degree * rtengine::RT_PI / 180.0); const double sint = sin(params->rotate.degree * rtengine::RT_PI / 180.0); - // auxiliary variables for vertical perspective correction + // auxiliary variables for perspective correction + // Simple. const double vpdeg = params->perspective.vertical / 100.0 * 45.0; const double vpalpha = (90.0 - vpdeg) / 180.0 * rtengine::RT_PI; const double vpteta = fabs(vpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos((vpdeg > 0 ? 1.0 : -1.0) * sqrt((-SQR(oW * tan(vpalpha)) + (vpdeg > 0 ? 1.0 : -1.0) * oW * tan(vpalpha) * sqrt(SQR(4 * maxRadius) + SQR(oW * tan(vpalpha)))) / (SQR(maxRadius) * 8))); const double vpcospt = (vpdeg >= 0 ? 1.0 : -1.0) * cos(vpteta); const double vptanpt = tan(vpteta); - - // auxiliary variables for horizontal perspective correction const double hpdeg = params->perspective.horizontal / 100.0 * 45.0; const double hpalpha = (90.0 - hpdeg) / 180.0 * rtengine::RT_PI; const double hpteta = fabs(hpalpha - rtengine::RT_PI / 2) < 3e-4 ? 0.0 : acos((hpdeg > 0 ? 1.0 : -1.0) * sqrt((-SQR(oH * tan(hpalpha)) + (hpdeg > 0 ? 1.0 : -1.0) * oH * tan(hpalpha) * sqrt(SQR(4 * maxRadius) + SQR(oH * tan(hpalpha)))) / (SQR(maxRadius) * 8))); const double hpcospt = (hpdeg >= 0 ? 1.0 : -1.0) * cos(hpteta); const double hptanpt = tan(hpteta); + // Camera-based. + const double f = + ((params->perspective.camera_focal_length > 0) ? params->perspective.camera_focal_length : 24.0) + * ((params->perspective.camera_crop_factor > 0) ? params->perspective.camera_crop_factor : 1.0) + * (maxRadius / sqrt(18.0*18.0 + 12.0*12.0)); + const double p_camera_yaw = params->perspective.camera_yaw / 180.0 * rtengine::RT_PI; + const double p_camera_pitch = params->perspective.camera_pitch / 180.0 * rtengine::RT_PI; + const double p_camera_roll = params->perspective.camera_roll * rtengine::RT_PI_180; + const double p_camera_shift_horiz = oW / 100.0 * params->perspective.camera_shift_horiz; + const double p_camera_shift_vert = oH / -100.0 * params->perspective.camera_shift_vert; + const double p_projection_shift_horiz = oW / 100.0 * params->perspective.projection_shift_horiz; + const double p_projection_shift_vert = oH / -100.0 * params->perspective.projection_shift_vert; + const double p_projection_rotate = params->perspective.projection_rotate * rtengine::RT_PI_180; + const double p_projection_yaw = -params->perspective.projection_yaw * rtengine::RT_PI_180; + const double p_projection_pitch = -params->perspective.projection_pitch * rtengine::RT_PI_180; + const double p_projection_scale = 1; + const homogeneous::Matrix p_matrix = perspectiveMatrix(f, + p_camera_shift_horiz, p_camera_shift_vert, p_camera_roll, + p_camera_pitch, p_camera_yaw, p_projection_yaw, p_projection_pitch, + p_projection_rotate, p_projection_shift_horiz, + p_projection_shift_vert, p_projection_scale); const double ascale = params->commonTrans.autofill ? getTransformAutoFill(oW, oH, pLCPMap) : 1.0; @@ -1088,14 +1229,25 @@ void ImProcFunctions::transformGeneral(bool highQuality, Imagefloat *original, I x_d += ascale * centerFactorx; // centering x coord & scale y_d += ascale * centerFactory; // centering y coord & scale - if (enablePerspective) { - // horizontal perspective transformation - y_d *= maxRadius / (maxRadius + x_d * hptanpt); - x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt); + switch (perspectiveType) { + case PerspType::NONE: + break; + case PerspType::SIMPLE: + // horizontal perspective transformation + y_d *= maxRadius / (maxRadius + x_d * hptanpt); + x_d *= maxRadius * hpcospt / (maxRadius + x_d * hptanpt); - // vertical perspective transformation - x_d *= maxRadius / (maxRadius - y_d * vptanpt); - y_d *= maxRadius * vpcospt / (maxRadius - y_d * vptanpt); + // vertical perspective transformation + x_d *= maxRadius / (maxRadius - y_d * vptanpt); + y_d *= maxRadius * vpcospt / (maxRadius - y_d * vptanpt); + break; + case PerspType::CAMERA_BASED: + const double w = p_matrix[3][0] * x_d + p_matrix[3][1] * y_d + p_matrix[3][3]; + const double xw = p_matrix[0][0] * x_d + p_matrix[0][1] * y_d + p_matrix[0][3]; + const double yw = p_matrix[1][0] * x_d + p_matrix[1][1] * y_d + p_matrix[1][3]; + x_d = xw / w; + y_d = yw / w; + break; } // rotate @@ -1323,7 +1475,19 @@ bool ImProcFunctions::needsRotation () const bool ImProcFunctions::needsPerspective () const { - return params->perspective.horizontal || params->perspective.vertical; + return ( (params->perspective.method == "simple") && + (params->perspective.horizontal || params->perspective.vertical) ) + || ( (params->perspective.method == "camera_based") && ( + params->perspective.camera_pitch || + params->perspective.camera_roll || + params->perspective.camera_shift_horiz || + params->perspective.camera_shift_vert || + params->perspective.camera_yaw || + params->perspective.projection_pitch || + params->perspective.projection_rotate || + params->perspective.projection_shift_horiz || + params->perspective.projection_shift_vert || + params->perspective.projection_yaw) ); } bool ImProcFunctions::needsGradient () const diff --git a/rtengine/ipvibrance.cc b/rtengine/ipvibrance.cc index 8636ecb95..43002c2a3 100644 --- a/rtengine/ipvibrance.cc +++ b/rtengine/ipvibrance.cc @@ -61,16 +61,16 @@ void fillCurveArrayVib (DiagonalCurve* diagCurve, LUTf &outCurve) * copyright (c)2011 Jacques Desmis and Jean-Christophe Frisch * */ -void ImProcFunctions::vibrance (LabImage* lab) +void ImProcFunctions::vibrance (LabImage* lab, const procparams::VibranceParams &vibranceParams, bool highlight, const Glib::ustring &workingProfile) { - if (!params->vibrance.enabled) { + if (!vibranceParams.enabled) { return; } BENCHFUN // int skip=1; //scale==1 ? 1 : 16; bool skinCurveIsSet = false; - DiagonalCurve* dcurve = new DiagonalCurve (params->vibrance.skintonescurve, CURVES_MIN_POLY_POINTS); + DiagonalCurve* dcurve = new DiagonalCurve (vibranceParams.skintonescurve, CURVES_MIN_POLY_POINTS); if (dcurve) { if (!dcurve->isIdentity()) { @@ -81,7 +81,7 @@ void ImProcFunctions::vibrance (LabImage* lab) } } - if (!skinCurveIsSet && !params->vibrance.pastels && !params->vibrance.saturated) { + if (!skinCurveIsSet && !vibranceParams.pastels && !vibranceParams.saturated) { if (dcurve) { delete dcurve; dcurve = nullptr; @@ -96,10 +96,10 @@ void ImProcFunctions::vibrance (LabImage* lab) // skin hue curve // I use diagonal because I think it's better - const float chromaPastel = params->vibrance.pastels / 100.f; - const float chromaSatur = params->vibrance.saturated / 100.f; + const float chromaPastel = vibranceParams.pastels / 100.f; + const float chromaSatur = vibranceParams.saturated / 100.f; constexpr float p00 = 0.07f; - const float limitpastelsatur = (static_cast(params->vibrance.psthreshold.getTopLeft()) / 100.f) * (1.f - p00) + p00; + const float limitpastelsatur = (static_cast(vibranceParams.psthreshold.getTopLeft()) / 100.f) * (1.f - p00) + p00; const float maxdp = (limitpastelsatur - p00) / 4.f; const float maxds = (1.f - limitpastelsatur) / 4.f; const float p0 = p00 + maxdp; @@ -108,7 +108,7 @@ void ImProcFunctions::vibrance (LabImage* lab) const float s0 = limitpastelsatur + maxds; const float s1 = limitpastelsatur + 2.f * maxds; const float s2 = limitpastelsatur + 3.f * maxds; - const float transitionweighting = static_cast(params->vibrance.psthreshold.getBottomLeft()) / 100.f; + const float transitionweighting = static_cast(vibranceParams.psthreshold.getBottomLeft()) / 100.f; float chromamean = 0.f; if (chromaPastel != chromaSatur) { @@ -144,6 +144,7 @@ void ImProcFunctions::vibrance (LabImage* lab) if (skinCurveIsSet) { fillCurveArrayVib (dcurve, skin_curve); skin_curve /= ask; +// skin_curve *= 2.f; } if (dcurve) { @@ -151,12 +152,10 @@ void ImProcFunctions::vibrance (LabImage* lab) dcurve = nullptr; } + const bool protectskins = vibranceParams.protectskins; + const bool avoidcolorshift = vibranceParams.avoidcolorshift; - const bool highlight = params->toneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated - const bool protectskins = params->vibrance.protectskins; - const bool avoidcolorshift = params->vibrance.avoidcolorshift; - - TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (params->icm.workingProfile); + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix (workingProfile); //inverse matrix user select const float wip[3][3] = { {static_cast(wiprof[0][0]), static_cast(wiprof[0][1]), static_cast(wiprof[0][2])}, diff --git a/rtengine/ipwavelet.cc b/rtengine/ipwavelet.cc index 47bd0f08a..949dbafbd 100644 --- a/rtengine/ipwavelet.cc +++ b/rtengine/ipwavelet.cc @@ -110,7 +110,6 @@ struct cont_params { bool lip3; bool tonemap; bool diag; - int TMmeth; float tmstrength; float balan; float sigmafin; @@ -168,10 +167,6 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const { -#ifdef _DEBUG - // init variables to display Munsell corrections - MunsellDebugInfo* MunsDebugInfo = new MunsellDebugInfo(); -#endif TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(params->icm.workingProfile); const double wip[3][3] = { {wiprof[0][0], wiprof[0][1], wiprof[0][2]}, @@ -462,16 +457,14 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const cp.t_ly = static_cast(params->wavelet.hueskin2.getTopLeft()) / 100.0f; cp.b_ry = static_cast(params->wavelet.hueskin2.getBottomRight()) / 100.0f; cp.t_ry = static_cast(params->wavelet.hueskin2.getTopRight()) / 100.0f; - cp.numlevH = params->wavelet.threshold; + cp.numlevH = params->wavelet.threshold -1; //shadows cp.b_lsl = static_cast(params->wavelet.bllev.getBottomLeft()); cp.t_lsl = static_cast(params->wavelet.bllev.getTopLeft()); cp.b_rsl = static_cast(params->wavelet.bllev.getBottomRight()); cp.t_rsl = static_cast(params->wavelet.bllev.getTopRight()); - cp.numlevS = 9 - params->wavelet.threshold2; - int maxlevS = cp.numlevH; - cp.numlevS = rtengine::max(cp.numlevS, maxlevS); + cp.numlevS = params->wavelet.threshold2; //rtengine::max(cp.numlevS, maxlevS); //highlight cp.b_lhl = static_cast(params->wavelet.hllev.getBottomLeft()); cp.t_lhl = static_cast(params->wavelet.hllev.getTopLeft()); @@ -868,11 +861,11 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const if (exblurL) { if (cp.mul[0] == 0.f) { - cp.mul[0] = 0.01f;//to always enable WaveletcontAllL if no contrast is nead + cp.mul[0] = 0.01f;//to always enable WaveletcontAllL if no contrast is needed } } - if (!exblurL && cp.contrast == 0.f && cp.blurres == 0.f && !cp.tonemap && cp.conres == 0.f && cp.conresH == 0.f && cp.val == 0 && !ref0 && params->wavelet.CLmethod == "all") { // no processing of residual L or edge=> we probably can reduce the number of levels + if (!exblurL && cp.contrast == 0.f && cp.blurres == 0.f && !cp.tonemap && cp.conres == 0.f && cp.conresH == 0.f && cp.val == 0 && !ref0 && params->wavelet.CLmethod == "all") { // no processing of residual L or edge=> we probably can reduce the number of levels while (levwavL > 0 && cp.mul[levwavL - 1] == 0.f) { // cp.mul[level] == 0.f means no changes to level levwavL--; } @@ -897,7 +890,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const if (levwavL > 0) { const std::unique_ptr Ldecomp(new wavelet_decomposition(labco->data, labco->W, labco->H, levwavL, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen)); - if (!Ldecomp->memoryAllocationFailed) { + if (!Ldecomp->memory_allocation_failed()) { float madL[10][3]; // float madL[8][3]; @@ -910,7 +903,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const int Wlvl_L = Ldecomp->level_W(lvl); int Hlvl_L = Ldecomp->level_H(lvl); - float ** WavCoeffs_L = Ldecomp->level_coeffs(lvl); + const float* const* WavCoeffs_L = Ldecomp->level_coeffs(lvl); madL[lvl][dir - 1] = SQR(Mad(WavCoeffs_L[dir], Wlvl_L * Hlvl_L)); @@ -935,7 +928,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const } if (cp.val > 0 || ref || contr) { //edge - Evaluate2(*Ldecomp, mean, meanN, sigma, sigmaN, MaxP, MaxN); + Evaluate2(*Ldecomp, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavNestedLevels); } //init for edge and denoise @@ -1008,7 +1001,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const WaveletDenoiseAllL(*Ldecomp, noisevarlum, madL, vari, edge, 1); } - } + } //Flat curve for Contrast=f(H) in levels FlatCurve* ChCurve = new FlatCurve(params->wavelet.Chcurve); //curve C=f(H) @@ -1026,7 +1019,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const WaveletcontAllL(labco, varhue, varchro, *Ldecomp, wavblcurve, cp, skip, mean, sigma, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, waOpacityCurveSH, ChCurve, Chutili); if (cp.val > 0 || ref || contr || cp.diagcurv) { //edge - Evaluate2(*Ldecomp, mean, meanN, sigma, sigmaN, MaxP, MaxN); + Evaluate2(*Ldecomp, mean, meanN, sigma, sigmaN, MaxP, MaxN, wavNestedLevels); } WaveletcontAllLfinal(*Ldecomp, cp, mean, sigma, MaxP, waOpacityCurveWL); @@ -1291,7 +1284,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const if (levwava > 0) { const std::unique_ptr adecomp(new wavelet_decomposition(labco->data + datalen, labco->W, labco->H, levwava, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen)); - if (!adecomp->memoryAllocationFailed) { + if (!adecomp->memory_allocation_failed()) { if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.chromco < 2.f )) { WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1); } else if (cp.chromfi > 0.f && cp.chromco >= 2.f){ @@ -1300,7 +1293,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1); } - Evaluate2(*adecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab); + Evaluate2(*adecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels); WaveletcontAllAB(labco, varhue, varchro, *adecomp, wavblcurve, waOpacityCurveW, cp, true, skip, meanab, sigmaab); adecomp->reconstruct(labco->data + datalen, cp.strength); } @@ -1328,7 +1321,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const if (levwavb > 0) { const std::unique_ptr bdecomp(new wavelet_decomposition(labco->data + 2 * datalen, labco->W, labco->H, levwavb, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen)); - if (!bdecomp->memoryAllocationFailed) { + if (!bdecomp->memory_allocation_failed()) { if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.chromco < 2.f )) { WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1); } else if (cp.chromfi > 0.f && cp.chromco >= 2.f){ @@ -1336,7 +1329,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1); } - Evaluate2(*bdecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab); + Evaluate2(*bdecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels); WaveletcontAllAB(labco, varhue, varchro, *bdecomp, wavblcurve, waOpacityCurveW, cp, false, skip, meanab, sigmaab); bdecomp->reconstruct(labco->data + 2 * datalen, cp.strength); } @@ -1344,12 +1337,6 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const } else {// a and b int levwavab = levwav; - if (!exblurab && cp.chrores == 0.f && cp.blurcres == 0.f && !hhutili && params->wavelet.CLmethod == "all") { // no processing of residual ab => we probably can reduce the number of levels - while (levwavab > 0 && (((cp.CHmet == 2 && (cp.chro == 0.f || cp.mul[levwavab - 1] == 0.f)) || (cp.CHmet != 2 && (levwavab == 10 || (!cp.curv || cp.mulC[levwavab - 1] == 0.f))))) && (!cp.opaRG || levwavab == 10 || (cp.opaRG && cp.mulopaRG[levwavab - 1] == 0.f)) && ((levwavab == 10 || (cp.CHSLmet == 1 && cp.mulC[levwavab - 1] == 0.f)))) { - levwavab--; - } - } - if (cp.chromfi > 0.f || cp.chromco > 0.f) { if (levwavab < 7) { levwavab = 7; @@ -1360,7 +1347,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const const std::unique_ptr adecomp(new wavelet_decomposition(labco->data + datalen, labco->W, labco->H, levwavab, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen)); const std::unique_ptr bdecomp(new wavelet_decomposition(labco->data + 2 * datalen, labco->W, labco->H, levwavab, 1, skip, rtengine::max(1, wavNestedLevels), DaubLen)); - if (!adecomp->memoryAllocationFailed && !bdecomp->memoryAllocationFailed) { + if (!adecomp->memory_allocation_failed() && !bdecomp->memory_allocation_failed()) { if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.chromco < 2.f)) { WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1); } else if (cp.chromfi > 0.f && cp.chromco >= 2.f){ @@ -1368,7 +1355,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const WaveletDenoiseAllAB(*Ldecomp, *adecomp, noisevarchrom, madL, variC, edge, noisevarab_r, true, false, false, 1); } - Evaluate2(*adecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab); + Evaluate2(*adecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels); WaveletcontAllAB(labco, varhue, varchro, *adecomp, wavblcurve, waOpacityCurveW, cp, true, skip, meanab, sigmaab); if (cp.noiseena && ((cp.chromfi > 0.f || cp.chromco > 0.f) && cp.chromco < 2.f)) { WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1); @@ -1377,7 +1364,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const WaveletDenoiseAllAB(*Ldecomp, *bdecomp, noisevarchrom, madL, variCb, edge, noisevarab_r, true, false, false, 1); } - Evaluate2(*bdecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab); + Evaluate2(*bdecomp, meanab, meanNab, sigmaab, sigmaNab, MaxPab, MaxNab, wavNestedLevels); WaveletcontAllAB(labco, varhue, varchro, *bdecomp, wavblcurve, waOpacityCurveW, cp, false, skip, meanab, sigmaab); WaveletAandBAllAB(*adecomp, *bdecomp, cp, hhCurve, hhutili); @@ -1519,13 +1506,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const float Lprov2 = Lold[i][j] / 327.68f; float memChprov = varchro[i1][j1]; float R, G, B; -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; - Color::gamutLchonly(HH, sincosv, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else Color::gamutLchonly(HH, sincosv, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f); -#endif L = Lprov1 * 327.68f; a = 327.68f * Chprov1 * sincosv.y; //gamut @@ -1534,11 +1515,7 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const float correctlum = 0.0f; Lprov1 = L / 327.68f; const float Chprov = sqrtf(SQR(a) + SQR(b)) / 327.68f; -#ifdef _DEBUG - Color::AllMunsellLch(true, Lprov1, Lprov2, HH, Chprov, memChprov, correctionHue, correctlum, MunsDebugInfo); -#else Color::AllMunsellLch(true, Lprov1, Lprov2, HH, Chprov, memChprov, correctionHue, correctlum); -#endif if (correctionHue != 0.f || correctlum != 0.f) { // only calculate sin and cos if HH changed if (std::fabs(correctionHue) < 0.015f) { @@ -1647,15 +1624,10 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const } } } - -#ifdef _DEBUG - delete MunsDebugInfo; -#endif - } -void ImProcFunctions::Aver(float * RESTRICT DataList, int datalen, float &averagePlus, float &averageNeg, float &max, float &min) +void ImProcFunctions::Aver(const float* RESTRICT DataList, int datalen, float &averagePlus, float &averageNeg, float &max, float &min, int numThreads) { //find absolute mean @@ -1666,7 +1638,7 @@ void ImProcFunctions::Aver(float * RESTRICT DataList, int datalen, float &averag max = 0.f; min = RT_INFINITY_F; #ifdef _OPENMP - #pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1) + #pragma omp parallel num_threads(numThreads) if (numThreads>1) #endif { float lmax = 0.f, lmin = 0.f; @@ -1710,14 +1682,14 @@ void ImProcFunctions::Aver(float * RESTRICT DataList, int datalen, float &averag } -void ImProcFunctions::Sigma(float * RESTRICT DataList, int datalen, float averagePlus, float averageNeg, float &sigmaPlus, float &sigmaNeg) +void ImProcFunctions::Sigma(const float* RESTRICT DataList, int datalen, float averagePlus, float averageNeg, float &sigmaPlus, float &sigmaNeg, int numThreads) { int countP = 0, countN = 0; double variP = 0.0, variN = 0.0; // use double precision for large summations float thres = 32.7f;//different fom zero to take into account only data large enough 32.7 = 0.1 in range 0..100 #ifdef _OPENMP - #pragma omp parallel for reduction(+:variP,variN,countP,countN) num_threads(wavNestedLevels) if (wavNestedLevels>1) + #pragma omp parallel for reduction(+:variP,variN,countP,countN) num_threads(numThreads) if (numThreads>1) #endif for (int i = 0; i < datalen; i++) { @@ -1744,8 +1716,7 @@ void ImProcFunctions::Sigma(float * RESTRICT DataList, int datalen, float avera } -void ImProcFunctions::Evaluate2(const wavelet_decomposition &WaveletCoeffs_L, - float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN) +void ImProcFunctions::Evaluate2(const wavelet_decomposition &WaveletCoeffs_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, int numThreads) { //StopWatch Stop1("Evaluate2"); int maxlvl = WaveletCoeffs_L.maxlevel(); @@ -1755,9 +1726,9 @@ void ImProcFunctions::Evaluate2(const wavelet_decomposition &WaveletCoeffs_L, int Wlvl_L = WaveletCoeffs_L.level_W(lvl); int Hlvl_L = WaveletCoeffs_L.level_H(lvl); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); + const float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); - Eval2(WavCoeffs_L, lvl, Wlvl_L, Hlvl_L, mean, meanN, sigma, sigmaN, MaxP, MaxN); + Eval2(WavCoeffs_L, lvl, Wlvl_L, Hlvl_L, mean, meanN, sigma, sigmaN, MaxP, MaxN, numThreads); } } @@ -1814,8 +1785,7 @@ void ImProcFunctions::calceffect(int level, float *mean, float *sigma, float *me mea[9] = offs * mean[level] + effect * 2.5f * sigma[level]; //99% } -void ImProcFunctions::Eval2(float ** WavCoeffs_L, int level, - int W_L, int H_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN) +void ImProcFunctions::Eval2(const float* const* WavCoeffs_L, int level, int W_L, int H_L, float *mean, float *meanN, float *sigma, float *sigmaN, float *MaxP, float *MaxN, int numThreads) { float avLP[4], avLN[4]; @@ -1824,8 +1794,8 @@ void ImProcFunctions::Eval2(float ** WavCoeffs_L, int level, float AvL, AvN, SL, SN, maxLP, maxLN; for (int dir = 1; dir < 4; dir++) { - Aver(WavCoeffs_L[dir], W_L * H_L, avLP[dir], avLN[dir], maxL[dir], minL[dir]); - Sigma(WavCoeffs_L[dir], W_L * H_L, avLP[dir], avLN[dir], sigP[dir], sigN[dir]); + Aver(WavCoeffs_L[dir], W_L * H_L, avLP[dir], avLN[dir], maxL[dir], minL[dir], numThreads); + Sigma(WavCoeffs_L[dir], W_L * H_L, avLP[dir], avLN[dir], sigP[dir], sigN[dir], numThreads); } AvL = 0.f; @@ -1916,219 +1886,128 @@ void ImProcFunctions::CompressDR(float *Source, int W_L, int H_L, float Compress } -void ImProcFunctions::ContrastResid(float * WavCoeffs_L0, struct cont_params &cp, int W_L, int H_L, float max0, float min0) +void ImProcFunctions::ContrastResid(float * WavCoeffs_L0, const cont_params &cp, int W_L, int H_L, float max0) { - float stren = cp.tmstrength; - float gamm = params->wavelet.gamma; - cp.TMmeth = 2; //default after testing - - if (cp.TMmeth == 1) { - min0 = 0.0f; - max0 = 32768.f; - } else if (cp.TMmeth == 2) { - min0 = 0.0f; - } + const float stren = cp.tmstrength; + const float gamm = params->wavelet.gamma; #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < W_L * H_L; i++) { - WavCoeffs_L0[i] = (WavCoeffs_L0[i] - min0) / max0; - WavCoeffs_L0[i] *= gamm; - } - - float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. - float DetailBoost = stren; - - if (stren < 0.0f) { - DetailBoost = 0.0f; //Go with effect of exponent only if uncompressing. + WavCoeffs_L0[i] *= (gamm / max0); } + const float Compression = std::exp(-stren); //This modification turns numbers symmetric around 0 into exponents. + const float DetailBoost = std::max(stren, 0.f); //Go with effect of exponent only if uncompressing. CompressDR(WavCoeffs_L0, W_L, H_L, Compression, DetailBoost); - + max0 /= gamm; #ifdef _OPENMP #pragma omp parallel for // removed schedule(dynamic,10) #endif for (int ii = 0; ii < W_L * H_L; ii++) { - WavCoeffs_L0[ii] = WavCoeffs_L0[ii] * max0 * (1.f / gamm) + min0; + WavCoeffs_L0[ii] *= max0; } } - - - -void ImProcFunctions::EPDToneMapResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, struct cont_params& cp, int W_L, int H_L, float max0, float min0) +void ImProcFunctions::EPDToneMapResid(float * WavCoeffs_L0, unsigned int Iterates, int skip, const cont_params& cp, int W_L, int H_L, float max0) { - float stren = cp.tmstrength; - float edgest = params->wavelet.edgs; - float sca = params->wavelet.scale; - float gamm = params->wavelet.gamma; - int rew = 0; //params->epd.reweightingIterates; + const float stren = cp.tmstrength; + const float edgest = params->wavelet.edgs; + const float sca = params->wavelet.scale; + const float gamm = params->wavelet.gamma; + constexpr int rew = 0; //params->epd.reweightingIterates; + EdgePreservingDecomposition epd2(W_L, H_L); - cp.TMmeth = 2; //default after testing - if (cp.TMmeth == 1) { - min0 = 0.0f; - max0 = 32768.f; - } else if (cp.TMmeth == 2) { - min0 = 0.0f; - } - - // max0=32768.f; #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < W_L * H_L; i++) { - WavCoeffs_L0[i] = (WavCoeffs_L0[i] - min0) / max0; - WavCoeffs_L0[i] *= gamm; + WavCoeffs_L0[i] *= (gamm / max0); } - float Compression = expf(-stren); //This modification turns numbers symmetric around 0 into exponents. - float DetailBoost = stren; - - if (stren < 0.0f) { - DetailBoost = 0.0f; //Go with effect of exponent only if uncompressing. - } + const float Compression = std::exp(-stren); //This modification turns numbers symmetric around 0 into exponents. + const float DetailBoost = std::max(stren, 0.f); //Go with effect of exponent only if uncompressing. //Auto select number of iterates. Note that p->EdgeStopping = 0 makes a Gaussian blur. if (Iterates == 0) { Iterates = (unsigned int)(edgest * 15.0f); } + epd2.CompressDynamicRange(WavCoeffs_L0, sca / skip, edgest, Compression, DetailBoost, Iterates, rew); - epd2.CompressDynamicRange(WavCoeffs_L0, (float)sca / skip, edgest, Compression, DetailBoost, Iterates, rew); - + max0 /= gamm; //Restore past range, also desaturate a bit per Mantiuk's Color correction for tone mapping. #ifdef _OPENMP #pragma omp parallel for // removed schedule(dynamic,10) #endif for (int ii = 0; ii < W_L * H_L; ii++) { - WavCoeffs_L0[ii] = WavCoeffs_L0[ii] * max0 * (1.f / gamm) + min0; + WavCoeffs_L0[ii] *= max0; } } -void ImProcFunctions::WaveletcontAllLfinal(const wavelet_decomposition &WaveletCoeffs_L, const cont_params &cp, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL) +void ImProcFunctions::WaveletcontAllLfinal(wavelet_decomposition& WaveletCoeffs_L, const cont_params &cp, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL) { int maxlvl = WaveletCoeffs_L.maxlevel(); - float * WavCoeffs_L0 = WaveletCoeffs_L.coeff0; + float* WavCoeffs_L0 = WaveletCoeffs_L.get_coeff0(); for (int dir = 1; dir < 4; dir++) { for (int lvl = 0; lvl < maxlvl; lvl++) { int Wlvl_L = WaveletCoeffs_L.level_W(lvl); int Hlvl_L = WaveletCoeffs_L.level_H(lvl); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); + float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); finalContAllL(WavCoeffs_L, WavCoeffs_L0, lvl, dir, cp, Wlvl_L, Hlvl_L, mean, sigma, MaxP, waOpacityCurveWL); } } } -void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float **varchrom, const wavelet_decomposition &WaveletCoeffs_L, const Wavblcurve & wavblcurve, +void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float **varchrom, wavelet_decomposition& WaveletCoeffs_L, const Wavblcurve & wavblcurve, struct cont_params &cp, int skip, float *mean, float *sigma, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveSH & waOpacityCurveSH, FlatCurve* ChCurve, bool Chutili) { const int maxlvl = WaveletCoeffs_L.maxlevel(); const int W_L = WaveletCoeffs_L.level_W(0); const int H_L = WaveletCoeffs_L.level_H(0); - float * WavCoeffs_L0 = WaveletCoeffs_L.coeff0; + float* WavCoeffs_L0 = WaveletCoeffs_L.get_coeff0(); - float contrast = cp.contrast; + const float contrast = cp.contrast; double avedbl = 0.0; // use double precision for large summations float max0 = 0.f; - float min0 = FLT_MAX; - if (contrast != 0.f || (cp.tonemap && cp.resena)) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step + if (contrast != 0.f || (cp.tonemap && cp.resena)) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step #ifdef _OPENMP - #pragma omp parallel for reduction(+:avedbl) num_threads(wavNestedLevels) if (wavNestedLevels>1) + #pragma omp parallel for reduction(+:avedbl) reduction(max:max0) num_threads(wavNestedLevels) if (wavNestedLevels>1) #endif for (int i = 0; i < W_L * H_L; i++) { avedbl += static_cast(WavCoeffs_L0[i]); + max0 = std::max(WavCoeffs_L0[i], max0); } - -#ifdef _OPENMP - #pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1) -#endif - { - float lminL = FLT_MAX; - float lmaxL = 0.f; - -#ifdef _OPENMP - #pragma omp for -#endif - - for (int i = 0; i < W_L * H_L; i++) { - if (WavCoeffs_L0[i] < lminL) { - lminL = WavCoeffs_L0[i]; - } - - if (WavCoeffs_L0[i] > lmaxL) { - lmaxL = WavCoeffs_L0[i]; - } - - } - -#ifdef _OPENMP - #pragma omp critical -#endif - { - if (lminL < min0) { - min0 = lminL; - } - - if (lmaxL > max0) { - max0 = lmaxL; - } - } - - } - } - //tone mapping - if (cp.tonemap && cp.contmet == 2 && cp.resena) { + if (cp.tonemap && cp.contmet == 2 && cp.resena) { //iterate = 5 - EPDToneMapResid(WavCoeffs_L0, 0, skip, cp, W_L, H_L, max0, min0); - + EPDToneMapResid(WavCoeffs_L0, 0, skip, cp, W_L, H_L, max0); } //end tonemapping max0 /= 327.68f; - min0 /= 327.68f; - float ave = avedbl / (double)(W_L * H_L); - float avg = ave / 32768.f; - float *koeLi[12]; - float maxkoeLi[12]; + const float ave = avedbl / (W_L * H_L); + const float avg = LIM01(ave / 32768.f); - float *koeLibuffer = nullptr; - - for (int y = 0; y < 12; y++) { - maxkoeLi[y] = 0.f; //9 - } - - koeLibuffer = new float[12 * H_L * W_L]; //12 - - for (int i = 0; i < 12; i++) { //9 - koeLi[i] = &koeLibuffer[i * W_L * H_L]; - } - - for (int j = 0; j < 12; j++) //9 - for (int i = 0; i < W_L * H_L; i++) { - koeLi[j][i] = 0.f; - } - - avg = LIM01(avg); - double contreal = 0.6 * contrast; + const double contreal = 0.6 * contrast; DiagonalCurve resid_contrast({ DCT_NURBS, 0, 0, @@ -2137,43 +2016,29 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * 1, 1 }); + if (contrast != 0.f && cp.resena && max0 > 0.f) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step #ifdef _OPENMP - #pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1) -#endif - { - if (contrast != 0.f && cp.resena && max0 > 0.f) { // contrast = 0.f means that all will be multiplied by 1.f, so we can skip this step - { - -#ifdef _OPENMP - #pragma omp for + #pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1) #endif - for (int i = 0; i < W_L * H_L; i++) { - float buf = LIM01(WavCoeffs_L0[i] / 32768.f); - buf = resid_contrast.getVal(buf); - buf *= 32768.f; - WavCoeffs_L0[i] = buf; - } - } - } - - - if (cp.tonemap && cp.contmet == 1 && cp.resena) { - float maxp = max0 * 256.f; - float minp = min0 * 256.f; -#ifdef _OPENMP - #pragma omp single -#endif - ContrastResid(WavCoeffs_L0, cp, W_L, H_L, maxp, minp); + for (int i = 0; i < W_L * H_L; i++) { + float buf = LIM01(WavCoeffs_L0[i] / 32768.f); + buf = resid_contrast.getVal(buf); + buf *= 32768.f; + WavCoeffs_L0[i] = buf; } } - if ((cp.conres >= 0.f || cp.conresH >= 0.f) && cp.resena && !cp.oldsh) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step - LabImage *temp = nullptr; - temp = new LabImage(W_L, H_L); + if (cp.tonemap && cp.contmet == 1 && cp.resena) { + const float maxp = max0 * 256.f; + ContrastResid(WavCoeffs_L0, cp, W_L, H_L, maxp); + } + + if ((cp.conres >= 0.f || cp.conresH >= 0.f) && cp.resena && !cp.oldsh) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step + const std::unique_ptr temp(new LabImage(W_L, H_L)); #ifdef _OPENMP - #pragma omp for + #pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1) #endif for (int i = 0; i < H_L; i++) { @@ -2182,12 +2047,10 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * } } - { - ImProcFunctions::shadowsHighlights(temp, true, 1, cp.conresH, cp.conres, cp.radius, skip, cp.thH, cp.th); - } + ImProcFunctions::shadowsHighlights(temp.get(), true, 1, cp.conresH, cp.conres, cp.radius, skip, cp.thH, cp.th); #ifdef _OPENMP - #pragma omp for + #pragma omp parallel for num_threads(wavNestedLevels) if (wavNestedLevels>1) #endif for (int i = 0; i < H_L; i++) { @@ -2195,18 +2058,11 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * WavCoeffs_L0[i * W_L + j] = temp->L[i][j]; } } - - delete temp; - } -#ifdef _OPENMP - #pragma omp barrier -#endif - if ((cp.conres != 0.f || cp.conresH != 0.f) && cp.resena && cp.oldsh) { // cp.conres = 0.f and cp.comresH = 0.f means that all will be multiplied by 1.f, so we can skip this step #ifdef _OPENMP - #pragma omp for nowait + #pragma omp parallel for #endif for (int i = 0; i < W_L * H_L; i++) { @@ -2277,6 +2133,15 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * int n0, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n32; n0 = n1 = n2 = n3 = n4 = n5 = n6 = n7 = n8 = n9 = n10 = n32 = 0; + float *koeLi[12]; + + const std::unique_ptr koeLibuffer(new float[12 * H_L * W_L]()); + + for (int i = 0; i < 12; i++) { + koeLi[i] = &koeLibuffer[i * W_L * H_L]; + } + + float maxkoeLi[12] = {0.f}; #ifdef _OPENMP #pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1) #endif @@ -2292,14 +2157,15 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * float eddlipinfl = 0.005f * cp.edgsens + 0.4f; float eddlipampl = 1.f + cp.edgampl / 50.f; - if (cp.detectedge) { //enabled Lipschitz control...more memory..more time... - float *tmCBuffer = new float[H_L * W_L]; + const std::unique_ptr tmCBuffer(new float[H_L * W_L]); float *tmC[H_L]; for (int i = 0; i < H_L; i++) { tmC[i] = &tmCBuffer[i * W_L]; } + float gradw = cp.eddet; + float tloww = cp.eddetthr; #ifdef _OPENMP #pragma omp for schedule(dynamic) collapse(2) @@ -2307,14 +2173,14 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * for (int lvl = 0; lvl < 4; lvl++) { for (int dir = 1; dir < 4; dir++) { - float ** WavCoeffs_LL = WaveletCoeffs_L.level_coeffs(lvl); - calckoe(WavCoeffs_LL, cp, koeLi, lvl, dir, WaveletCoeffs_L.level_W(lvl), WaveletCoeffs_L.level_H(lvl), edd, maxkoeLi, tmC); + const float* const* WavCoeffs_LL = WaveletCoeffs_L.level_coeffs(lvl); + float tempkoeli; + calckoe (WavCoeffs_LL, gradw, tloww, koeLi, lvl , dir, W_L, H_L, edd, tempkoeli, tmC); + maxkoeLi[lvl * 3 + dir - 1] = tempkoeli; // return convolution KoeLi and maxkoeLi of level 0 1 2 3 and Dir Horiz, Vert, Diag } } - delete [] tmCBuffer; - float aamp = 1.f + cp.eddetthrHi / 100.f; for (int lvl = 0; lvl < 4; lvl++) { @@ -2430,10 +2296,9 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * int Wlvl_L = WaveletCoeffs_L.level_W(lvl); int Hlvl_L = WaveletCoeffs_L.level_H(lvl); - float ** WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); + float* const* WavCoeffs_L = WaveletCoeffs_L.level_coeffs(lvl); -// ContAllL(koeLi, maxkoeLi, true, maxlvl, labco, varhue, varchrom, WavCoeffs_L, WavCoeffs_L0, lvl, dir, cp, Wlvl_L, Hlvl_L, skip, mean, sigma, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, ChCurve, Chutili); - ContAllL(koeLi, maxkoeLi, true, maxlvl, labco, varhue, varchrom, WavCoeffs_L, WavCoeffs_L0, lvl, dir, cp, Wlvl_L, Hlvl_L, skip, mean, sigma, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, waOpacityCurveSH, ChCurve, Chutili); + ContAllL(koeLi, maxkoeLi[lvl * 3 + dir - 1], true, maxlvl, labco, varhue, varchrom, WavCoeffs_L, WavCoeffs_L0, lvl, dir, cp, Wlvl_L, Hlvl_L, skip, mean, sigma, MaxP, MaxN, wavCLVCcurve, waOpacityCurveW, waOpacityCurveSH, ChCurve, Chutili); int minWL = min(Wlvl_L, Hlvl_L); if(minWL > 180) { @@ -2518,14 +2383,9 @@ void ImProcFunctions::WaveletcontAllL(LabImage * labco, float ** varhue, float * } } } - - //delete edge detection - if (koeLibuffer) { - delete [] koeLibuffer; - } } -void ImProcFunctions::WaveletAandBAllAB(const wavelet_decomposition &WaveletCoeffs_a, const wavelet_decomposition &WaveletCoeffs_b, +void ImProcFunctions::WaveletAandBAllAB(wavelet_decomposition& WaveletCoeffs_a, wavelet_decomposition& WaveletCoeffs_b, const cont_params &cp, FlatCurve* hhCurve, bool hhutili) { // StopWatch Stop1("WaveletAandBAllAB"); @@ -2533,8 +2393,8 @@ void ImProcFunctions::WaveletAandBAllAB(const wavelet_decomposition &WaveletCoef int W_L = WaveletCoeffs_a.level_W(0); int H_L = WaveletCoeffs_a.level_H(0); - float * WavCoeffs_a0 = WaveletCoeffs_a.coeff0; - float * WavCoeffs_b0 = WaveletCoeffs_b.coeff0; + float* WavCoeffs_a0 = WaveletCoeffs_a.get_coeff0(); + float* WavCoeffs_b0 = WaveletCoeffs_b.get_coeff0(); #ifdef _OPENMP #pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1) #endif @@ -2591,7 +2451,7 @@ void ImProcFunctions::WaveletAandBAllAB(const wavelet_decomposition &WaveletCoef } -void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float **varchrom, const wavelet_decomposition &WaveletCoeffs_ab, const Wavblcurve & wavblcurve, const WavOpacityCurveW & waOpacityCurveW, +void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float **varchrom, wavelet_decomposition& WaveletCoeffs_ab, const Wavblcurve & wavblcurve, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp, const bool useChannelA, int skip, float *meanab, float *sigmaab) { @@ -2599,7 +2459,7 @@ void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float int W_L = WaveletCoeffs_ab.level_W(0); int H_L = WaveletCoeffs_ab.level_H(0); - float * WavCoeffs_ab0 = WaveletCoeffs_ab.coeff0; + float* WavCoeffs_ab0 = WaveletCoeffs_ab.get_coeff0(); #ifdef _OPENMP #pragma omp parallel num_threads(wavNestedLevels) if (wavNestedLevels>1) @@ -2655,7 +2515,7 @@ void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float int jj = i - ii * W_L; float LL = (labco->L[ii * 2][jj * 2]) / 327.68f; //I use labco but I can use also WavCoeffs_L0 (more exact but more memory) - float sca = 1.f; //amplifer - reducter...about 1, but perhaps 0.6 or 1.3 + float sca = 1.f; //amplifier - reducter...about 1, but perhaps 0.6 or 1.3 if (useChannelA) { //green red (little magenta) //transition to avoid artifacts with 6 between 30 to 36 and 63 to 69 @@ -2748,7 +2608,7 @@ void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float int Wlvl_ab = WaveletCoeffs_ab.level_W(lvl); int Hlvl_ab = WaveletCoeffs_ab.level_H(lvl); - float ** WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); + float* const* WavCoeffs_ab = WaveletCoeffs_ab.level_coeffs(lvl); ContAllAB(labco, maxlvl, varhue, varchrom, WavCoeffs_ab, WavCoeffs_ab0, lvl, dir, waOpacityCurveW, cp, Wlvl_ab, Hlvl_ab, useChannelA, meanab, sigmaab); int minWL = min(Wlvl_ab, Hlvl_ab); @@ -2828,11 +2688,12 @@ void ImProcFunctions::WaveletcontAllAB(LabImage * labco, float ** varhue, float //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, float *koeLi[12], int level, int dir, int W_L, int H_L, float edd, float *maxkoeLi, float **tmC) +//void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, float *koeLi[12], int level, int dir, int W_L, int H_L, float edd, float *maxkoeLi, float **tmC) +void ImProcFunctions::calckoe (const float* const* WavCoeffs_LL, float gradw, float tloww, float *koeLi[12], int level, int dir, int W_L, int H_L, float edd, float &maxkoeLi, float **tmC) { int borderL = 2; - if (cp.eddetthr < 30.f) { + if (tloww < 30.f) { borderL = 1; // I calculate coefficients with r size matrix 3x3 r=1 ; 5x5 r=2; 7x7 r=3 @@ -2856,7 +2717,7 @@ void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, floa } } - } else if (cp.eddetthr >= 30.f && cp.eddetthr < 50.f) { + } else if (tloww < 50.f) { borderL = 1; for (int i = 1; i < H_L - 1; i++) { //sigma=0.85 @@ -2872,7 +2733,7 @@ void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, floa } - else if (cp.eddetthr >= 50.f && cp.eddetthr < 75.f) { + else if (tloww < 75.f) { borderL = 1; for (int i = 1; i < H_L - 1; i++) { @@ -2884,7 +2745,7 @@ void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, floa } } - else if (cp.eddetthr >= 75.f) { + else if (tloww >= 75.f) { borderL = 2; if (level > 1) { // do not activate 5x5 if level 0 or 1 @@ -2904,7 +2765,7 @@ void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, floa // 4 9 12 9 4 // 2 4 5 4 2 // divi 159 - if (cp.eddetthr < 85.f) { //sigma=1.1 + if (tloww < 85.f) { //sigma=1.1 tmC[i][j] = (15.f * WavCoeffs_LL[dir][i * W_L + j] + 10.f * WavCoeffs_LL[dir][(i - 1) * W_L + j] + 10.f * WavCoeffs_LL[dir][(i + 1) * W_L + j] + 10.f * WavCoeffs_LL[dir][i * W_L + j + 1] + 10.f * WavCoeffs_LL[dir][i * W_L + j - 1] + 7.f * WavCoeffs_LL[dir][(i - 1) * W_L + j - 1] + 7.f * WavCoeffs_LL[dir][(i - 1) * W_L + j + 1] + 7.f * WavCoeffs_LL[dir][(i + 1) * W_L + j - 1] + 7.f * WavCoeffs_LL[dir][(i + 1) * W_L + j + 1] @@ -2937,8 +2798,8 @@ void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, floa float thr = 40.f; //avoid artifact eg. noise...to test float thr2 = 1.5f * edd; //edd can be modified in option ed_detect - thr2 += cp.eddet / 30.f; //to test - float diffFactor = (cp.eddet / 100.f); + thr2 += gradw / 30.f; //to test + float diffFactor = (gradw / 100.f); for (int i = 0; i < H_L; i++) { for (int j = 0; j < W_L; j++) { @@ -2954,19 +2815,19 @@ void ImProcFunctions::calckoe(float ** WavCoeffs_LL, const cont_params& cp, floa koeLi[level * 3 + dir - 1][i * W_L + j] = rtengine::min(thr2, std::fabs(tmC[i][j] / temp)); // limit maxi //it will be more complicated to calculate both Wh and Wv, but we have also Wd==> pseudo Lipschitz - if (koeLi[level * 3 + dir - 1][i * W_L + j] > maxkoeLi[level * 3 + dir - 1]) { - maxkoeLi[level * 3 + dir - 1] = koeLi[level * 3 + dir - 1][i * W_L + j]; + if (koeLi[level * 3 + dir - 1][i * W_L + j] > maxkoeLi) { + maxkoeLi = koeLi[level * 3 + dir - 1][i * W_L + j]; } - float diff = maxkoeLi[level * 3 + dir - 1] - koeLi[level * 3 + dir - 1][i * W_L + j]; + float diff = maxkoeLi - koeLi[level * 3 + dir - 1][i * W_L + j]; diff *= diffFactor; - koeLi[level * 3 + dir - 1][i * W_L + j] = maxkoeLi[level * 3 + dir - 1] - diff; + koeLi[level * 3 + dir - 1][i * W_L + j] = maxkoeLi - diff; } } } -void ImProcFunctions::finalContAllL(float ** WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, const cont_params &cp, +void ImProcFunctions::finalContAllL(float* const* WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, const cont_params &cp, int W_L, int H_L, float *mean, float *sigma, float *MaxP, const WavOpacityCurveWL & waOpacityCurveWL) { if (cp.diagcurv && cp.finena && MaxP[level] > 0.f && mean[level] != 0.f && sigma[level] != 0.f) { //curve @@ -3120,7 +2981,7 @@ void ImProcFunctions::finalContAllL(float ** WavCoeffs_L, float * WavCoeffs_L0, } -void ImProcFunctions::ContAllL(float *koeLi[12], float *maxkoeLi, bool lipschitz, int maxlvl, LabImage * labco, float ** varhue, float **varchrom, float ** WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, struct cont_params &cp, +void ImProcFunctions::ContAllL(float *koeLi[12], float maxkoeLi, bool lipschitz, int maxlvl, LabImage * labco, const float* const* varhue, const float* const* varchrom, float* const* WavCoeffs_L, float * WavCoeffs_L0, int level, int dir, struct cont_params &cp, int W_L, int H_L, int skip, float *mean, float *sigma, float *MaxP, float *MaxN, const WavCurve & wavCLVCcurve, const WavOpacityCurveW & waOpacityCurveW, const WavOpacityCurveSH & waOpacityCurveSH, FlatCurve* ChCurve, bool Chutili) { assert(level >= 0); @@ -3376,7 +3237,7 @@ void ImProcFunctions::ContAllL(float *koeLi[12], float *maxkoeLi, bool lipschitz if (lipschitz) { if (level < 4) { - edge = 1.f + (edgePrecalc - 1.f) * (koeLi[level * 3][k]) / (1.f + 0.9f * maxkoeLi[level * 3 + dir - 1]); + edge = 1.f + (edgePrecalc - 1.f) * (koeLi[level * 3][k]) / (1.f + 0.9f * maxkoeLi); } else { edge = edgePrecalc; } @@ -3482,7 +3343,7 @@ void ImProcFunctions::ContAllL(float *koeLi[12], float *maxkoeLi, bool lipschitz if (lipschitz) { if (level < 4) { - edge = 1.f + (edgePrecalc - 1.f) * (koeLi[level * 3][k]) / (1.f + 0.9f * maxkoeLi[level * 3 + dir - 1]); + edge = 1.f + (edgePrecalc - 1.f) * (koeLi[level * 3][k]) / (1.f + 0.9f * maxkoeLi); } else { edge = edgePrecalc; } @@ -3618,10 +3479,12 @@ void ImProcFunctions::ContAllL(float *koeLi[12], float *maxkoeLi, bool lipschitz float red0 = 0.005f * (110.f - lowthr); float red1 = 0.008f * (110.f - lowthr); float red2 = 0.011f * (110.f - lowthr); - +// int n = 0; +// int m = 0; +// int p = 0; +// int q = 0; for (int i = 0; i < W_L * H_L; i++) { - float kLlevH = 1.f; - float kLlevS = 1.f; + float kLlev = 1.f; if (cpMul < 0.f) { lbeta = 1.f; // disabled for negatives values "less contrast" @@ -3725,41 +3588,43 @@ void ImProcFunctions::ContAllL(float *koeLi[12], float *maxkoeLi, bool lipschitz float aaarS = (alpha - 1.f) / (cp.t_rsl - cp.b_rsl); float bbbrS = 1.f - cp.b_rsl * aaarS; -// if (level <= cp.numlevH) { //in function of levels - float klevred = 2.f * (waOpacityCurveSH[level * 55.5f] - 0.5f); - if(klevred > 0.f && level <= 6) {// level < 6 to avoid bad use of the curve if user put negative values positives + if (level <= cp.numlevH) { //in function of levels if ((LL100 > cp.t_lhl * kH[level] && LL100 < cp.t_rhl * kH[level])) { - kLlevH = alpha; + kLlev = alpha; } else if ((LL100 > cp.b_lhl * kH[level] && LL100 <= cp.t_lhl * kH[level])) { - kLlevH = aaal * LL100 + bbal; + kLlev = aaal * LL100 + bbal; } else if ((LL100 > cp.t_rhl * kH[level] && LL100 <= cp.b_rhl * kH[level])) { - kLlevH = aaar * LL100 + bbbr; + kLlev = aaar * LL100 + bbbr; } else { - kLlevH = 1.f; + kLlev = 1.f; } - kLlevH = 1.f + (kLlevH - 1.f) * klevred; } - // if (level >= (9 - cp.numlevS)) { - if(klevred < 0.f && level >= 3) {//level > 3 to avoid bad use of the curve if user put positives values negatives + if (level >= cp.numlevS - 1) { + // if(klevred < 0.f && level >= 3) {//level > 3 to avoid bad use of the curve if user put positives values negatives if ((LL100 > cp.t_lsl && LL100 < cp.t_rsl)) { - kLlevS = alpha; + kLlev = alpha; + // n++; } else if ((LL100 > cp.b_lsl && LL100 <= cp.t_lsl)) { - kLlevS = aaalS * LL100 + bbalS; + kLlev = aaalS * LL100 + bbalS; + // m++; } else if ((LL100 > cp.t_rsl && LL100 <= cp.b_rsl)) { - kLlevS = aaarS * LL100 + bbbrS; + kLlev = aaarS * LL100 + bbbrS; + // p++; } else { - kLlevS = 1.f; + kLlev = 1.f; + // q++; } - kLlevS = 1.f - (kLlevS - 1.f) * klevred; } } else { - kLlevH = kLlevS = alpha; + kLlev = alpha; } - WavCoeffs_L[dir][i] *= (kLlevH * kLlevS); + WavCoeffs_L[dir][i] *= (kLlev); } + + // printf("lev=%i n=%i m=%i p=%i q=%i\n", level, n, m, p, q); } if (waOpacityCurveW) { @@ -3907,7 +3772,7 @@ void ImProcFunctions::ContAllL(float *koeLi[12], float *maxkoeLi, bool lipschitz // choicelevel = choicelevel == -1 ? 4 : choicelevel; } -void ImProcFunctions::ContAllAB(LabImage * labco, int maxlvl, float ** varhue, float **varchrom, float ** WavCoeffs_ab, float * WavCoeffs_ab0, int level, int dir, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp, +void ImProcFunctions::ContAllAB(LabImage * labco, int maxlvl, float ** varhue, float **varchrom, float* const* WavCoeffs_ab, float * WavCoeffs_ab0, int level, int dir, const WavOpacityCurveW & waOpacityCurveW, struct cont_params &cp, int W_ab, int H_ab, const bool useChannelA, float *meanab, float *sigmaab) { float cpMul = cp.mul[level]; diff --git a/rtengine/labimage.cc b/rtengine/labimage.cc index 153af4c75..319103d64 100644 --- a/rtengine/labimage.cc +++ b/rtengine/labimage.cc @@ -24,9 +24,12 @@ namespace rtengine { -LabImage::LabImage (int w, int h) : W(w), H(h) +LabImage::LabImage (int w, int h, bool initZero, bool multiThread) : W(w), H(h) { allocLab(w, h); + if (initZero) { + clear(multiThread); + } } LabImage::~LabImage () @@ -34,9 +37,21 @@ LabImage::~LabImage () deleteLab(); } -void LabImage::CopyFrom(LabImage *Img) +void LabImage::CopyFrom(LabImage *Img, bool multiThread) { - memcpy(data, Img->data, W * H * 3 * sizeof(float)); +#ifdef _OPENMP + #pragma omp parallel sections if(multiThread) + { + #pragma omp section + memcpy(L[0], Img->L[0], static_cast(W) * H * sizeof(float)); + #pragma omp section + memcpy(a[0], Img->a[0], static_cast(W) * H * sizeof(float)); + #pragma omp section + memcpy(b[0], Img->b[0], static_cast(W) * H * sizeof(float)); + } +#else + memcpy(data, Img->data, static_cast(W) * H * 3 * sizeof(float)); +#endif } void LabImage::getPipetteData (float &v1, float &v2, float &v3, int posX, int posY, int squareSize) @@ -102,4 +117,12 @@ void LabImage::reallocLab() allocLab(W, H); } +void LabImage::clear(bool multiThread) { +#ifdef _OPENMP + #pragma omp parallel for if(multiThread) +#endif + for(size_t i = 0; i < static_cast(H) * W * 3; ++i) { + data[i] = 0.f; + } + } } diff --git a/rtengine/labimage.h b/rtengine/labimage.h index 7140d9de0..b4b974c29 100644 --- a/rtengine/labimage.h +++ b/rtengine/labimage.h @@ -35,14 +35,15 @@ public: float** a; float** b; - LabImage (int w, int h); + LabImage (int w, int h, bool initZero = false, bool multiThread = true); ~LabImage (); //Copies image data in Img into this instance. - void CopyFrom(LabImage *Img); + void CopyFrom(LabImage *Img, bool multiThread = true); void getPipetteData (float &L, float &a, float &b, int posX, int posY, int squareSize); void deleteLab(); void reallocLab(); + void clear(bool multiThread = false); }; } diff --git a/rtengine/perspectivecorrection.cc b/rtengine/perspectivecorrection.cc new file mode 100644 index 000000000..5fa7b32f3 --- /dev/null +++ b/rtengine/perspectivecorrection.cc @@ -0,0 +1,362 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Alberto Griggio + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +// taken from darktable (src/iop/ashift.c) +/* + This file is part of darktable, + copyright (c) 2016 Ulrich Pegelow. + + darktable 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. + + darktable 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 darktable. If not, see . +*/ +// Inspiration to this module comes from the program ShiftN (http://www.shiftn.de) by +// Marcus Hebel. + +// Thanks to Marcus for his support when implementing part of the ShiftN functionality +// to darktable. + + +#include "perspectivecorrection.h" +#include "improcfun.h" +#include "procparams.h" +#include "rt_math.h" +#include +#include +#include +#include +#include +#include +#include + +#include "../rtgui/threadutils.h" +#include "colortemp.h" +#include "imagefloat.h" +#include "settings.h" + +namespace rtengine { extern const Settings *settings; } + +#define _(msg) (msg) +#define dt_control_log(msg) \ + if (settings->verbose) { \ + printf("%s\n", msg); \ + fflush(stdout); \ + } + + +namespace rtengine { + +namespace { + +inline int mat3inv(float *const dst, const float *const src) +{ + std::array, 3> tmpsrc; + std::array, 3> tmpdst; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + tmpsrc[i][j] = src[3 * i + j]; + } + } + if (invertMatrix(tmpsrc, tmpdst)) { + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + dst[3 * i + j] = tmpdst[i][j]; + } + } + return 0; + } else { + return 1; + } +} + + +// the darktable ashift iop (adapted to RT), which does most of the work +#include "ashift_dt.c" + + +} // namespace + + +namespace { + +/* +std::vector get_corners(int w, int h) +{ + int x1 = 0, y1 = 0; + int x2 = w, y2 = h; + + std::vector corners = { + Coord2D(x1, y1), + Coord2D(x1, y2), + Coord2D(x2, y2), + Coord2D(x2, y1) + }; + return corners; +} +*/ + +void init_dt_structures(dt_iop_ashift_params_t *p, dt_iop_ashift_gui_data_t *g, + const procparams::PerspectiveParams *params) +{ + dt_iop_ashift_params_t dp = { + 0.0f, + 0.0f, + 0.0f, + 0.0f, + DEFAULT_F_LENGTH, + 1.f, + 0.0f, + 1.0f, + ASHIFT_MODE_SPECIFIC, + 0, + ASHIFT_CROP_OFF, + 0.0f, + 1.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f + }; + *p = dp; + + g->buf = NULL; + g->buf_width = 0; + g->buf_height = 0; + g->buf_x_off = 0; + g->buf_y_off = 0; + g->buf_scale = 1.0f; + g->buf_hash = 0; + g->isflipped = 0; + g->lastfit = ASHIFT_FIT_NONE; + g->fitting = 0; + g->lines = NULL; + g->lines_count =0; + g->horizontal_count = 0; + g->vertical_count = 0; + g->grid_hash = 0; + g->lines_hash = 0; + g->rotation_range = ROTATION_RANGE_SOFT; + g->lensshift_v_range = LENSSHIFT_RANGE_SOFT; + g->lensshift_h_range = LENSSHIFT_RANGE_SOFT; + g->shear_range = SHEAR_RANGE_SOFT; + g->camera_pitch_range = CAMERA_ANGLE_RANGE_SOFT; + g->camera_yaw_range = CAMERA_ANGLE_RANGE_SOFT; + g->lines_suppressed = 0; + g->lines_version = 0; + g->show_guides = 0; + g->isselecting = 0; + g->isdeselecting = 0; + g->isbounding = ASHIFT_BOUNDING_OFF; + g->near_delta = 0; + g->selecting_lines_version = 0; + g->points = NULL; + g->points_idx = NULL; + g->points_lines_count = 0; + g->points_version = 0; + g->jobcode = ASHIFT_JOBCODE_NONE; + g->jobparams = 0; + g->adjust_crop = FALSE; + g->lastx = g->lasty = -1.0f; + g->crop_cx = g->crop_cy = 1.0f; + + if (params) { + p->rotation = params->camera_roll; + p->lensshift_v = params->camera_shift_vert; + p->lensshift_h = params->camera_shift_horiz; + p->f_length = params->camera_focal_length; + p->crop_factor = params->camera_crop_factor; + p->camera_pitch = params->camera_pitch; + p->camera_yaw = params->camera_yaw; + } +} + + +/* +void get_view_size(int w, int h, const procparams::PerspectiveParams ¶ms, double &cw, double &ch) +{ + double min_x = RT_INFINITY, max_x = -RT_INFINITY; + double min_y = RT_INFINITY, max_y = -RT_INFINITY; + + auto corners = get_corners(w, h); + + float homo[3][3]; + homography((float *)homo, params.angle, params.vertical / 100.0, -params.horizontal / 100.0, params.shear / 100.0, params.flength * params.cropfactor, 100.f, params.aspect, w, h, ASHIFT_HOMOGRAPH_FORWARD); + + for (auto &c : corners) { + float pin[3] = { float(c.x), float(c.y), 1.f }; + float pout[3]; + mat3mulv(pout, (float *)homo, pin); + double x = pout[0] / pout[2]; + double y = pout[1] / pout[2]; + min_x = min(min_x, x); + max_x = max(max_x, x); + min_y = min(min_y, y); + max_y = max(max_y, y); + } + + cw = max_x - min_x; + ch = max_y - min_y; +} +*/ + +} // namespace + + +PerspectiveCorrection::Params PerspectiveCorrection::autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata) +{ + auto pcp = procparams::PerspectiveParams(pparams->perspective); + procparams::PerspectiveParams dflt; + /* + pcp.horizontal = dflt.horizontal; + pcp.vertical = dflt.vertical; + pcp.angle = dflt.angle; + pcp.shear = dflt.shear; + */ + pcp.camera_pitch = dflt.camera_pitch; + pcp.camera_roll = dflt.camera_roll; + pcp.camera_yaw = dflt.camera_yaw; + + dt_iop_ashift_params_t p; + dt_iop_ashift_gui_data_t g; + init_dt_structures(&p, &g, &pparams->perspective); + dt_iop_module_t module; + module.gui_data = &g; + module.is_raw = src->isRAW(); + + int tr = getCoarseBitMask(pparams->coarse); + int fw, fh; + src->getFullSize(fw, fh, tr); + int skip = max(float(max(fw, fh)) / 900.f + 0.5f, 1.f); + PreviewProps pp(0, 0, fw, fh, skip); + int w, h; + src->getSize(pp, w, h); + std::unique_ptr img(new Imagefloat(w, h)); + + ProcParams neutral; + neutral.raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST); + neutral.raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST); + neutral.icm.outputProfile = ColorManagementParams::NoICMString; + src->getImage(src->getWB(), tr, img.get(), pp, neutral.toneCurve, neutral.raw); + src->convertColorSpace(img.get(), pparams->icm, src->getWB()); + + neutral.commonTrans.autofill = false; // Ensures crop factor is correct. + // TODO: Ensure image borders of rotated image do not get detected as lines. + neutral.rotate = pparams->rotate; + neutral.distortion = pparams->distortion; + neutral.lensProf = pparams->lensProf; + ImProcFunctions ipf(&neutral, true); + if (ipf.needsTransform(w, h, src->getRotateDegree(), src->getMetaData())) { + Imagefloat *tmp = new Imagefloat(w, h); + ipf.transform(img.get(), tmp, 0, 0, 0, 0, w, h, w, h, + src->getMetaData(), src->getRotateDegree(), false); + img.reset(tmp); + } + + // allocate the gui buffer + g.buf = static_cast(malloc(sizeof(float) * w * h * 4)); + g.buf_width = w; + g.buf_height = h; + + img->normalizeFloatTo1(); + +#ifdef _OPENMP +# pragma omp parallel for +#endif + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + int i = (y * w + x) * 4; + g.buf[i] = img->r(y, x); + g.buf[i+1] = img->g(y, x); + g.buf[i+2] = img->b(y, x); + g.buf[i+3] = 1.f; + } + } + + dt_iop_ashift_fitaxis_t fitaxis = ASHIFT_FIT_NONE; + if (corr_pitch && corr_yaw) { + fitaxis = ASHIFT_FIT_BOTH_SHEAR; + } else if (corr_pitch) { + fitaxis = ASHIFT_FIT_VERTICALLY; + } else if (corr_yaw) { + fitaxis = ASHIFT_FIT_HORIZONTALLY; + } + + // reset the pseudo-random seed for repeatability -- ashift_dt uses rand() + // internally! + srand(1); + + auto res = do_get_structure(&module, &p, ASHIFT_ENHANCE_EDGES) && do_fit(&module, &p, fitaxis); + Params retval = { + .angle = p.rotation, + .pitch = p.camera_pitch, + .yaw = p.camera_yaw + }; + + // cleanup the gui + if (g.lines) free(g.lines); + if (g.points) free(g.points); + if (g.points_idx) free(g.points_idx); + free(g.buf); + + if (!res) { + retval.angle = pparams->perspective.camera_roll; + retval.pitch = pparams->perspective.camera_pitch; + retval.yaw = pparams->perspective.camera_yaw; + } + return retval; +} + + +/* +void PerspectiveCorrection::autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams ¶ms, const FramesMetaData *metadata, int &x, int &y, int &w, int &h) +{ + auto pp = import_meta(params, metadata); + double cw, ch; + get_view_size(width, height, params, cw, ch); + double s = min(double(width)/cw, double(height)/ch); + dt_iop_ashift_params_t p; + dt_iop_ashift_gui_data_t g; + init_dt_structures(&p, &g, &pp); + dt_iop_module_t module = { &g, false }; + g.buf_width = width; + g.buf_height = height; + p.cropmode = fixratio ? ASHIFT_CROP_ASPECT : ASHIFT_CROP_LARGEST; + do_crop(&module, &p); + cw *= s; + ch *= s; + double ox = p.cl * cw; + double oy = p.ct * ch; + x = ox - (cw - width)/2.0 + 0.5; + y = oy - (ch - height)/2.0 + 0.5; + w = (p.cr - p.cl) * cw; + h = (p.cb - p.ct) * ch; +} +*/ + +} // namespace rtengine diff --git a/rtengine/perspectivecorrection.h b/rtengine/perspectivecorrection.h new file mode 100644 index 000000000..bf7cfa08d --- /dev/null +++ b/rtengine/perspectivecorrection.h @@ -0,0 +1,43 @@ +/* -*- C++ -*- + * + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Alberto Griggio + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ + +#pragma once + +#include "coord2d.h" +#include "procparams.h" +#include "imagesource.h" + +namespace rtengine { + +class PerspectiveCorrection { +public: + struct Params + { + double angle; + double pitch; + double yaw; + }; + + static Params autocompute(ImageSource *src, bool corr_pitch, bool corr_yaw, const procparams::ProcParams *pparams, const FramesMetaData *metadata); + + //static void autocrop(int width, int height, bool fixratio, const procparams::PerspectiveParams ¶ms, const FramesMetaData *metadata, int &x, int &y, int &w, int &h); +}; + +} // namespace rtengine diff --git a/rtengine/procevents.h b/rtengine/procevents.h index 1b00d4218..e3243938f 100644 --- a/rtengine/procevents.h +++ b/rtengine/procevents.h @@ -518,29 +518,481 @@ enum ProcEventCode { EvLEnabled = 492, EvPdShrEnabled = 493, EvPdShrMaskToggled = 494, + EvLocallabSpotDeleted = 495, + EvLocallabSpotSelected = 496, + EvLocallabSpotName = 497, + EvLocallabSpotVisibility = 498, + EvLocallabSpotShape = 499, + EvLocallabSpotSpotMethod = 500, + EvLocallabSpotShapeMethod = 501, + EvLocallabSpotLocX = 502, + EvLocallabSpotLocXL = 503, + EvLocallabSpotLocY = 504, + EvLocallabSpotLocYT = 505, + EvLocallabSpotCenter = 506, + EvLocallabSpotCircrad = 507, + EvLocallabSpotQualityMethod = 508, + EvLocallabSpotTransit = 509, + EvLocallabSpotThresh = 510, + EvLocallabSpotIter = 511, + EvLocallabSpotSensiexclu = 512, + EvLocallabSpotStruc = 513, + EvlocallabEnabled = 514, + EvLocenacolor = 515, + Evlocallabcurvactiv = 516, + Evlocallablightness = 517, + Evlocallabcontrast = 518, + Evlocallabchroma = 519, + Evlocallabsensi = 520, + EvlocallabqualitycurveMethod = 521, + Evlocallabllshape = 522, + Evlocallabccshape = 523, + EvlocallabLHshape = 524, + EvlocallabHHshape = 525, + Evlocallabinvers = 526, + EvLocenaexpose = 527, + Evlocallabexpcomp = 528, + Evlocallabhlcompr = 529, + Evlocallabhlcomprthresh = 530, + Evlocallabblack = 531, + Evlocallabshcompr = 532, + Evlocallabwarm = 533, + Evlocallabsensiex = 534, + Evlocallabshapeexpos = 535, + EvLocenavibrance = 536, + EvlocallabSaturated = 537, + EvlocallabPastels = 538, + EvlocallabPastSatThreshold = 539, + EvlocallabProtectSkins = 540, + EvlocallabAvoidColorShift = 541, + EvlocallabPastSatTog = 542, + Evlocallabsensiv = 543, + EvlocallabSkinTonesCurve = 544, + EvLocenablur = 545, + Evlocallabradius = 546, + Evlocallabstrength = 547, + Evlocallabsensibn = 548, + EvlocallabblurMethod = 549, + Evlocallabactivlum = 550, + EvLocenatonemap = 551, + Evlocallabstren = 552, + Evlocallabgamma = 553, + Evlocallabestop = 554, + Evlocallabscaltm = 555, + Evlocallabrewei = 556, + Evlocallabsensitm = 557, + EvLocenareti = 558, + EvlocallabretinexMethod = 559, + Evlocallabstr = 560, + Evlocallabchrrt = 561, + Evlocallabneigh = 562, + Evlocallabvart = 563, + Evlocallabsensih = 564, + EvlocallabCTgainCurve = 565, + Evlocallabinversret = 566, + EvLocenasharp = 567, + Evlocallabsharradius = 568, + Evlocallabsharamount = 569, + Evlocallabshardamping = 570, + Evlocallabshariter = 571, + Evlocallabsensis = 572, + Evlocallabinverssha = 573, + EvLocenacbdl = 574, + EvlocallabEqualizer = 575, + Evlocallabchromacbdl = 576, + EvlocallabThresho = 577, + Evlocallabsensicb = 578, + // EvLocenadenoi = 579, + Evlocallabnoiselumf = 580, + Evlocallabnoiselumc = 581, + Evlocallabnoiselumdetail = 582, + Evlocallabnoiselequal = 583, + Evlocallabnoisechrof = 584, + Evlocallabnoisechroc = 585, + Evlocallabnoisechrodetail = 586, + Evlocallabadjblur = 587, + Evlocallabbilateral = 588, + Evlocallabsensiden = 589, + Evlocallabavoid = 590, + Evlocallabsharcontrast = 591, + EvLocenacontrast = 592, + Evlocallablcradius = 593, + Evlocallablcamount = 594, + Evlocallablcdarkness = 595, + Evlocallablclightness = 596, + Evlocallabsensilc = 597, + Evlocallabdehaz = 598, + EvLocenasoft = 599, + Evlocallabstreng = 600, + Evlocallabsensisf = 601, + Evlocallabsharblur = 602, + EvLocenalabregion = 603, + EvlocallabshowmaskMethod = 604, + EvLocallabSpotSelectedWithMask = 605, + EvlocallabCCmaskshape = 606, + EvlocallabLLmaskshape = 607, + EvlocallabCCmaskexpshape = 608, + EvlocallabLLmaskexpshape = 609, + EvlocallabHHmaskshape = 610, + Evlocallabstructcol = 611, + Evlocallabstructexp = 612, + EvlocallabHHmaskexpshape = 613, + Evlocallabblendmaskcol = 614, + Evlocallabblendmaskexp = 615, + Evlocallabblurexpde = 616, + EvLocallabEnaColorMask = 617, + EvLocallabEnaExpMask = 618, + Evlocallabblurcolde = 619, + Evlocallabinversex = 620, + Evlocallabstructexlu = 621, + Evlocallabexpchroma = 622, + EvLocallabLabGridValue = 623, + EvLocallabLabstrengthgrid = 624, + EvLocallabgridMethod = 625, + EvLocenashadhigh = 626, + Evlocallabhighlights = 627, + Evlocallabh_tonalwidth = 628, + Evlocallabshadows = 629, + Evlocallabs_tonalwidth = 630, + Evlocallabsh_radius = 631, + Evlocallabsensihs = 632, + Evlocallabradmaskcol = 633, + Evlocallabradmaskexp = 634, + EvlocallabToolAdded = 635, + EvlocallabCCmaskSHshape = 636, + EvlocallabLLmaskSHshape = 637, + EvlocallabHHmaskSHshape = 638, + EvlocallabblendmaskSH = 639, + EvLocallabEnaSHMask = 640, + EvlocallabradmaskSH = 641, + EvlocallabblurSHde = 642, + Evlocallabinverssh = 643, + EvLocallabSpotbalan = 644, + Evlocallabchromaskexp = 645, + Evlocallabgammaskexp = 646, + Evlocallabslomaskexp = 647, + Evlocallabsoftradiusexp = 648, + Evlocallabchromaskcol = 649, + Evlocallabgammaskcol = 650, + Evlocallabslomaskcol = 651, + EvlocallabchromaskSH = 652, + EvlocallabgammaskSH = 653, + EvlocallabslomaskSH = 654, + Evlocallabsoftradiuscol = 655, + Evlocallabsoftradiusret = 656, + Evlocallabsoftradiuscb = 657, + EvLocallabSpotTransitweak = 658, + EvLocallabclarityml = 659, + EvLocallabcontresid = 660, + Evlocallabnoiselumf0 = 661, + Evlocallabnoiselumf2 = 662, + EvLocallabblurcbdl = 663, + Evlocallabblendmaskcb = 664, + Evlocallabradmaskcb = 665, + Evlocallabchromaskcb = 666, + Evlocallabgammaskcb = 667, + Evlocallabslomaskcb = 668, + EvlocallabCCmaskcbshape = 669, + EvlocallabLLmaskcbshape = 670, + EvlocallabHHmaskcbshape = 671, + EvLocallabEnacbMask = 672, + EvlocallabToolRemovedWithoutRefresh = 673, + Evlocallabsoftradiustm = 674, + EvLocallabSpotTransitgrad = 675, + Evlocallabamount = 676, + Evlocallabsatur = 677, + EvlocallabCCmaskretishape = 678, + EvlocallabLLmaskretishape = 679, + EvlocallabHHmaskretishape = 680, + EvLocallabEnaretiMask = 681, + Evlocallabblendmaskreti = 682, + Evlocallabradmaskreti = 683, + Evlocallabchromaskreti = 684, + Evlocallabgammaskreti = 685, + Evlocallabslomaskreti = 686, + EvlocallabToolRemovedWithRefresh = 687, + EvLocallabEnaretiMasktmap = 688, + Evlocallabscalereti = 689, + Evlocallabdarkness = 690, + Evlocallablightnessreti = 691, + Evlocallablimd = 692, + Evlocallablaplace = 693, + EvlocallabsoftMethod = 694, + Evlocallabequilret = 695, + Evlocallabequiltm = 696, + Evlocallabfftwlc = 697, + Evlocallabfftwreti = 698, + // EvlocallabshowmasksoftMethod = 699, + Evlocallabshadex = 700, + EvlocallabexpMethod = 701, + Evlocallablaplacexp = 702, + Evlocallabbalanexp = 703, + Evlocallablinear = 704, + EvlocallabCCmasktmshape = 705, + EvlocallabLLmasktmshape = 706, + EvlocallabHHmasktmshape = 707, + EvLocallabEnatmMask = 708, + Evlocallabblendmasktm = 709, + Evlocallabradmasktm = 710, + Evlocallabchromasktm = 711, + Evlocallabgammasktm = 712, + Evlocallabslomasktm = 713, + // EvlocallabshowmasktmMethod = 714, + EvlocallablocalcontMethod = 715, + EvlocallabwavCurve = 716, + Evlocallablevelwav = 717, + Evlocallabresidcont = 718, + EvlocallabCCmaskblshape = 719, + EvlocallabLLmaskblshape = 720, + EvlocallabHHmaskblshape = 721, + EvLocallabEnablMask = 722, + // EvlocallabshowmaskblMethod = 723, + Evlocallabblendmaskbl = 724, + Evlocallabradmaskbl = 725, + Evlocallabchromaskbl = 726, + Evlocallabgammaskbl = 727, + Evlocallabslomaskbl = 728, + EvlocallabblMethod = 729, + EvlocallabmedMethod = 730, + Evlocallabitera = 731, + Evlocallabguidbl = 732, + Evlocallabepsbl = 733, + // EvlocallabshowmaskcolMethodinv = 734, + // EvlocallabshowmaskexpMethodinv = 735, + // EvlocallabshowmaskSHMethodinv = 736, + Evlocallabclarilres = 737, + Evlocallabclarisoft = 738, + Evlocallabclaricres = 739, + Evlocallabresidchro = 740, + Evlocallabgamm = 741, + Evlocallabfatamount = 742, + Evlocallabfatdetail = 743, + Evlocallabfatanchor = 744, + Evlocallabfatlevel = 745, + EvLocallabSpotCreated = 746, + EvlocallabexnoiseMethod = 747, + Evlocallabdepth = 748, + Evlocallabloglin = 749, + Evlocallablumonly = 750, + Evlocallaboffs = 751, + EvlocallabCTtransCurve = 752, + Evlocallabcliptm = 753, + EvLocallabEnatmMaskaft = 754, + EvLocallabEnaExpMaskaft = 755, + Evlocallablapmasktm = 756, + Evlocallablapmaskreti = 757, + Evlocallablapmaskexp = 758, + Evlocallablapmaskcol = 759, + EvlocallablapmaskSH = 760, + Evlocallablapmaskcb = 761, + Evlocallablapmaskbl = 762, + Evlocallablaplac = 763, + Evlocallabdetailthr = 764, + Evlocallabfftwbl = 765, + Evlocallabisogr = 766, + Evlocallabstrengr = 767, + Evlocallabscalegr = 768, + EvlocallabLmaskshape = 769, + EvlocallabLmaskexpshape = 770, + EvlocallabLmaskSHshape = 771, + EvlocallabLmasktmshape = 772, + EvlocallabLmaskretishape = 773, + EvlocallabLmaskcbshape = 774, + EvlocallabLmaskblshape = 775, + EvlocallabLLmaskblshapewav = 776, + Evlocallabshadmaskbl = 777, + EvlocallabLLmaskcolshapewav = 778, + Evlocallabshadmaskcol = 779, + EvlocallabcsThreshold = 780, + EvlocallabcsThresholdblur = 781, + EvlocallabcsThresholdcol = 782, + Evlocallabdeltae = 783, + EvLocallabSpotscopemask = 784, + EvlocallabshMethod = 785, + EvlocallabEqualizersh = 786, + EvlocallabdetailSH = 787, + EvlocallabfatamountSH = 788, + EvlocallabfatanchorSH = 789, + Evlocallabshortc = 790, + EvLocallabSpotlumask = 791, + EvlocallabgamSH = 792, + EvlocallabsloSH = 793, + Evlocallabsavrest = 794, + Evlocallabrecurs = 795, + EvLocallabmergecolMethod = 796, + Evlocallabopacol = 797, + Evlocallabrgbshape = 798, + EvLocallabtoneMethod = 799, + EvLocallabspecial = 800, + Evlocallabconthrcol = 801, + EvLocallabmerMethod = 802, + Evlocallabstrumaskcol = 803, + Evlocallabstrumaskbl = 804, + EvLocallabtoolcol = 805, + Evlocallabtoolbl = 806, + EvlocallabHHhmaskshape = 807, + EvlocallabCCmaskvibshape = 808, + EvlocallabLLmaskvibshape = 809, + EvlocallabHHmaskvibshape = 810, + // EvlocallabshowmaskvibMethod = 811, + EvLocallabEnavibMask = 812, + Evlocallabblendmaskvi = 813, + Evlocallabradmaskvib = 814, + Evlocallabchromaskvib = 815, + Evlocallabgammaskvib = 816, + Evlocallabslomaskvib = 817, + Evlocallablapmaskvib = 818, + EvlocallabLmaskvibshape = 819, + EvLocallabLabGridmergValue = 820, + Evlocallabmercol = 821, + Evlocallabmerlucol = 822, + Evlocallabstrmaskexp = 823, + Evlocallabangmaskexp = 824, + Evlocallabstrexp = 825, + Evlocallabangexp = 826, + EvlocallabstrSH = 827, + EvlocallabangSH = 828, + Evlocallabstrcol = 829, + Evlocallabangcol = 830, + Evlocallabstrcolab = 831, + EvLocallabSpotfeather = 832, + Evlocallabstrcolh = 833, + Evlocallabstrvib = 834, + Evlocallabangvib = 835, + Evlocallabstrvibab = 836, + Evlocallabstrvibh = 837, + EvLocallabSpotcomplexMethod = 838, + Evlocallabclshape = 839, + Evlocallablcshape = 840, + Evlocallabblurcol = 841, + Evlocallabcontcol = 842, + EvLocallabfftColorMask = 843, + EvLocenalog = 844, + EvLocallabAuto = 845, + EvlocallabsourceGray = 846, + EvlocallabsourceGrayAuto = 847, + EvlocallabAutogray = 848, + EvlocallabblackEv = 849, + EvlocallabwhiteEv = 850, + EvlocallabtargetGray = 851, + Evlocallabdetail = 852, + Evlocallabsensilog = 853, + Evlocallabfullimage = 854, + Evlocallabbaselog = 855, + Evlocallabresidblur = 856, + Evlocallabblurlc = 857, + Evlocallablevelblur = 858, + EvlocallabwavCurvelev = 859, + EvlocallabwavCurvecon = 860, + Evlocallabsigma = 861, + Evlocallaboriglc = 862, + Evlocallabsigmadc = 863, + Evlocallabdeltad = 864, + EvlocallabwavCurvecomp = 865, + Evlocallabfatres = 866, + EvLocallabSpotbalanh = 867, + EvlocallabwavCurveden = 868, + EvlocallabHHmasklcshape = 869, + EvlocallabCCmasklcshape = 870, + EvlocallabLLmasklcshape = 871, + EvLocallabEnalcMask = 872, + // EvlocallabshowmasklcMethod = 873, + Evlocallabblendmasklc = 874, + Evlocallabradmasklc = 875, + Evlocallabchromasklc = 876, + EvlocallabLmasklcshape = 877, + Evlocallabchromalev = 878, + Evlocallabchromablu = 879, + Evlocallaboffset = 880, + Evlocallabwavblur = 881, + Evlocallabwavcont = 882, + Evlocallabwavcomp = 883, + Evlocallabwavcompre = 884, + EvlocallabwavCurvecompre = 885, + Evlocallabresidcomp = 886, + Evlocallabthreswav = 887, + Evlocallabstrwav = 888, + Evlocallabangwav = 889, + Evlocallabwavgradl = 890, + Evlocallabstrlog = 891, + Evlocallabanglog = 892, + EvLocallabSpotcolorde = 893, + // EvlocallabshowmasksharMethod = 894, + Evlocallabshowreset = 895, + Evlocallabstrengthw = 896, + Evlocallabradiusw = 897, + Evlocallabdetailw = 898, + Evlocallabgradw = 899, + Evlocallabtloww = 900, + Evlocallabthigw = 901, + EvlocallabwavCurveedg = 902, + EvlocallablocaledgMethod = 903, + Evlocallabwavedg = 904, + Evlocallabedgw = 905, + Evlocallabbasew = 906, + EvlocallablocalneiMethod = 907, + Evlocallabwaveshow = 908, + EvLocallabSpotwavMethod = 909, + EvlocallabchroMethod = 910, + Evlocallabstrbl = 911, + Evlocallabsigmadr = 912, + Evlocallabsigmabl = 913, + Evlocallabsigmaed = 914, + Evlocallabresidsha = 915, + Evlocallabresidshathr = 916, + Evlocallabresidhi = 917, + Evlocallabresidhithr = 918, + Evlocallabsigmalc = 919, + Evlocallabsigmalc2 = 920, + Evlocallabblwh = 921, + EvlocallabcomplexityWithRefresh = 922, + EvlocallabcomplexityWithoutRefresh = 923, + EvLocallabSpotcolorscope = 924, + EvlocallabshowmasktypMethod = 925, + Evlocallabshadmaskblsha = 926, NUMOFEVENTS - }; - -class ProcEvent { +class ProcEvent +{ public: ProcEvent(): code_(0) {} ProcEvent(ProcEventCode code): code_(code) {} explicit ProcEvent(int code): code_(code) {} - operator int() const { return code_; } + operator int() const + { + return code_; + } private: int code_; }; -inline bool operator ==(ProcEvent a, ProcEvent b) { return int(a) == int(b); } -inline bool operator ==(ProcEvent a, ProcEventCode b) { return int(a) == int(b); } -inline bool operator ==(ProcEventCode a, ProcEvent b) { return int(a) == int(b); } -inline bool operator !=(ProcEvent a, ProcEvent b) { return int(a) != int(b); } -inline bool operator !=(ProcEvent a, ProcEventCode b) { return int(a) != int(b); } -inline bool operator !=(ProcEventCode a, ProcEvent b) { return int(a) != int(b); } +inline bool operator ==(ProcEvent a, ProcEvent b) +{ + return int(a) == int(b); +} +inline bool operator ==(ProcEvent a, ProcEventCode b) +{ + return int(a) == int(b); +} +inline bool operator ==(ProcEventCode a, ProcEvent b) +{ + return int(a) == int(b); +} +inline bool operator !=(ProcEvent a, ProcEvent b) +{ + return int(a) != int(b); +} +inline bool operator !=(ProcEvent a, ProcEventCode b) +{ + return int(a) != int(b); +} +inline bool operator !=(ProcEventCode a, ProcEvent b) +{ + return int(a) != int(b); +} } diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 57bcf604a..623d8c988 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -132,6 +132,16 @@ void getFromKeyfile( value = keyfile.get_string(group_name, key); } +void getFromKeyfile( + const Glib::KeyFile& keyfile, + const Glib::ustring& group_name, + const Glib::ustring& key, + std::vector& value +) +{ + value = keyfile.get_integer_list(group_name, key); +} + void getFromKeyfile( const Glib::KeyFile& keyfile, const Glib::ustring& group_name, @@ -604,7 +614,6 @@ bool RGBCurvesParams::operator !=(const RGBCurvesParams& other) const return !(*this == other); } - LocalContrastParams::LocalContrastParams(): enabled(false), radius(80), @@ -614,7 +623,6 @@ LocalContrastParams::LocalContrastParams(): { } - bool LocalContrastParams::operator==(const LocalContrastParams &other) const { return @@ -625,13 +633,11 @@ bool LocalContrastParams::operator==(const LocalContrastParams &other) const && lightness == other.lightness; } - bool LocalContrastParams::operator!=(const LocalContrastParams &other) const { return !(*this == other); } - const double ColorToningParams::LABGRID_CORR_MAX = 12000.f; const double ColorToningParams::LABGRID_CORR_SCALE = 3.f; @@ -644,46 +650,46 @@ ColorToningParams::LabCorrectionRegion::LabCorrectionRegion(): power(1), hueMask{ FCT_MinMaxCPoints, - 0.166666667, - 1., - 0.35, - 0.35, - 0.8287775246, - 1., - 0.35, - 0.35 + 0.166666667, + 1., + 0.35, + 0.35, + 0.8287775246, + 1., + 0.35, + 0.35 }, chromaticityMask{ FCT_MinMaxCPoints, - 0., - 1., - 0.35, - 0.35, - 1., - 1., - 0.35, - 0.35 - }, + 0., + 1., + 0.35, + 0.35, + 1., + 1., + 0.35, + 0.35 + }, lightnessMask{ FCT_MinMaxCPoints, - 0., - 1., - 0.35, - 0.35, - 1., - 1., - 0.35, - 0.35 - }, + 0., + 1., + 0.35, + 0.35, + 1., + 1., + 0.35, + 0.35 + }, maskBlur(0), channel(ColorToningParams::LabCorrectionRegion::CHAN_ALL) { } - bool ColorToningParams::LabCorrectionRegion::operator==(const LabCorrectionRegion &other) const { - return a == other.a + return + a == other.a && b == other.b && saturation == other.saturation && slope == other.slope @@ -696,13 +702,11 @@ bool ColorToningParams::LabCorrectionRegion::operator==(const LabCorrectionRegio && channel == other.channel; } - bool ColorToningParams::LabCorrectionRegion::operator!=(const LabCorrectionRegion &other) const { return !(*this == other); } - ColorToningParams::ColorToningParams() : enabled(false), autosat(true), @@ -741,7 +745,7 @@ ColorToningParams::ColorToningParams() : strength(50), balance(0), hlColSat(60, 80, false), - shadowsColSat (80, 208, false), + shadowsColSat(80, 208, false), clcurve{ DCT_NURBS, 0.00, @@ -844,7 +848,7 @@ void ColorToningParams::mixerToCurve(std::vector& colorCurve, std::vecto float highSat = 0.f; float minTmp, maxTmp; -// Fill the shadow mixer values of the Color TOning tool + // Fill the shadow mixer values of the Color TOning tool low[0] = float (redlow) / 100.f; // [-1. ; +1.] low[1] = float (greenlow) / 100.f; // [-1. ; +1.] low[2] = float (bluelow) / 100.f; // [-1. ; +1.] @@ -886,7 +890,7 @@ void ColorToningParams::mixerToCurve(std::vector& colorCurve, std::vecto low[0] = low[1] = low[2] = 1.f; } -// Fill the mid-tones mixer values of the Color TOning tool + // Fill the mid-tones mixer values of the Color TOning tool med[0] = float (redmed) / 100.f; // [-1. ; +1.] med[1] = float (greenmed) / 100.f; // [-1. ; +1.] med[2] = float (bluemed) / 100.f; // [-1. ; +1.] @@ -1274,17 +1278,23 @@ WBParams::WBParams() : bool WBParams::isPanningRelatedChange(const WBParams& other) const { - return ! - (enabled == other.enabled - && ((method == "Camera" && other.method == "Camera") - || - (method == other.method - && temperature == other.temperature - && green == other.green - && equal == other.equal - && tempBias == other.tempBias) + return + !( + enabled == other.enabled + && ( + ( + method == "Camera" + && other.method == "Camera" + ) + || ( + method == other.method + && temperature == other.temperature + && green == other.green + && equal == other.equal + && tempBias == other.tempBias + ) ) - ); + ); } bool WBParams::operator ==(const WBParams& other) const @@ -1305,8 +1315,6 @@ bool WBParams::operator !=(const WBParams& other) const const std::vector& WBParams::getWbEntries() { - - static const std::vector wb_entries = { {"Camera", WBEntry::Type::CAMERA, M("TP_WBALANCE_CAMERA"), 0, 1.f, 1.f, 0.f}, {"autitcgreen", WBEntry::Type::AUTO, M("TP_WBALANCE_AUTOITCGREEN"), 0, 1.f, 1.f, 0.f}, @@ -1859,16 +1867,42 @@ LensProfParams::LcMode LensProfParams::getMethodNumber(const Glib::ustring& mode } PerspectiveParams::PerspectiveParams() : + method("simple"), horizontal(0.0), - vertical(0.0) + vertical(0.0), + camera_crop_factor(0.0), + camera_focal_length(0.0), + camera_pitch(0.0), + camera_roll(0.0), + camera_shift_horiz(0.0), + camera_shift_vert(0.0), + camera_yaw(0.0), + projection_pitch(0.0), + projection_rotate(0.0), + projection_shift_horiz(0.0), + projection_shift_vert(0.0), + projection_yaw(0.0) { } bool PerspectiveParams::operator ==(const PerspectiveParams& other) const { return - horizontal == other.horizontal - && vertical == other.vertical; + method == other.method + && horizontal == other.horizontal + && vertical == other.vertical + && camera_focal_length == other.camera_focal_length + && camera_crop_factor == other.camera_crop_factor + && camera_pitch == other.camera_pitch + && camera_roll == other.camera_roll + && camera_shift_horiz == other.camera_shift_horiz + && camera_shift_vert == other.camera_shift_vert + && camera_yaw == other.camera_yaw + && projection_shift_horiz == other.projection_shift_horiz + && projection_shift_vert == other.projection_shift_vert + && projection_rotate == other.projection_rotate + && projection_pitch == other.projection_pitch + && projection_yaw == other.projection_yaw; } bool PerspectiveParams::operator !=(const PerspectiveParams& other) const @@ -2176,12 +2210,21 @@ WaveletParams::WaveletParams() : 0.35, 0.5, 0., + 0.35, + 0.35, + 1.0, + 0.0, + 0.35, + 0.35 +/* + 0.0, 0.35, 0.35, 1.0, 0.0, 0.35, 0.35 +*/ }, opacityCurveRG{ static_cast(FCT_MinMaxCPoints), @@ -2194,6 +2237,42 @@ WaveletParams::WaveletParams() : 0.35, 0.35 }, + opacityCurveSH{ + static_cast(FCT_MinMaxCPoints), + 0., + 1., + 0.35, + 0.35, + 0.15, + 0.9, + 0.35, + 0.35, + 0.4, + 0.8, + 0.35, + 0.35, + 0.4, + 0.5, + 0.35, + 0.35, + 0.5, + 0.5, + 0.35, + 0.35, + 0.5, + 0.2, + 0.35, + 0.35, + 0.8, + 0.1, + 0.35, + 0.35, + 1.0, + 0., + 0.35, + 0.35 + }, +/* opacityCurveSH{ static_cast(FCT_MinMaxCPoints), 0.0, @@ -2213,6 +2292,7 @@ WaveletParams::WaveletParams() : 0.35, 0.35 }, +*/ opacityCurveBY{ static_cast(FCT_MinMaxCPoints), 0.0, @@ -2266,7 +2346,7 @@ WaveletParams::WaveletParams() : enabled(false), median(false), medianlev(false), - linkedg(true), + linkedg(false), cbenab(false), greenlow(0), bluelow(0), @@ -2278,7 +2358,7 @@ WaveletParams::WaveletParams() : balchrom(0.), chromfi(0.), chromco(0.), - mergeL(40.), + mergeL(20.), mergeC(20.), softrad(0.), softradend(0.), @@ -2343,7 +2423,7 @@ WaveletParams::WaveletParams() : thres(7), chroma(5), chro(0), - threshold(5), + threshold(4), threshold2(5), edgedetect(90), edgedetectthr(20), @@ -2531,6 +2611,1629 @@ void WaveletParams::getCurves( } +LocallabParams::LocallabSpot::LocallabSpot() : + // Control spot settings + name(""), + isvisible(true), + shape("ELI"), + spotMethod("norm"), + wavMethod("D4"), + sensiexclu(12), + structexclu(0), + struc(4.0), + shapeMethod("IND"), + loc{150, 150, 150, 150}, + centerX(0), + centerY(0), + circrad(18), + qualityMethod("enh"), + complexMethod("mod"), + transit(60.), + feather(25.), + thresh(2.0), + iter(2.0), + balan(1.0), + balanh(1.0), + colorde(5.0), + colorscope(30.0), + transitweak(1.0), + transitgrad(0.0), + avoid(false), + blwh(false), + recurs(false), + laplac(true), + deltae(true), + shortc(false), + savrest(false), + scopemask(60), + lumask(10), + // Color & Light + visicolor(false), + expcolor(false), + complexcolor(0), + curvactiv(false), + lightness(0), + contrast(0), + chroma(0), + labgridALow(0.0), + labgridBLow(0.0), + labgridAHigh(0.0), + labgridBHigh(0.0), + labgridALowmerg(0.0), + labgridBLowmerg(0.0), + labgridAHighmerg(-3500.0), + labgridBHighmerg(-4600.0), + strengthgrid(30), + sensi(15), + structcol(0), + strcol(0.), + strcolab(0.), + strcolh(0.), + angcol(0.), + blurcolde(5), + blurcol(0.2), + contcol(0.), + blendmaskcol(0), + radmaskcol(0.0), + chromaskcol(0.0), + gammaskcol(1.0), + slomaskcol(0.0), + shadmaskcol(0), + strumaskcol(0.), + lapmaskcol(0.0), + qualitycurveMethod("none"), + gridMethod("one"), + merMethod("mone"), + toneMethod("fou"), + mergecolMethod("one"), + llcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + lccurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + cccurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + clcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + rgbcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + LHcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.50, + 0.35, + 0.35, + 0.166, + 0.50, + 0.35, + 0.35, + 0.333, + 0.50, + 0.35, + 0.35, + 0.50, + 0.50, + 0.35, + 0.35, + 0.666, + 0.50, + 0.35, + 0.35, + 0.833, + 0.50, + 0.35, + 0.35 + }, + HHcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.50, + 0.35, + 0.35, + 0.166, + 0.50, + 0.35, + 0.35, + 0.333, + 0.50, + 0.35, + 0.35, + 0.50, + 0.50, + 0.35, + 0.35, + 0.666, + 0.50, + 0.35, + 0.35, + 0.833, + 0.50, + 0.35, + 0.35 + }, + invers(false), + special(false), + toolcol(true), + enaColorMask(false), + fftColorMask(true), + CCmaskcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.00, + 1.0, + 0.35, + 0.35 + }, + LLmaskcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.00, + 1.0, + 0.35, + 0.35 + }, + HHmaskcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.00, + 1.0, + 0.35, + 0.35 + }, + HHhmaskcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 0.50, + 0.5, + 0.35, + 0.35, + 1.00, + 0.5, + 0.35, + 0.35 + }, + softradiuscol(0.0), + opacol(60.0), + mercol(18.0), + merlucol(32.0), + conthrcol(0.0), + Lmaskcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + LLmaskcolcurvewav{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 1., + 0.5, + 0.35, + 0.35 + }, + csthresholdcol(0, 0, 6, 5, false), + // Exposure + visiexpose(false), + expexpose(false), + complexexpose(0), + expcomp(0.0), + hlcompr(0), + hlcomprthresh(0), + black(0), + shadex(0), + shcompr(50), + expchroma(5), + sensiex(60), + structexp(0), + blurexpde(5), + strexp(0.), + angexp(0.), + excurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + inversex(false), + enaExpMask(false), + enaExpMaskaft(false), + CCmaskexpcurve{ + static_cast(FCT_MinMaxCPoints),0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmaskexpcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmaskexpcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + blendmaskexp(0), + radmaskexp(0.0), + chromaskexp(0.0), + gammaskexp(1.0), + slomaskexp(0.0), + lapmaskexp(0.0), + strmaskexp(0.0), + angmaskexp(0.0), + softradiusexp(0.0), + Lmaskexpcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + expMethod("std"), + exnoiseMethod("none"), + laplacexp(0.0), + balanexp(1.0), + linear(0.05), + gamm(0.4), + fatamount(1.0), + fatdetail(40.0), + fatanchor(1.0), + fatlevel(1.), + // Shadow highlight + visishadhigh(false), + expshadhigh(false), + complexshadhigh(0), + shMethod("std"), + multsh{0, 0, 0, 0, 0}, + highlights(0), + h_tonalwidth(70), + shadows(0), + s_tonalwidth(30), + sh_radius(40), + sensihs(15), + enaSHMask(false), + CCmaskSHcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmaskSHcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmaskSHcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + blendmaskSH(0), + radmaskSH(0.0), + blurSHde(5), + strSH(0.), + angSH(0.), + inverssh(false), + chromaskSH(0.0), + gammaskSH(1.0), + slomaskSH(0.0), + lapmaskSH(0.0), + detailSH(0), + LmaskSHcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + fatamountSH(1.0), + fatanchorSH(50.0), + gamSH(2.4), + sloSH(12.92), + // Vibrance + visivibrance(false), + expvibrance(false), + complexvibrance(0), + saturated(0), + pastels(0), + warm(0), + psthreshold({0, 75, false}), + protectskins(false), + avoidcolorshift(true), + pastsattog(true), + sensiv(15), + skintonescurve{ + static_cast(DCT_Linear) + }, + CCmaskvibcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmaskvibcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmaskvibcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + enavibMask(false), + blendmaskvib(0), + radmaskvib(0.0), + chromaskvib(0.0), + gammaskvib(1.0), + slomaskvib(0.0), + lapmaskvib(0.0), + strvib(0.0), + strvibab(0.0), + strvibh(0.0), + angvib(0.0), + Lmaskvibcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + // Soft Light + visisoft(false), + expsoft(false), + complexsoft(0), + streng(0), + sensisf(30), + laplace(25.), + softMethod("soft"), + // Blur & Noise + visiblur(false), + expblur(false), + complexblur(0), + radius(1.5), + strength(0), + sensibn(40), + itera(1), + guidbl(0), + strbl(50), + isogr(400), + strengr(0), + scalegr(100), + epsbl(0), + blMethod("blur"), + chroMethod("lum"), + blurMethod("norm"), + medMethod("33"), + activlum(true), + noiselumf(0.), + noiselumf0(0.), + noiselumf2(0.), + noiselumc(0.), + noiselumdetail(0.), + noiselequal(7), + noisechrof(0.), + noisechroc(0.), + noisechrodetail(0.), + adjblur(0), + bilateral(0), + sensiden(60), + detailthr(0), + locwavcurveden{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.0, + 0.0, + 0.35, + 0.5, + 0., + 0.35, + 0.35, + 1.0, + 0.0, + 0.35, + 0.35 + }, + showmaskblMethodtyp("blur"), + CCmaskblcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmaskblcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmaskblcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + enablMask(false), + fftwbl(false), + toolbl(false), + blendmaskbl(0), + radmaskbl(0.0), + chromaskbl(0.0), + gammaskbl(1.0), + slomaskbl(0.0), + lapmaskbl(0.0), + shadmaskbl(0), + shadmaskblsha(0), + strumaskbl(0.), + Lmaskblcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + LLmaskblcurvewav{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 1., + 0.5, + 0.35, + 0.35 + }, + csthresholdblur(0, 0, 6, 5, false), + // Tone Mapping + visitonemap(false), + exptonemap(false), + complextonemap(0), + stren(0.5), + gamma(1.0), + estop(1.4), + scaltm(1.0), + rewei(0), + satur(0.), + sensitm(30), + softradiustm(0.0), + amount(95.), + equiltm(true), + CCmasktmcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmasktmcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmasktmcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + enatmMask(false), + enatmMaskaft(false), + blendmasktm(0), + radmasktm(0.0), + chromasktm(0.0), + gammasktm(1.0), + slomasktm(0.0), + lapmasktm(0.0), + Lmasktmcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + // Retinex + visireti(false), + expreti(false), + complexreti(0), + retinexMethod("high"), + str(0.), + chrrt(0.0), + neigh(50.0), + vart(150.0), + offs(0.0), + dehaz(0), + depth(25), + sensih(60), + localTgaincurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.12, + 0.35, + 0.35, + 0.70, + 0.50, + 0.35, + 0.35, + 1.00, + 0.12, + 0.35, + 0.35 + }, + localTtranscurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.50, + 0.35, + 0.35, + 0.5, + 0.5, + 0.35, + 0.35, + 1.00, + 0.50, + 0.35, + 0.35 + }, + inversret(false), + equilret(true), + loglin(false), + lumonly(false), + softradiusret(40.0), + CCmaskreticurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmaskreticurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmaskreticurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + enaretiMask(false), + enaretiMasktmap(true), + blendmaskreti(0), + radmaskreti(0.0), + chromaskreti(0.0), + gammaskreti(1.0), + slomaskreti(0.0), + lapmaskreti(0.0), + scalereti(2.0), + darkness(2.0), + lightnessreti(1.0), + limd(8.0), + cliptm(1.0), + fftwreti(false), + Lmaskreticurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + // Sharpening + visisharp(false), + expsharp(false), + complexsharp(0), + sharcontrast(20), + sharradius(0.75), + sharamount(100), + shardamping(0), + shariter(30), + sharblur(0.2), + sensisha(40), + inverssha(false), + // Local Contrast + visicontrast(false), + expcontrast(false), + complexcontrast(0), + lcradius(80), + lcamount(0.0), + lcdarkness(1.0), + lclightness(1.0), + sigmalc(1.0), + levelwav(4), + residcont(0.0), + residsha(0.0), + residshathr(30.0), + residhi(0.0), + residhithr(70.0), + residblur(0.0), + levelblur(0.0), + sigmabl(1.0), + residchro(0.0), + residcomp(0.0), + sigma(1.0), + offset(1.0), + sigmadr(1.0), + threswav(1.4), + chromalev(1.0), + chromablu(0.0), + sigmadc(1.0), + deltad(0.0), + fatres(0.0), + clarilres(0.0), + claricres(0.0), + clarisoft(1.0), + sigmalc2(1.0), + strwav(0.0), + angwav(0.0), + strengthw(0.0), + sigmaed(1.0), + radiusw(15.0), + detailw(10.0), + gradw(90.0), + tloww(20.0), + thigw(0.0), + edgw(60.0), + basew(10.0), + sensilc(60), + fftwlc(false), + blurlc(true), + wavblur(false), + wavedg(false), + waveshow(false), + wavcont(false), + wavcomp(false), + wavgradl(false), + wavcompre(false), + origlc(false), + localcontMethod("loc"), + localedgMethod("thr"), + localneiMethod("low"), + locwavcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 1., + 0.5, + 0.35, + 0.35 + }, + csthreshold(0, 0, 6, 6, false), + loclevwavcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.0, + 0.0, + 0.35, + 0.5, + 0., + 0.35, + 0.35, + 1.0, + 0.0, + 0.35, + 0.35 + }, + locconwavcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.5, + 0.35, + 0.35, + 1., + 0.5, + 0.35, + 0.35 + }, + loccompwavcurve{ + static_cast(FCT_MinMaxCPoints), + 0.00, + 0.35, + 0.35, + 0.00, + 0.35, + 0.75, + 0.35, + 0.35, + 0.60, + 0.75, + 0.35, + 0.35, + 1.00, + 0.35, + 0.00, + 0.00 + }, + loccomprewavcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.75, + 0.35, + 0.35, + 1., + 0.75, + 0.35, + 0.35 + }, + locedgwavcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 0.25, + 0.35, + 0.35, + 0.50, + 0.75, + 0.35, + 0.35, + 0.90, + 0.0, + 0.35, + 0.35 + }, + CCmasklccurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmasklccurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmasklccurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + enalcMask(false), + blendmasklc(0), + radmasklc(0.0), + chromasklc(0.0), + Lmasklccurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + // Contrast by detail levels + visicbdl(false), + expcbdl(false), + complexcbdl(0), + mult{1.0, 1.0, 1.0, 1.0, 1.0, 1.0}, + chromacbdl(0.), + threshold(0.2), + sensicb(60), + clarityml(0.1), + contresid(0), + blurcbdl(0.), + softradiuscb(0.0), + enacbMask(false), + CCmaskcbcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + LLmaskcbcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + HHmaskcbcurve{ + static_cast(FCT_MinMaxCPoints), + 0.0, + 1.0, + 0.35, + 0.35, + 0.50, + 1.0, + 0.35, + 0.35, + 1.0, + 1.0, + 0.35, + 0.35 + }, + blendmaskcb(0), + radmaskcb(0.0), + chromaskcb(0.0), + gammaskcb(1.0), + slomaskcb(0.0), + lapmaskcb(0.0), + Lmaskcbcurve{ + static_cast(DCT_NURBS), + 0.0, + 0.0, + 1.0, + 1.0 + }, + // Log encoding + visilog(false), + explog(false), + autocompute(false), + sourceGray(10.), + targetGray(18.), + Autogray(true), + fullimage(true), + blackEv(-5.0), + whiteEv(10.0), + detail(0.6), + sensilog(60), + baselog(2.), + strlog(0.0), + anglog(0.0) +{ +} + +bool LocallabParams::LocallabSpot::operator ==(const LocallabSpot& other) const +{ + return + // Control spot settings + name == other.name + && isvisible == other.isvisible + && shape == other.shape + && spotMethod == other.spotMethod + && wavMethod == other.wavMethod + && sensiexclu == other.sensiexclu + && structexclu == other.structexclu + && struc == other.struc + && shapeMethod == other.shapeMethod + && loc == other.loc + && centerX == other.centerX + && centerY == other.centerY + && circrad == other.circrad + && qualityMethod == other.qualityMethod + && complexMethod == other.complexMethod + && transit == other.transit + && feather == other.feather + && thresh == other.thresh + && iter == other.iter + && balan == other.balan + && balanh == other.balanh + && colorde == other.colorde + && colorscope == other.colorscope + && transitweak == other.transitweak + && transitgrad == other.transitgrad + && avoid == other.avoid + && blwh == other.blwh + && recurs == other.recurs + && laplac == other.laplac + && deltae == other.deltae + && shortc == other.shortc + && savrest == other.savrest + && scopemask == other.scopemask + && lumask == other.lumask + // Color & Light + && visicolor == other.visicolor + && expcolor == other.expcolor + && complexcolor == other.complexcolor + && curvactiv == other.curvactiv + && lightness == other.lightness + && contrast == other.contrast + && chroma == other.chroma + && labgridALow == other.labgridALow + && labgridBLow == other.labgridBLow + && labgridAHigh == other.labgridAHigh + && labgridBHigh == other.labgridBHigh + && labgridALowmerg == other.labgridALowmerg + && labgridBLowmerg == other.labgridBLowmerg + && labgridAHighmerg == other.labgridAHighmerg + && labgridBHighmerg == other.labgridBHighmerg + && strengthgrid == other.strengthgrid + && sensi == other.sensi + && structcol == other.structcol + && strcol == other.strcol + && strcolab == other.strcolab + && strcolh == other.strcolh + && angcol == other.angcol + && blurcolde == other.blurcolde + && blurcol == other.blurcol + && contcol == other.contcol + && blendmaskcol == other.blendmaskcol + && radmaskcol == other.radmaskcol + && chromaskcol == other.chromaskcol + && gammaskcol == other.gammaskcol + && slomaskcol == other.slomaskcol + && shadmaskcol == other.shadmaskcol + && strumaskcol == other.strumaskcol + && lapmaskcol == other.lapmaskcol + && qualitycurveMethod == other.qualitycurveMethod + && gridMethod == other.gridMethod + && merMethod == other.merMethod + && toneMethod == other.toneMethod + && mergecolMethod == other.mergecolMethod + && llcurve == other.llcurve + && lccurve == other.lccurve + && cccurve == other.cccurve + && clcurve == other.clcurve + && rgbcurve == other.rgbcurve + && LHcurve == other.LHcurve + && HHcurve == other.HHcurve + && invers == other.invers + && special == other.special + && toolcol == other.toolcol + && enaColorMask == other.enaColorMask + && fftColorMask == other.fftColorMask + && CCmaskcurve == other.CCmaskcurve + && LLmaskcurve == other.LLmaskcurve + && HHmaskcurve == other.HHmaskcurve + && HHhmaskcurve == other.HHhmaskcurve + && softradiuscol == other.softradiuscol + && opacol == other.opacol + && mercol == other.mercol + && merlucol == other.merlucol + && conthrcol == other.conthrcol + && Lmaskcurve == other.Lmaskcurve + && LLmaskcolcurvewav == other.LLmaskcolcurvewav + && csthresholdcol == other.csthresholdcol + // Exposure + && visiexpose == other.visiexpose + && expexpose == other.expexpose + && complexexpose == other.complexexpose + && expcomp == other.expcomp + && hlcompr == other.hlcompr + && hlcomprthresh == other.hlcomprthresh + && black == other.black + && shadex == other.shadex + && shcompr == other.shcompr + && expchroma == other.expchroma + && sensiex == other.sensiex + && structexp == other.structexp + && blurexpde == other.blurexpde + && strexp == other.strexp + && angexp == other.angexp + && excurve == other.excurve + && inversex == other.inversex + && enaExpMask == other.enaExpMask + && enaExpMaskaft == other.enaExpMaskaft + && CCmaskexpcurve == other.CCmaskexpcurve + && LLmaskexpcurve == other.LLmaskexpcurve + && HHmaskexpcurve == other.HHmaskexpcurve + && blendmaskexp == other.blendmaskexp + && radmaskexp == other.radmaskexp + && chromaskexp == other.chromaskexp + && gammaskexp == other.gammaskexp + && slomaskexp == other.slomaskexp + && lapmaskexp == other.lapmaskexp + && strmaskexp == other.strmaskexp + && angmaskexp == other.angmaskexp + && softradiusexp == other.softradiusexp + && Lmaskexpcurve == other.Lmaskexpcurve + && expMethod == other.expMethod + && exnoiseMethod == other.exnoiseMethod + && laplacexp == other.laplacexp + && balanexp == other.balanexp + && linear == other.linear + && gamm == other.gamm + && fatamount == other.fatamount + && fatdetail == other.fatdetail + && fatanchor == other.fatanchor + && fatlevel == other.fatlevel + // Shadow highlight + && visishadhigh == other.visishadhigh + && expshadhigh == other.expshadhigh + && complexshadhigh == other.complexshadhigh + && shMethod == other.shMethod + && [this, &other]() -> bool + { + for (int i = 0; i < 5; ++i) { + if (multsh[i] != other.multsh[i]) { + return false; + } + } + return true; + }() + && highlights == other.highlights + && h_tonalwidth == other.h_tonalwidth + && shadows == other.shadows + && s_tonalwidth == other.s_tonalwidth + && sh_radius == other.sh_radius + && sensihs == other.sensihs + && enaSHMask == other.enaSHMask + && CCmaskSHcurve == other.CCmaskSHcurve + && LLmaskSHcurve == other.LLmaskSHcurve + && HHmaskSHcurve == other.HHmaskSHcurve + && blendmaskSH == other.blendmaskSH + && radmaskSH == other.radmaskSH + && blurSHde == other.blurSHde + && strSH == other.strSH + && angSH == other.angSH + && inverssh == other.inverssh + && chromaskSH == other.chromaskSH + && gammaskSH == other.gammaskSH + && slomaskSH == other.slomaskSH + && lapmaskSH == other.lapmaskSH + && detailSH == other.detailSH + && LmaskSHcurve == other.LmaskSHcurve + && fatamountSH == other.fatamountSH + && fatanchorSH == other.fatanchorSH + && gamSH == other.gamSH + && sloSH == other.sloSH + // Vibrance + && visivibrance == other.visivibrance + && expvibrance == other.expvibrance + && complexvibrance == other.complexvibrance + && saturated == other.saturated + && pastels == other.pastels + && warm == other.warm + && psthreshold == other.psthreshold + && protectskins == other.protectskins + && avoidcolorshift == other.avoidcolorshift + && pastsattog == other.pastsattog + && sensiv == other.sensiv + && skintonescurve == other.skintonescurve + && CCmaskvibcurve == other.CCmaskvibcurve + && LLmaskvibcurve == other.LLmaskvibcurve + && HHmaskvibcurve == other.HHmaskvibcurve + && enavibMask == other.enavibMask + && blendmaskvib == other.blendmaskvib + && radmaskvib == other.radmaskvib + && chromaskvib == other.chromaskvib + && gammaskvib == other.gammaskvib + && slomaskvib == other.slomaskvib + && lapmaskvib == other.lapmaskvib + && strvib == other.strvib + && strvibab == other.strvibab + && strvibh == other.strvibh + && angvib == other.angvib + && Lmaskvibcurve == other.Lmaskvibcurve + // Soft Light + && visisoft == other.visisoft + && expsoft == other.expsoft + && complexsoft == other.complexsoft + && streng == other.streng + && sensisf == other.sensisf + && laplace == other.laplace + && softMethod == other.softMethod + // Blur & Noise + && visiblur == other.visiblur + && expblur == other.expblur + && complexblur == other.complexblur + && radius == other.radius + && strength == other.strength + && sensibn == other.sensibn + && itera == other.itera + && guidbl == other.guidbl + && strbl == other.strbl + && isogr == other.isogr + && strengr == other.strengr + && scalegr == other.scalegr + && epsbl == other.epsbl + && blMethod == other.blMethod + && chroMethod == other.chroMethod + && blurMethod == other.blurMethod + && medMethod == other.medMethod + && activlum == other.activlum + && noiselumf == other.noiselumf + && noiselumf0 == other.noiselumf0 + && noiselumf2 == other.noiselumf2 + && noiselumc == other.noiselumc + && noiselumdetail == other.noiselumdetail + && noiselequal == other.noiselequal + && noisechrof == other.noisechrof + && noisechroc == other.noisechroc + && noisechrodetail == other.noisechrodetail + && adjblur == other.adjblur + && bilateral == other.bilateral + && sensiden == other.sensiden + && detailthr == other.detailthr + && locwavcurveden == other.locwavcurveden + && showmaskblMethodtyp == other.showmaskblMethodtyp + && CCmaskblcurve == other.CCmaskblcurve + && LLmaskblcurve == other.LLmaskblcurve + && HHmaskblcurve == other.HHmaskblcurve + && enablMask == other.enablMask + && fftwbl == other.fftwbl + && toolbl == other.toolbl + && blendmaskbl == other.blendmaskbl + && radmaskbl == other.radmaskbl + && chromaskbl == other.chromaskbl + && gammaskbl == other.gammaskbl + && slomaskbl == other.slomaskbl + && lapmaskbl == other.lapmaskbl + && shadmaskbl == other.shadmaskbl + && shadmaskblsha == other.shadmaskblsha + && strumaskbl == other.strumaskbl + && Lmaskblcurve == other.Lmaskblcurve + && LLmaskblcurvewav == other.LLmaskblcurvewav + && csthresholdblur == other.csthresholdblur + // Tone Mapping + && visitonemap == other.visitonemap + && exptonemap == other.exptonemap + && complextonemap == other.complextonemap + && stren == other.stren + && gamma == other.gamma + && estop == other.estop + && scaltm == other.scaltm + && rewei == other.rewei + && satur == other.satur + && sensitm == other.sensitm + && softradiustm == other.softradiustm + && amount == other.amount + && equiltm == other.equiltm + && CCmasktmcurve == other.CCmasktmcurve + && LLmasktmcurve == other.LLmasktmcurve + && HHmasktmcurve == other.HHmasktmcurve + && enatmMask == other.enatmMask + && enatmMaskaft == other.enatmMaskaft + && blendmasktm == other.blendmasktm + && radmasktm == other.radmasktm + && chromasktm == other.chromasktm + && gammasktm == other.gammasktm + && slomasktm == other.slomasktm + && lapmasktm == other.lapmasktm + && Lmasktmcurve == other.Lmasktmcurve + // Retinex + && visireti == other.visireti + && expreti == other.expreti + && complexreti == other.complexreti + && retinexMethod == other.retinexMethod + && str == other.str + && chrrt == other.chrrt + && neigh == other.neigh + && vart == other.vart + && offs == other.offs + && dehaz == other.dehaz + && depth == other.depth + && sensih == other.sensih + && localTgaincurve == other.localTgaincurve + && localTtranscurve == other.localTtranscurve + && inversret == other.inversret + && equilret == other.equilret + && loglin == other.loglin + && lumonly == other.lumonly + && softradiusret == other.softradiusret + && CCmaskreticurve == other.CCmaskreticurve + && LLmaskreticurve == other.LLmaskreticurve + && HHmaskreticurve == other.HHmaskreticurve + && enaretiMask == other.enaretiMask + && enaretiMasktmap == other.enaretiMasktmap + && blendmaskreti == other.blendmaskreti + && radmaskreti == other.radmaskreti + && chromaskreti == other.chromaskreti + && gammaskreti == other.gammaskreti + && slomaskreti == other.slomaskreti + && lapmaskreti == other.lapmaskreti + && scalereti == other.scalereti + && darkness == other.darkness + && lightnessreti == other.lightnessreti + && limd == other.limd + && cliptm == other.cliptm + && fftwreti == other.fftwreti + && Lmaskreticurve == other.Lmaskreticurve + // Sharpening + && visisharp == other.visisharp + && expsharp == other.expsharp + && complexsharp == other.complexsharp + && sharcontrast == other.sharcontrast + && sharradius == other.sharradius + && sharamount == other.sharamount + && shardamping == other.shardamping + && shariter == other.shariter + && sharblur == other.sharblur + && sensisha == other.sensisha + && inverssha == other.inverssha + // Local contrast + && visicontrast == other.visicontrast + && expcontrast == other.expcontrast + && complexcontrast == other.complexcontrast + && lcradius == other.lcradius + && lcamount == other.lcamount + && lcdarkness == other.lcdarkness + && lclightness == other.lclightness + && sigmalc == other.sigmalc + && levelwav == other.levelwav + && residcont == other.residcont + && residsha == other.residsha + && residshathr == other.residshathr + && residhi == other.residhi + && residhithr == other.residhithr + && residblur == other.residblur + && levelblur == other.levelblur + && sigmabl == other.sigmabl + && residchro == other.residchro + && residcomp == other.residcomp + && sigma == other.sigma + && offset == other.offset + && sigmadr == other.sigmadr + && threswav == other.threswav + && chromalev == other.chromalev + && chromablu == other.chromablu + && sigmadc == other.sigmadc + && deltad == other.deltad + && fatres == other.fatres + && clarilres == other.clarilres + && claricres == other.claricres + && clarisoft == other.clarisoft + && sigmalc2 == other.sigmalc2 + && strwav == other.strwav + && angwav == other.angwav + && strengthw == other.strengthw + && sigmaed == other.sigmaed + && radiusw == other.radiusw + && detailw == other.detailw + && gradw == other.gradw + && tloww == other.tloww + && thigw == other.thigw + && edgw == other.edgw + && basew == other.basew + && sensilc == other.sensilc + && fftwlc == other.fftwlc + && blurlc == other.blurlc + && wavblur == other.wavblur + && wavedg == other.wavedg + && waveshow == other.waveshow + && wavcont == other.wavcont + && wavcomp == other.wavcomp + && wavgradl == other.wavgradl + && wavcompre == other.wavcompre + && origlc == other.origlc + && localcontMethod == other.localcontMethod + && localedgMethod == other.localedgMethod + && localneiMethod == other.localneiMethod + && locwavcurve == other.locwavcurve + && csthreshold == other.csthreshold + && loclevwavcurve == other.loclevwavcurve + && locconwavcurve == other.locconwavcurve + && loccompwavcurve == other.loccompwavcurve + && loccomprewavcurve == other.loccomprewavcurve + && locedgwavcurve == other.locedgwavcurve + && CCmasklccurve == other.CCmasklccurve + && LLmasklccurve == other.LLmasklccurve + && HHmasklccurve == other.HHmasklccurve + && enalcMask == other.enalcMask + && blendmasklc == other.blendmasklc + && radmasklc == other.radmasklc + && chromasklc == other.chromasklc + && Lmasklccurve == other.Lmasklccurve + // Contrast by detail levels + && visicbdl == other.visicbdl + && expcbdl == other.expcbdl + && complexcbdl == other.complexcbdl + && [this, &other]() -> bool + { + for (int i = 0; i < 6; ++i) { + if (mult[i] != other.mult[i]) { + return false; + } + } + return true; + }() + && chromacbdl == other.chromacbdl + && threshold == other.threshold + && sensicb == other.sensicb + && clarityml == other.clarityml + && contresid == other.contresid + && blurcbdl == other.blurcbdl + && softradiuscb == other.softradiuscb + && enacbMask == other.enacbMask + && CCmaskcbcurve == other.CCmaskcbcurve + && LLmaskcbcurve == other.LLmaskcbcurve + && HHmaskcbcurve == other.HHmaskcbcurve + && blendmaskcb == other.blendmaskcb + && radmaskcb == other.radmaskcb + && chromaskcb == other.chromaskcb + && gammaskcb == other.gammaskcb + && slomaskcb == other.slomaskcb + && lapmaskcb == other.lapmaskcb + && Lmaskcbcurve == other.Lmaskcbcurve + // Log encoding + && visilog == other.visilog + && explog == other.explog + && autocompute == other.autocompute + && sourceGray == other.sourceGray + && targetGray == other.targetGray + && Autogray == other.Autogray + && fullimage == other.fullimage + && blackEv == other.blackEv + && whiteEv == other.whiteEv + && detail == other.detail + && sensilog == other.sensilog + && baselog == other.baselog + && strlog == other.strlog + && anglog == other.anglog; +} + +bool LocallabParams::LocallabSpot::operator !=(const LocallabSpot& other) const +{ + return !(*this == other); +} + +const double LocallabParams::LABGRIDL_CORR_MAX = 12800.; +const double LocallabParams::LABGRIDL_CORR_SCALE = 3.276; +const double LocallabParams::LABGRIDL_DIRECT_SCALE = 41950.; + +LocallabParams::LocallabParams() : + enabled(false), + selspot(0), + spots() +{ +} + +bool LocallabParams::operator ==(const LocallabParams& other) const +{ + return + enabled == other.enabled + && selspot == other.selspot + && spots == other.spots; +} + +bool LocallabParams::operator !=(const LocallabParams& other) const +{ + return !(*this == other); +} + DirPyrEqualizerParams::DirPyrEqualizerParams() : enabled(false), gamutlab(false), @@ -2544,7 +4247,7 @@ DirPyrEqualizerParams::DirPyrEqualizerParams() : }, threshold(0.2), skinprotect(0.0), - hueskin (-5, 25, 170, 120, false), + hueskin(-5, 25, 170, 120, false), cbdlMethod("bef") { } @@ -2807,8 +4510,6 @@ Glib::ustring RAWParams::BayerSensor::getPSDemosaicMethodString(PSDemosaicMethod return getPSDemosaicMethodStrings()[toUnderlying(method)]; } - - RAWParams::XTransSensor::XTransSensor() : method(getMethodString(Method::THREE_PASS)), dualDemosaicAutoContrast(true), @@ -3055,6 +4756,8 @@ void ProcParams::setDefaults() vignetting = {}; + locallab = {}; + chmixer = {}; blackwhite = {}; @@ -3375,7 +5078,6 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo {ColorAppearanceParams::CtcMode::CHROMA, "Chroma"}, {ColorAppearanceParams::CtcMode::SATUR, "Saturation"}, {ColorAppearanceParams::CtcMode::COLORF, "Colorfullness"} - }, colorappearance.curveMode3, keyFile @@ -3493,8 +5195,22 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->lensProf.lfLens, "LensProfile", "LFLens", lensProf.lfLens, keyFile); // Perspective correction + saveToKeyfile(!pedited || pedited->perspective.method, "Perspective", "Method", perspective.method, keyFile); saveToKeyfile(!pedited || pedited->perspective.horizontal, "Perspective", "Horizontal", perspective.horizontal, keyFile); saveToKeyfile(!pedited || pedited->perspective.vertical, "Perspective", "Vertical", perspective.vertical, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_crop_factor, "Perspective", "CameraCropFactor", perspective.camera_crop_factor, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_focal_length, "Perspective", "CameraFocalLength", perspective.camera_focal_length, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_pitch, "Perspective", "CameraPitch", perspective.camera_pitch, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_roll, "Perspective", "CameraRoll", perspective.camera_roll, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_shift_horiz, "Perspective", "CameraShiftHorizontal", perspective.camera_shift_horiz, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_shift_vert, "Perspective", "CameraShiftVertical", perspective.camera_shift_vert, keyFile); + saveToKeyfile(!pedited || pedited->perspective.camera_yaw, "Perspective", "CameraYaw", perspective.camera_yaw, keyFile); + saveToKeyfile(!pedited || pedited->perspective.projection_shift_horiz, "Perspective", "ProjectionShiftHorizontal", perspective.projection_shift_horiz, keyFile); + saveToKeyfile(!pedited || pedited->perspective.projection_pitch, "Perspective", "ProjectionPitch", perspective.projection_pitch, keyFile); + saveToKeyfile(!pedited || pedited->perspective.projection_rotate, "Perspective", "ProjectionRotate", perspective.projection_rotate, keyFile); + saveToKeyfile(!pedited || pedited->perspective.projection_shift_horiz, "Perspective", "ProjectionShiftHorizontal", perspective.projection_shift_horiz, keyFile); + saveToKeyfile(!pedited || pedited->perspective.projection_shift_vert, "Perspective", "ProjectionShiftVertical", perspective.projection_shift_vert, keyFile); + saveToKeyfile(!pedited || pedited->perspective.projection_yaw, "Perspective", "ProjectionYaw", perspective.projection_yaw, keyFile); // Gradient saveToKeyfile(!pedited || pedited->gradient.enabled, "Gradient", "Enabled", gradient.enabled, keyFile); @@ -3504,6 +5220,488 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo saveToKeyfile(!pedited || pedited->gradient.centerX, "Gradient", "CenterX", gradient.centerX, keyFile); saveToKeyfile(!pedited || pedited->gradient.centerY, "Gradient", "CenterY", gradient.centerY, keyFile); +// Locallab + saveToKeyfile(!pedited || pedited->locallab.enabled, "Locallab", "Enabled", locallab.enabled, keyFile); + saveToKeyfile(!pedited || pedited->locallab.selspot, "Locallab", "Selspot", locallab.selspot, keyFile); + + for (size_t i = 0; i < locallab.spots.size(); ++i) { + if (!pedited || i < pedited->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = locallab.spots.at(i); + const LocallabParamsEdited::LocallabSpotEdited* const spot_edited = + pedited + ? &pedited->locallab.spots.at(i) + : nullptr; + const std::string index_str = std::to_string(i); + // Control spot settings + saveToKeyfile(!pedited || spot_edited->name, "Locallab", "Name_" + index_str, spot.name, keyFile); + saveToKeyfile(!pedited || spot_edited->isvisible, "Locallab", "Isvisible_" + index_str, spot.isvisible, keyFile); + saveToKeyfile(!pedited || spot_edited->shape, "Locallab", "Shape_" + index_str, spot.shape, keyFile); + saveToKeyfile(!pedited || spot_edited->spotMethod, "Locallab", "SpotMethod_" + index_str, spot.spotMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->wavMethod, "Locallab", "WavMethod_" + index_str, spot.wavMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->sensiexclu, "Locallab", "SensiExclu_" + index_str, spot.sensiexclu, keyFile); + saveToKeyfile(!pedited || spot_edited->structexclu, "Locallab", "StructExclu_" + index_str, spot.structexclu, keyFile); + saveToKeyfile(!pedited || spot_edited->struc, "Locallab", "Struc_" + index_str, spot.struc, keyFile); + saveToKeyfile(!pedited || spot_edited->shapeMethod, "Locallab", "ShapeMethod_" + index_str, spot.shapeMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->loc, "Locallab", "Loc_" + index_str, spot.loc, keyFile); + saveToKeyfile(!pedited || spot_edited->centerX, "Locallab", "CenterX_" + index_str, spot.centerX, keyFile); + saveToKeyfile(!pedited || spot_edited->centerY, "Locallab", "CenterY_" + index_str, spot.centerY, keyFile); + saveToKeyfile(!pedited || spot_edited->circrad, "Locallab", "Circrad_" + index_str, spot.circrad, keyFile); + saveToKeyfile(!pedited || spot_edited->qualityMethod, "Locallab", "QualityMethod_" + index_str, spot.qualityMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->complexMethod, "Locallab", "ComplexMethod_" + index_str, spot.complexMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->transit, "Locallab", "Transit_" + index_str, spot.transit, keyFile); + saveToKeyfile(!pedited || spot_edited->feather, "Locallab", "Feather_" + index_str, spot.feather, keyFile); + saveToKeyfile(!pedited || spot_edited->thresh, "Locallab", "Thresh_" + index_str, spot.thresh, keyFile); + saveToKeyfile(!pedited || spot_edited->iter, "Locallab", "Iter_" + index_str, spot.iter, keyFile); + saveToKeyfile(!pedited || spot_edited->balan, "Locallab", "Balan_" + index_str, spot.balan, keyFile); + saveToKeyfile(!pedited || spot_edited->balanh, "Locallab", "Balanh_" + index_str, spot.balanh, keyFile); + saveToKeyfile(!pedited || spot_edited->colorde, "Locallab", "Colorde_" + index_str, spot.colorde, keyFile); + saveToKeyfile(!pedited || spot_edited->colorscope, "Locallab", "Colorscope_" + index_str, spot.colorscope, keyFile); + saveToKeyfile(!pedited || spot_edited->transitweak, "Locallab", "Transitweak_" + index_str, spot.transitweak, keyFile); + saveToKeyfile(!pedited || spot_edited->transitgrad, "Locallab", "Transitgrad_" + index_str, spot.transitgrad, keyFile); + saveToKeyfile(!pedited || spot_edited->avoid, "Locallab", "Avoid_" + index_str, spot.avoid, keyFile); + saveToKeyfile(!pedited || spot_edited->blwh, "Locallab", "Blwh_" + index_str, spot.blwh, keyFile); + saveToKeyfile(!pedited || spot_edited->recurs, "Locallab", "Recurs_" + index_str, spot.recurs, keyFile); + saveToKeyfile(!pedited || spot_edited->laplac, "Locallab", "Laplac_" + index_str, spot.laplac, keyFile); + saveToKeyfile(!pedited || spot_edited->deltae, "Locallab", "Deltae_" + index_str, spot.deltae, keyFile); + saveToKeyfile(!pedited || spot_edited->shortc, "Locallab", "Shortc_" + index_str, spot.shortc, keyFile); + saveToKeyfile(!pedited || spot_edited->savrest, "Locallab", "Savrest_" + index_str, spot.savrest, keyFile); + saveToKeyfile(!pedited || spot_edited->scopemask, "Locallab", "Scopemask_" + index_str, spot.scopemask, keyFile); + saveToKeyfile(!pedited || spot_edited->lumask, "Locallab", "Lumask_" + index_str, spot.lumask, keyFile); + // Color & Light + if ((!pedited || spot_edited->visicolor) && spot.visicolor) { + saveToKeyfile(!pedited || spot_edited->expcolor, "Locallab", "Expcolor_" + index_str, spot.expcolor, keyFile); + saveToKeyfile(!pedited || spot_edited->complexcolor, "Locallab", "Complexcolor_" + index_str, spot.complexcolor, keyFile); + saveToKeyfile(!pedited || spot_edited->curvactiv, "Locallab", "Curvactiv_" + index_str, spot.curvactiv, keyFile); + saveToKeyfile(!pedited || spot_edited->lightness, "Locallab", "Lightness_" + index_str, spot.lightness, keyFile); + saveToKeyfile(!pedited || spot_edited->contrast, "Locallab", "Contrast_" + index_str, spot.contrast, keyFile); + saveToKeyfile(!pedited || spot_edited->chroma, "Locallab", "Chroma_" + index_str, spot.chroma, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridALow, "Locallab", "labgridALow_" + index_str, spot.labgridALow, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridBLow, "Locallab", "labgridBLow_" + index_str, spot.labgridBLow, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridAHigh, "Locallab", "labgridAHigh_" + index_str, spot.labgridAHigh, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridBHigh, "Locallab", "labgridBHigh_" + index_str, spot.labgridBHigh, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridALowmerg, "Locallab", "labgridALowmerg_" + index_str, spot.labgridALowmerg, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridBLowmerg, "Locallab", "labgridBLowmerg_" + index_str, spot.labgridBLowmerg, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridAHighmerg, "Locallab", "labgridAHighmerg_" + index_str, spot.labgridAHighmerg, keyFile); + saveToKeyfile(!pedited || spot_edited->labgridBHighmerg, "Locallab", "labgridBHighmerg_" + index_str, spot.labgridBHighmerg, keyFile); + saveToKeyfile(!pedited || spot_edited->strengthgrid, "Locallab", "Strengthgrid_" + index_str, spot.strengthgrid, keyFile); + saveToKeyfile(!pedited || spot_edited->sensi, "Locallab", "Sensi_" + index_str, spot.sensi, keyFile); + saveToKeyfile(!pedited || spot_edited->structcol, "Locallab", "Structcol_" + index_str, spot.structcol, keyFile); + saveToKeyfile(!pedited || spot_edited->strcol, "Locallab", "Strcol_" + index_str, spot.strcol, keyFile); + saveToKeyfile(!pedited || spot_edited->strcolab, "Locallab", "Strcolab_" + index_str, spot.strcolab, keyFile); + saveToKeyfile(!pedited || spot_edited->strcolh, "Locallab", "Strcolh_" + index_str, spot.strcolh, keyFile); + saveToKeyfile(!pedited || spot_edited->angcol, "Locallab", "Angcol_" + index_str, spot.angcol, keyFile); + saveToKeyfile(!pedited || spot_edited->blurcolde, "Locallab", "Blurcolde_" + index_str, spot.blurcolde, keyFile); + saveToKeyfile(!pedited || spot_edited->blurcol, "Locallab", "Blurcol_" + index_str, spot.blurcol, keyFile); + saveToKeyfile(!pedited || spot_edited->contcol, "Locallab", "Contcol_" + index_str, spot.contcol, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskcol, "Locallab", "Blendmaskcol_" + index_str, spot.blendmaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskcol, "Locallab", "Radmaskcol_" + index_str, spot.radmaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskcol, "Locallab", "Chromaskcol_" + index_str, spot.chromaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskcol, "Locallab", "Gammaskcol_" + index_str, spot.gammaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskcol, "Locallab", "Slomaskcol_" + index_str, spot.slomaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->shadmaskcol, "Locallab", "shadmaskcol_" + index_str, spot.shadmaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->strumaskcol, "Locallab", "strumaskcol_" + index_str, spot.strumaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmaskcol, "Locallab", "Lapmaskcol_" + index_str, spot.lapmaskcol, keyFile); + saveToKeyfile(!pedited || spot_edited->qualitycurveMethod, "Locallab", "QualityCurveMethod_" + index_str, spot.qualitycurveMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->gridMethod, "Locallab", "gridMethod_" + index_str, spot.gridMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->merMethod, "Locallab", "Merg_Method_" + index_str, spot.merMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->toneMethod, "Locallab", "ToneMethod_" + index_str, spot.toneMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->mergecolMethod, "Locallab", "mergecolMethod_" + index_str, spot.mergecolMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->llcurve, "Locallab", "LLCurve_" + index_str, spot.llcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->lccurve, "Locallab", "LCCurve_" + index_str, spot.lccurve, keyFile); + saveToKeyfile(!pedited || spot_edited->cccurve, "Locallab", "CCCurve_" + index_str, spot.cccurve, keyFile); + saveToKeyfile(!pedited || spot_edited->clcurve, "Locallab", "CLCurve_" + index_str, spot.clcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->rgbcurve, "Locallab", "RGBCurve_" + index_str, spot.rgbcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LHcurve, "Locallab", "LHCurve_" + index_str, spot.LHcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHcurve, "Locallab", "HHCurve_" + index_str, spot.HHcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->invers, "Locallab", "Invers_" + index_str, spot.invers, keyFile); + saveToKeyfile(!pedited || spot_edited->special, "Locallab", "Special_" + index_str, spot.special, keyFile); + saveToKeyfile(!pedited || spot_edited->toolcol, "Locallab", "Toolcol_" + index_str, spot.toolcol, keyFile); + saveToKeyfile(!pedited || spot_edited->enaColorMask, "Locallab", "EnaColorMask_" + index_str, spot.enaColorMask, keyFile); + saveToKeyfile(!pedited || spot_edited->fftColorMask, "Locallab", "FftColorMask_" + index_str, spot.fftColorMask, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskcurve, "Locallab", "CCmaskCurve_" + index_str, spot.CCmaskcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskcurve, "Locallab", "LLmaskCurve_" + index_str, spot.LLmaskcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskcurve, "Locallab", "HHmaskCurve_" + index_str, spot.HHmaskcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHhmaskcurve, "Locallab", "HHhmaskCurve_" + index_str, spot.HHhmaskcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->softradiuscol, "Locallab", "Softradiuscol_" + index_str, spot.softradiuscol, keyFile); + saveToKeyfile(!pedited || spot_edited->opacol, "Locallab", "Opacol_" + index_str, spot.opacol, keyFile); + saveToKeyfile(!pedited || spot_edited->mercol, "Locallab", "Mercol_" + index_str, spot.mercol, keyFile); + saveToKeyfile(!pedited || spot_edited->merlucol, "Locallab", "Merlucol_" + index_str, spot.merlucol, keyFile); + saveToKeyfile(!pedited || spot_edited->conthrcol, "Locallab", "Conthrcol_" + index_str, spot.conthrcol, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmaskcurve, "Locallab", "LmaskCurve_" + index_str, spot.Lmaskcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskcolcurvewav, "Locallab", "LLmaskcolCurvewav_" + index_str, spot.LLmaskcolcurvewav, keyFile); + saveToKeyfile(!pedited || spot_edited->csthresholdcol, "Locallab", "CSThresholdcol_" + index_str, spot.csthresholdcol.toVector(), keyFile); + } + // Exposure + if ((!pedited || spot_edited->visiexpose) && spot.visiexpose) { + saveToKeyfile(!pedited || spot_edited->expexpose, "Locallab", "Expexpose_" + index_str, spot.expexpose, keyFile); + saveToKeyfile(!pedited || spot_edited->complexexpose, "Locallab", "Complexexpose_" + index_str, spot.complexexpose, keyFile); + saveToKeyfile(!pedited || spot_edited->expcomp, "Locallab", "Expcomp_" + index_str, spot.expcomp, keyFile); + saveToKeyfile(!pedited || spot_edited->hlcompr, "Locallab", "Hlcompr_" + index_str, spot.hlcompr, keyFile); + saveToKeyfile(!pedited || spot_edited->hlcomprthresh, "Locallab", "Hlcomprthresh_" + index_str, spot.hlcomprthresh, keyFile); + saveToKeyfile(!pedited || spot_edited->black, "Locallab", "Black_" + index_str, spot.black, keyFile); + saveToKeyfile(!pedited || spot_edited->shadex, "Locallab", "Shadex_" + index_str, spot.shadex, keyFile); + saveToKeyfile(!pedited || spot_edited->shcompr, "Locallab", "Shcompr_" + index_str, spot.shcompr, keyFile); + saveToKeyfile(!pedited || spot_edited->expchroma, "Locallab", "Expchroma_" + index_str, spot.expchroma, keyFile); + saveToKeyfile(!pedited || spot_edited->sensiex, "Locallab", "Sensiex_" + index_str, spot.sensiex, keyFile); + saveToKeyfile(!pedited || spot_edited->structexp, "Locallab", "Structexp_" + index_str, spot.structexp, keyFile); + saveToKeyfile(!pedited || spot_edited->blurexpde, "Locallab", "Blurexpde_" + index_str, spot.blurexpde, keyFile); + saveToKeyfile(!pedited || spot_edited->strexp, "Locallab", "Strexp_" + index_str, spot.strexp, keyFile); + saveToKeyfile(!pedited || spot_edited->angexp, "Locallab", "Angexp_" + index_str, spot.angexp, keyFile); + saveToKeyfile(!pedited || spot_edited->excurve, "Locallab", "ExCurve_" + index_str, spot.excurve, keyFile); + saveToKeyfile(!pedited || spot_edited->inversex, "Locallab", "Inversex_" + index_str, spot.inversex, keyFile); + saveToKeyfile(!pedited || spot_edited->enaExpMask, "Locallab", "EnaExpMask_" + index_str, spot.enaExpMask, keyFile); + saveToKeyfile(!pedited || spot_edited->enaExpMaskaft, "Locallab", "EnaExpMaskaft_" + index_str, spot.enaExpMaskaft, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskexpcurve, "Locallab", "CCmaskexpCurve_" + index_str, spot.CCmaskexpcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskexpcurve, "Locallab", "LLmaskexpCurve_" + index_str, spot.LLmaskexpcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskexpcurve, "Locallab", "HHmaskexpCurve_" + index_str, spot.HHmaskexpcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskexp, "Locallab", "Blendmaskexp_" + index_str, spot.blendmaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskexp, "Locallab", "Radmaskexp_" + index_str, spot.radmaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskexp, "Locallab", "Chromaskexp_" + index_str, spot.chromaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskexp, "Locallab", "Gammaskexp_" + index_str, spot.gammaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskexp, "Locallab", "Slomaskexp_" + index_str, spot.slomaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmaskexp, "Locallab", "Lapmaskexp_" + index_str, spot.lapmaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->strmaskexp, "Locallab", "Strmaskexp_" + index_str, spot.strmaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->angmaskexp, "Locallab", "Angmaskexp_" + index_str, spot.angmaskexp, keyFile); + saveToKeyfile(!pedited || spot_edited->softradiusexp, "Locallab", "Softradiusexp_" + index_str, spot.softradiusexp, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmaskexpcurve, "Locallab", "LmaskexpCurve_" + index_str, spot.Lmaskexpcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->expMethod, "Locallab", "ExpMethod_" + index_str, spot.expMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->exnoiseMethod, "Locallab", "ExnoiseMethod_" + index_str, spot.exnoiseMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->laplacexp, "Locallab", "Laplacexp_" + index_str, spot.laplacexp, keyFile); + saveToKeyfile(!pedited || spot_edited->balanexp, "Locallab", "Balanexp_" + index_str, spot.balanexp, keyFile); + saveToKeyfile(!pedited || spot_edited->linear, "Locallab", "Linearexp_" + index_str, spot.linear, keyFile); + saveToKeyfile(!pedited || spot_edited->gamm, "Locallab", "Gamm_" + index_str, spot.gamm, keyFile); + saveToKeyfile(!pedited || spot_edited->fatamount, "Locallab", "Fatamount_" + index_str, spot.fatamount, keyFile); + saveToKeyfile(!pedited || spot_edited->fatdetail, "Locallab", "Fatdetail_" + index_str, spot.fatdetail, keyFile); + saveToKeyfile(!pedited || spot_edited->fatanchor, "Locallab", "Fatanchor_" + index_str, spot.fatanchor, keyFile); + saveToKeyfile(!pedited || spot_edited->fatlevel, "Locallab", "Fatlevel_" + index_str, spot.fatlevel, keyFile); + } + // Shadow highlight + if ((!pedited || spot_edited->visishadhigh) && spot.visishadhigh) { + saveToKeyfile(!pedited || spot_edited->expshadhigh, "Locallab", "Expshadhigh_" + index_str, spot.expshadhigh, keyFile); + saveToKeyfile(!pedited || spot_edited->complexshadhigh, "Locallab", "Complexshadhigh_" + index_str, spot.complexshadhigh, keyFile); + saveToKeyfile(!pedited || spot_edited->shMethod, "Locallab", "ShMethod_" + index_str, spot.shMethod, keyFile); + + for (int j = 0; j < 5; j++) { + saveToKeyfile(!pedited || spot_edited->multsh[j], "Locallab", "Multsh" + std::to_string(j) + "_" + index_str, spot.multsh[j], keyFile); + } + + saveToKeyfile(!pedited || spot_edited->highlights, "Locallab", "highlights_" + index_str, spot.highlights, keyFile); + saveToKeyfile(!pedited || spot_edited->h_tonalwidth, "Locallab", "h_tonalwidth_" + index_str, spot.h_tonalwidth, keyFile); + saveToKeyfile(!pedited || spot_edited->shadows, "Locallab", "shadows_" + index_str, spot.shadows, keyFile); + saveToKeyfile(!pedited || spot_edited->s_tonalwidth, "Locallab", "s_tonalwidth_" + index_str, spot.s_tonalwidth, keyFile); + saveToKeyfile(!pedited || spot_edited->sh_radius, "Locallab", "sh_radius_" + index_str, spot.sh_radius, keyFile); + saveToKeyfile(!pedited || spot_edited->sensihs, "Locallab", "sensihs_" + index_str, spot.sensihs, keyFile); + saveToKeyfile(!pedited || spot_edited->enaSHMask, "Locallab", "EnaSHMask_" + index_str, spot.enaSHMask, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskSHcurve, "Locallab", "CCmaskSHCurve_" + index_str, spot.CCmaskSHcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskSHcurve, "Locallab", "LLmaskSHCurve_" + index_str, spot.LLmaskSHcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskSHcurve, "Locallab", "HHmaskSHCurve_" + index_str, spot.HHmaskSHcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskSH, "Locallab", "BlendmaskSH_" + index_str, spot.blendmaskSH, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskSH, "Locallab", "RadmaskSH_" + index_str, spot.radmaskSH, keyFile); + saveToKeyfile(!pedited || spot_edited->blurSHde, "Locallab", "BlurSHde_" + index_str, spot.blurSHde, keyFile); + saveToKeyfile(!pedited || spot_edited->strSH, "Locallab", "StrSH_" + index_str, spot.strSH, keyFile); + saveToKeyfile(!pedited || spot_edited->angSH, "Locallab", "AngSH_" + index_str, spot.angSH, keyFile); + saveToKeyfile(!pedited || spot_edited->inverssh, "Locallab", "Inverssh_" + index_str, spot.inverssh, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskSH, "Locallab", "ChromaskSH_" + index_str, spot.chromaskSH, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskSH, "Locallab", "GammaskSH_" + index_str, spot.gammaskSH, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskSH, "Locallab", "SlomaskSH_" + index_str, spot.slomaskSH, keyFile); + saveToKeyfile(!pedited || spot_edited->detailSH, "Locallab", "DetailSH_" + index_str, spot.detailSH, keyFile); + saveToKeyfile(!pedited || spot_edited->LmaskSHcurve, "Locallab", "LmaskSHCurve_" + index_str, spot.LmaskSHcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->fatamountSH, "Locallab", "FatamountSH_" + index_str, spot.fatamountSH, keyFile); + saveToKeyfile(!pedited || spot_edited->fatanchorSH, "Locallab", "FatanchorSH_" + index_str, spot.fatanchorSH, keyFile); + saveToKeyfile(!pedited || spot_edited->gamSH, "Locallab", "GamSH_" + index_str, spot.gamSH, keyFile); + saveToKeyfile(!pedited || spot_edited->sloSH, "Locallab", "SloSH_" + index_str, spot.sloSH, keyFile); + } + // Vibrance + if ((!pedited || spot_edited->visivibrance) && spot.visivibrance) { + saveToKeyfile(!pedited || spot_edited->expvibrance, "Locallab", "Expvibrance_" + index_str, spot.expvibrance, keyFile); + saveToKeyfile(!pedited || spot_edited->complexvibrance, "Locallab", "Complexvibrance_" + index_str, spot.complexvibrance, keyFile); + saveToKeyfile(!pedited || spot_edited->saturated, "Locallab", "Saturated_" + index_str, spot.saturated, keyFile); + saveToKeyfile(!pedited || spot_edited->pastels, "Locallab", "Pastels_" + index_str, spot.pastels, keyFile); + saveToKeyfile(!pedited || spot_edited->warm, "Locallab", "Warm_" + index_str, spot.warm, keyFile); + saveToKeyfile(!pedited || spot_edited->psthreshold, "Locallab", "PSThreshold_" + index_str, spot.psthreshold.toVector(), keyFile); + saveToKeyfile(!pedited || spot_edited->protectskins, "Locallab", "ProtectSkins_" + index_str, spot.protectskins, keyFile); + saveToKeyfile(!pedited || spot_edited->avoidcolorshift, "Locallab", "AvoidColorShift_" + index_str, spot.avoidcolorshift, keyFile); + saveToKeyfile(!pedited || spot_edited->pastsattog, "Locallab", "PastSatTog_" + index_str, spot.pastsattog, keyFile); + saveToKeyfile(!pedited || spot_edited->sensiv, "Locallab", "Sensiv_" + index_str, spot.sensiv, keyFile); + saveToKeyfile(!pedited || spot_edited->skintonescurve, "Locallab", "SkinTonesCurve_" + index_str, spot.skintonescurve, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskvibcurve, "Locallab", "CCmaskvibCurve_" + index_str, spot.CCmaskvibcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskvibcurve, "Locallab", "LLmaskvibCurve_" + index_str, spot.LLmaskvibcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskvibcurve, "Locallab", "HHmaskvibCurve_" + index_str, spot.HHmaskvibcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->enavibMask, "Locallab", "EnavibMask_" + index_str, spot.enavibMask, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskvib, "Locallab", "Blendmaskvib_" + index_str, spot.blendmaskvib, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskvib, "Locallab", "Radmaskvib_" + index_str, spot.radmaskvib, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskvib, "Locallab", "Chromaskvib_" + index_str, spot.chromaskvib, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskvib, "Locallab", "Gammaskvib_" + index_str, spot.gammaskvib, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskvib, "Locallab", "Slomaskvib_" + index_str, spot.slomaskvib, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmaskvib, "Locallab", "Lapmaskvib_" + index_str, spot.lapmaskvib, keyFile); + saveToKeyfile(!pedited || spot_edited->strvib, "Locallab", "Strvib_" + index_str, spot.strvib, keyFile); + saveToKeyfile(!pedited || spot_edited->strvibab, "Locallab", "Strvibab_" + index_str, spot.strvibab, keyFile); + saveToKeyfile(!pedited || spot_edited->strvibh, "Locallab", "Strvibh_" + index_str, spot.strvibh, keyFile); + saveToKeyfile(!pedited || spot_edited->angvib, "Locallab", "Angvib_" + index_str, spot.angvib, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmaskvibcurve, "Locallab", "LmaskvibCurve_" + index_str, spot.Lmaskvibcurve, keyFile); + } + // Soft Light + if ((!pedited || spot_edited->visisoft) && spot.visisoft) { + saveToKeyfile(!pedited || spot_edited->expsoft, "Locallab", "Expsoft_" + index_str, spot.expsoft, keyFile); + saveToKeyfile(!pedited || spot_edited->complexsoft, "Locallab", "Complexsoft_" + index_str, spot.complexsoft, keyFile); + saveToKeyfile(!pedited || spot_edited->streng, "Locallab", "Streng_" + index_str, spot.streng, keyFile); + saveToKeyfile(!pedited || spot_edited->sensisf, "Locallab", "Sensisf_" + index_str, spot.sensisf, keyFile); + saveToKeyfile(!pedited || spot_edited->laplace, "Locallab", "Laplace_" + index_str, spot.laplace, keyFile); + saveToKeyfile(!pedited || spot_edited->softMethod, "Locallab", "SoftMethod_" + index_str, spot.softMethod, keyFile); + } + // Blur & Noise + if ((!pedited || spot_edited->visiblur) && spot.visiblur) { + saveToKeyfile(!pedited || spot_edited->expblur, "Locallab", "Expblur_" + index_str, spot.expblur, keyFile); + saveToKeyfile(!pedited || spot_edited->complexblur, "Locallab", "Complexblur_" + index_str, spot.complexblur, keyFile); + saveToKeyfile(!pedited || spot_edited->radius, "Locallab", "Radius_" + index_str, spot.radius, keyFile); + saveToKeyfile(!pedited || spot_edited->strength, "Locallab", "Strength_" + index_str, spot.strength, keyFile); + saveToKeyfile(!pedited || spot_edited->sensibn, "Locallab", "Sensibn_" + index_str, spot.sensibn, keyFile); + saveToKeyfile(!pedited || spot_edited->itera, "Locallab", "Iteramed_" + index_str, spot.itera, keyFile); + saveToKeyfile(!pedited || spot_edited->guidbl, "Locallab", "Guidbl_" + index_str, spot.guidbl, keyFile); + saveToKeyfile(!pedited || spot_edited->strbl, "Locallab", "Strbl_" + index_str, spot.strbl, keyFile); + saveToKeyfile(!pedited || spot_edited->isogr, "Locallab", "Isogr_" + index_str, spot.isogr, keyFile); + saveToKeyfile(!pedited || spot_edited->strengr, "Locallab", "Strengr_" + index_str, spot.strengr, keyFile); + saveToKeyfile(!pedited || spot_edited->scalegr, "Locallab", "Scalegr_" + index_str, spot.scalegr, keyFile); + saveToKeyfile(!pedited || spot_edited->epsbl, "Locallab", "Epsbl_" + index_str, spot.epsbl, keyFile); + saveToKeyfile(!pedited || spot_edited->blMethod, "Locallab", "BlMethod_" + index_str, spot.blMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->chroMethod, "Locallab", "ChroMethod_" + index_str, spot.chroMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->blurMethod, "Locallab", "BlurMethod_" + index_str, spot.blurMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->medMethod, "Locallab", "MedMethod_" + index_str, spot.medMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->activlum, "Locallab", "activlum_" + index_str, spot.activlum, keyFile); + saveToKeyfile(!pedited || spot_edited->noiselumf, "Locallab", "noiselumf_" + index_str, spot.noiselumf, keyFile); + saveToKeyfile(!pedited || spot_edited->noiselumf0, "Locallab", "noiselumf0_" + index_str, spot.noiselumf0, keyFile); + saveToKeyfile(!pedited || spot_edited->noiselumf2, "Locallab", "noiselumf2_" + index_str, spot.noiselumf2, keyFile); + saveToKeyfile(!pedited || spot_edited->noiselumc, "Locallab", "noiselumc_" + index_str, spot.noiselumc, keyFile); + saveToKeyfile(!pedited || spot_edited->noiselumdetail, "Locallab", "noiselumdetail_" + index_str, spot.noiselumdetail, keyFile); + saveToKeyfile(!pedited || spot_edited->noiselequal, "Locallab", "noiselequal_" + index_str, spot.noiselequal, keyFile); + saveToKeyfile(!pedited || spot_edited->noisechrof, "Locallab", "noisechrof_" + index_str, spot.noisechrof, keyFile); + saveToKeyfile(!pedited || spot_edited->noisechroc, "Locallab", "noisechroc_" + index_str, spot.noisechroc, keyFile); + saveToKeyfile(!pedited || spot_edited->noisechrodetail, "Locallab", "noisechrodetail_" + index_str, spot.noisechrodetail, keyFile); + saveToKeyfile(!pedited || spot_edited->adjblur, "Locallab", "Adjblur_" + index_str, spot.adjblur, keyFile); + saveToKeyfile(!pedited || spot_edited->bilateral, "Locallab", "Bilateral_" + index_str, spot.bilateral, keyFile); + saveToKeyfile(!pedited || spot_edited->sensiden, "Locallab", "Sensiden_" + index_str, spot.sensiden, keyFile); + saveToKeyfile(!pedited || spot_edited->detailthr, "Locallab", "Detailthr_" + index_str, spot.detailthr, keyFile); + saveToKeyfile(!pedited || spot_edited->locwavcurveden, "Locallab", "LocwavCurveden_" + index_str, spot.locwavcurveden, keyFile); + saveToKeyfile(!pedited || spot_edited->showmaskblMethodtyp, "Locallab", "Showmasktyp_" + index_str, spot.showmaskblMethodtyp, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskblcurve, "Locallab", "CCmaskblCurve_" + index_str, spot.CCmaskblcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskblcurve, "Locallab", "LLmaskblCurve_" + index_str, spot.LLmaskblcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskblcurve, "Locallab", "HHmaskblCurve_" + index_str, spot.HHmaskblcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->enablMask, "Locallab", "EnablMask_" + index_str, spot.enablMask, keyFile); + saveToKeyfile(!pedited || spot_edited->fftwbl, "Locallab", "Fftwbl_" + index_str, spot.fftwbl, keyFile); + saveToKeyfile(!pedited || spot_edited->toolbl, "Locallab", "Toolbl_" + index_str, spot.toolbl, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskbl, "Locallab", "Blendmaskbl_" + index_str, spot.blendmaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskbl, "Locallab", "Radmaskbl_" + index_str, spot.radmaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskbl, "Locallab", "Chromaskbl_" + index_str, spot.chromaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskbl, "Locallab", "Gammaskbl_" + index_str, spot.gammaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskbl, "Locallab", "Slomaskbl_" + index_str, spot.slomaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmaskbl, "Locallab", "Lapmaskbl_" + index_str, spot.lapmaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->shadmaskbl, "Locallab", "shadmaskbl_" + index_str, spot.shadmaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->shadmaskblsha, "Locallab", "shadmaskblsha_" + index_str, spot.shadmaskblsha, keyFile); + saveToKeyfile(!pedited || spot_edited->strumaskbl, "Locallab", "strumaskbl_" + index_str, spot.strumaskbl, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmaskblcurve, "Locallab", "LmaskblCurve_" + index_str, spot.Lmaskblcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskblcurvewav, "Locallab", "LLmaskblCurvewav_" + index_str, spot.LLmaskblcurvewav, keyFile); + saveToKeyfile(!pedited || spot_edited->csthresholdblur, "Locallab", "CSThresholdblur_" + index_str, spot.csthresholdblur.toVector(), keyFile); + } + // Tone Mapping + if ((!pedited || spot_edited->visitonemap) && spot.visitonemap) { + saveToKeyfile(!pedited || spot_edited->exptonemap, "Locallab", "Exptonemap_" + index_str, spot.exptonemap, keyFile); + saveToKeyfile(!pedited || spot_edited->complextonemap, "Locallab", "Complextonemap_" + index_str, spot.complextonemap, keyFile); + saveToKeyfile(!pedited || spot_edited->stren, "Locallab", "Stren_" + index_str, spot.stren, keyFile); + saveToKeyfile(!pedited || spot_edited->gamma, "Locallab", "Gamma_" + index_str, spot.gamma, keyFile); + saveToKeyfile(!pedited || spot_edited->estop, "Locallab", "Estop_" + index_str, spot.estop, keyFile); + saveToKeyfile(!pedited || spot_edited->scaltm, "Locallab", "Scaltm_" + index_str, spot.scaltm, keyFile); + saveToKeyfile(!pedited || spot_edited->rewei, "Locallab", "Rewei_" + index_str, spot.rewei, keyFile); + saveToKeyfile(!pedited || spot_edited->satur, "Locallab", "Satur_" + index_str, spot.satur, keyFile); + saveToKeyfile(!pedited || spot_edited->sensitm, "Locallab", "Sensitm_" + index_str, spot.sensitm, keyFile); + saveToKeyfile(!pedited || spot_edited->softradiustm, "Locallab", "Softradiustm_" + index_str, spot.softradiustm, keyFile); + saveToKeyfile(!pedited || spot_edited->amount, "Locallab", "Amount_" + index_str, spot.amount, keyFile); + saveToKeyfile(!pedited || spot_edited->equiltm, "Locallab", "Equiltm_" + index_str, spot.equiltm, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmasktmcurve, "Locallab", "CCmasktmCurve_" + index_str, spot.CCmasktmcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmasktmcurve, "Locallab", "LLmasktmCurve_" + index_str, spot.LLmasktmcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmasktmcurve, "Locallab", "HHmasktmCurve_" + index_str, spot.HHmasktmcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->enatmMask, "Locallab", "EnatmMask_" + index_str, spot.enatmMask, keyFile); + saveToKeyfile(!pedited || spot_edited->enatmMaskaft, "Locallab", "EnatmMaskaft_" + index_str, spot.enatmMaskaft, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmasktm, "Locallab", "Blendmasktm_" + index_str, spot.blendmasktm, keyFile); + saveToKeyfile(!pedited || spot_edited->radmasktm, "Locallab", "Radmasktm_" + index_str, spot.radmasktm, keyFile); + saveToKeyfile(!pedited || spot_edited->chromasktm, "Locallab", "Chromasktm_" + index_str, spot.chromasktm, keyFile); + saveToKeyfile(!pedited || spot_edited->gammasktm, "Locallab", "Gammasktm_" + index_str, spot.gammasktm, keyFile); + saveToKeyfile(!pedited || spot_edited->slomasktm, "Locallab", "Slomasktm_" + index_str, spot.slomasktm, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmasktm, "Locallab", "Lapmasktm_" + index_str, spot.lapmasktm, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmasktmcurve, "Locallab", "LmasktmCurve_" + index_str, spot.Lmasktmcurve, keyFile); + } + // Retinex + if ((!pedited || spot_edited->visireti) && spot.visireti) { + saveToKeyfile(!pedited || spot_edited->expreti, "Locallab", "Expreti_" + index_str, spot.expreti, keyFile); + saveToKeyfile(!pedited || spot_edited->complexreti, "Locallab", "Complexreti_" + index_str, spot.complexreti, keyFile); + saveToKeyfile(!pedited || spot_edited->retinexMethod, "Locallab", "retinexMethod_" + index_str, spot.retinexMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->str, "Locallab", "Str_" + index_str, spot.str, keyFile); + saveToKeyfile(!pedited || spot_edited->chrrt, "Locallab", "Chrrt_" + index_str, spot.chrrt, keyFile); + saveToKeyfile(!pedited || spot_edited->neigh, "Locallab", "Neigh_" + index_str, spot.neigh, keyFile); + saveToKeyfile(!pedited || spot_edited->vart, "Locallab", "Vart_" + index_str, spot.vart, keyFile); + saveToKeyfile(!pedited || spot_edited->offs, "Locallab", "Offs_" + index_str, spot.offs, keyFile); + saveToKeyfile(!pedited || spot_edited->dehaz, "Locallab", "Dehaz_" + index_str, spot.dehaz, keyFile); + saveToKeyfile(!pedited || spot_edited->depth, "Locallab", "Depth_" + index_str, spot.depth, keyFile); + saveToKeyfile(!pedited || spot_edited->sensih, "Locallab", "Sensih_" + index_str, spot.sensih, keyFile); + saveToKeyfile(!pedited || spot_edited->localTgaincurve, "Locallab", "TgainCurve_" + index_str, spot.localTgaincurve, keyFile); + saveToKeyfile(!pedited || spot_edited->localTtranscurve, "Locallab", "TtransCurve_" + index_str, spot.localTtranscurve, keyFile); + saveToKeyfile(!pedited || spot_edited->inversret, "Locallab", "Inversret_" + index_str, spot.inversret, keyFile); + saveToKeyfile(!pedited || spot_edited->equilret, "Locallab", "Equilret_" + index_str, spot.equilret, keyFile); + saveToKeyfile(!pedited || spot_edited->loglin, "Locallab", "Loglin_" + index_str, spot.loglin, keyFile); + saveToKeyfile(!pedited || spot_edited->lumonly, "Locallab", "Lumonly_" + index_str, spot.lumonly, keyFile); + saveToKeyfile(!pedited || spot_edited->softradiusret, "Locallab", "Softradiusret_" + index_str, spot.softradiusret, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskreticurve, "Locallab", "CCmaskretiCurve_" + index_str, spot.CCmaskreticurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskreticurve, "Locallab", "LLmaskretiCurve_" + index_str, spot.LLmaskreticurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskreticurve, "Locallab", "HHmaskretiCurve_" + index_str, spot.HHmaskreticurve, keyFile); + saveToKeyfile(!pedited || spot_edited->enaretiMask, "Locallab", "EnaretiMask_" + index_str, spot.enaretiMask, keyFile); + saveToKeyfile(!pedited || spot_edited->enaretiMasktmap, "Locallab", "EnaretiMasktmap_" + index_str, spot.enaretiMasktmap, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskreti, "Locallab", "Blendmaskreti_" + index_str, spot.blendmaskreti, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskreti, "Locallab", "Radmaskreti_" + index_str, spot.radmaskreti, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskreti, "Locallab", "Chromaskreti_" + index_str, spot.chromaskreti, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskreti, "Locallab", "Gammaskreti_" + index_str, spot.gammaskreti, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskreti, "Locallab", "Slomaskreti_" + index_str, spot.slomaskreti, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmaskreti, "Locallab", "Lapmaskreti_" + index_str, spot.lapmaskreti, keyFile); + saveToKeyfile(!pedited || spot_edited->scalereti, "Locallab", "Scalereti_" + index_str, spot.scalereti, keyFile); + saveToKeyfile(!pedited || spot_edited->darkness, "Locallab", "Darkness_" + index_str, spot.darkness, keyFile); + saveToKeyfile(!pedited || spot_edited->lightnessreti, "Locallab", "Lightnessreti_" + index_str, spot.lightnessreti, keyFile); + saveToKeyfile(!pedited || spot_edited->limd, "Locallab", "Limd_" + index_str, spot.limd, keyFile); + saveToKeyfile(!pedited || spot_edited->cliptm, "Locallab", "Cliptm_" + index_str, spot.cliptm, keyFile); + saveToKeyfile(!pedited || spot_edited->fftwreti, "Locallab", "Fftwreti_" + index_str, spot.fftwreti, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmaskreticurve, "Locallab", "LmaskretiCurve_" + index_str, spot.Lmaskreticurve, keyFile); + } + // Sharpening + if ((!pedited || spot_edited->visisharp) && spot.visisharp) { + saveToKeyfile(!pedited || spot_edited->expsharp, "Locallab", "Expsharp_" + index_str, spot.expsharp, keyFile); + saveToKeyfile(!pedited || spot_edited->complexsharp, "Locallab", "Complexsharp_" + index_str, spot.complexsharp, keyFile); + saveToKeyfile(!pedited || spot_edited->sharcontrast, "Locallab", "Sharcontrast_" + index_str, spot.sharcontrast, keyFile); + saveToKeyfile(!pedited || spot_edited->sharradius, "Locallab", "Sharradius_" + index_str, spot.sharradius, keyFile); + saveToKeyfile(!pedited || spot_edited->sharamount, "Locallab", "Sharamount_" + index_str, spot.sharamount, keyFile); + saveToKeyfile(!pedited || spot_edited->shardamping, "Locallab", "Shardamping_" + index_str, spot.shardamping, keyFile); + saveToKeyfile(!pedited || spot_edited->shariter, "Locallab", "Shariter_" + index_str, spot.shariter, keyFile); + saveToKeyfile(!pedited || spot_edited->sharblur, "Locallab", "Sharblur_" + index_str, spot.sharblur, keyFile); + saveToKeyfile(!pedited || spot_edited->sensisha, "Locallab", "Sensisha_" + index_str, spot.sensisha, keyFile); + saveToKeyfile(!pedited || spot_edited->inverssha, "Locallab", "Inverssha_" + index_str, spot.inverssha, keyFile); + } + // Local Contrast + if ((!pedited || spot_edited->visicontrast) && spot.visicontrast) { + saveToKeyfile(!pedited || spot_edited->expcontrast, "Locallab", "Expcontrast_" + index_str, spot.expcontrast, keyFile); + saveToKeyfile(!pedited || spot_edited->complexcontrast, "Locallab", "Complexcontrast_" + index_str, spot.complexcontrast, keyFile); + saveToKeyfile(!pedited || spot_edited->lcradius, "Locallab", "Lcradius_" + index_str, spot.lcradius, keyFile); + saveToKeyfile(!pedited || spot_edited->lcamount, "Locallab", "Lcamount_" + index_str, spot.lcamount, keyFile); + saveToKeyfile(!pedited || spot_edited->lcdarkness, "Locallab", "Lcdarkness_" + index_str, spot.lcdarkness, keyFile); + saveToKeyfile(!pedited || spot_edited->lclightness, "Locallab", "Lclightness_" + index_str, spot.lclightness, keyFile); + saveToKeyfile(!pedited || spot_edited->sigmalc, "Locallab", "Sigmalc_" + index_str, spot.sigmalc, keyFile); + saveToKeyfile(!pedited || spot_edited->levelwav, "Locallab", "Levelwav_" + index_str, spot.levelwav, keyFile); + saveToKeyfile(!pedited || spot_edited->residcont, "Locallab", "Residcont_" + index_str, spot.residcont, keyFile); + saveToKeyfile(!pedited || spot_edited->residsha, "Locallab", "Residsha_" + index_str, spot.residsha, keyFile); + saveToKeyfile(!pedited || spot_edited->residshathr, "Locallab", "Residshathr_" + index_str, spot.residshathr, keyFile); + saveToKeyfile(!pedited || spot_edited->residhi, "Locallab", "Residhi_" + index_str, spot.residhi, keyFile); + saveToKeyfile(!pedited || spot_edited->residhithr, "Locallab", "Residhithr_" + index_str, spot.residhithr, keyFile); + saveToKeyfile(!pedited || spot_edited->residblur, "Locallab", "Residblur_" + index_str, spot.residblur, keyFile); + saveToKeyfile(!pedited || spot_edited->levelblur, "Locallab", "Levelblur_" + index_str, spot.levelblur, keyFile); + saveToKeyfile(!pedited || spot_edited->sigmabl, "Locallab", "Sigmabl_" + index_str, spot.sigmabl, keyFile); + saveToKeyfile(!pedited || spot_edited->residchro, "Locallab", "Residchro_" + index_str, spot.residchro, keyFile); + saveToKeyfile(!pedited || spot_edited->residcomp, "Locallab", "Residcomp_" + index_str, spot.residcomp, keyFile); + saveToKeyfile(!pedited || spot_edited->sigma, "Locallab", "Sigma_" + index_str, spot.sigma, keyFile); + saveToKeyfile(!pedited || spot_edited->offset, "Locallab", "Offset_" + index_str, spot.offset, keyFile); + saveToKeyfile(!pedited || spot_edited->sigmadr, "Locallab", "Sigmadr_" + index_str, spot.sigmadr, keyFile); + saveToKeyfile(!pedited || spot_edited->threswav, "Locallab", "Threswav_" + index_str, spot.threswav, keyFile); + saveToKeyfile(!pedited || spot_edited->chromalev, "Locallab", "Chromalev_" + index_str, spot.chromalev, keyFile); + saveToKeyfile(!pedited || spot_edited->chromablu, "Locallab", "Chromablu_" + index_str, spot.chromablu, keyFile); + saveToKeyfile(!pedited || spot_edited->sigmadc, "Locallab", "sigmadc_" + index_str, spot.sigmadc, keyFile); + saveToKeyfile(!pedited || spot_edited->deltad, "Locallab", "deltad_" + index_str, spot.deltad, keyFile); + saveToKeyfile(!pedited || spot_edited->fatres, "Locallab", "Fatres_" + index_str, spot.fatres, keyFile); + saveToKeyfile(!pedited || spot_edited->clarilres, "Locallab", "ClariLres_" + index_str, spot.clarilres, keyFile); + saveToKeyfile(!pedited || spot_edited->claricres, "Locallab", "ClariCres_" + index_str, spot.claricres, keyFile); + saveToKeyfile(!pedited || spot_edited->clarisoft, "Locallab", "Clarisoft_" + index_str, spot.clarisoft, keyFile); + saveToKeyfile(!pedited || spot_edited->sigmalc2, "Locallab", "Sigmalc2_" + index_str, spot.sigmalc2, keyFile); + saveToKeyfile(!pedited || spot_edited->strwav, "Locallab", "Strwav_" + index_str, spot.strwav, keyFile); + saveToKeyfile(!pedited || spot_edited->angwav, "Locallab", "Angwav_" + index_str, spot.angwav, keyFile); + saveToKeyfile(!pedited || spot_edited->strengthw, "Locallab", "Strengthw_" + index_str, spot.strengthw, keyFile); + saveToKeyfile(!pedited || spot_edited->sigmaed, "Locallab", "Sigmaed_" + index_str, spot.sigmaed, keyFile); + saveToKeyfile(!pedited || spot_edited->radiusw, "Locallab", "Radiusw_" + index_str, spot.radiusw, keyFile); + saveToKeyfile(!pedited || spot_edited->detailw, "Locallab", "Detailw_" + index_str, spot.detailw, keyFile); + saveToKeyfile(!pedited || spot_edited->gradw, "Locallab", "Gradw_" + index_str, spot.gradw, keyFile); + saveToKeyfile(!pedited || spot_edited->tloww, "Locallab", "Tloww_" + index_str, spot.tloww, keyFile); + saveToKeyfile(!pedited || spot_edited->thigw, "Locallab", "Thigw_" + index_str, spot.thigw, keyFile); + saveToKeyfile(!pedited || spot_edited->edgw, "Locallab", "Edgw_" + index_str, spot.edgw, keyFile); + saveToKeyfile(!pedited || spot_edited->basew, "Locallab", "Basew_" + index_str, spot.basew, keyFile); + saveToKeyfile(!pedited || spot_edited->sensilc, "Locallab", "Sensilc_" + index_str, spot.sensilc, keyFile); + saveToKeyfile(!pedited || spot_edited->fftwlc, "Locallab", "Fftwlc_" + index_str, spot.fftwlc, keyFile); + saveToKeyfile(!pedited || spot_edited->blurlc, "Locallab", "Blurlc_" + index_str, spot.blurlc, keyFile); + saveToKeyfile(!pedited || spot_edited->wavblur, "Locallab", "Wavblur_" + index_str, spot.wavblur, keyFile); + saveToKeyfile(!pedited || spot_edited->wavedg, "Locallab", "Wavedg_" + index_str, spot.wavedg, keyFile); + saveToKeyfile(!pedited || spot_edited->waveshow, "Locallab", "Waveshow_" + index_str, spot.waveshow, keyFile); + saveToKeyfile(!pedited || spot_edited->wavcont, "Locallab", "Wavcont_" + index_str, spot.wavcont, keyFile); + saveToKeyfile(!pedited || spot_edited->wavcomp, "Locallab", "Wavcomp_" + index_str, spot.wavcomp, keyFile); + saveToKeyfile(!pedited || spot_edited->wavgradl, "Locallab", "Wavgradl_" + index_str, spot.wavgradl, keyFile); + saveToKeyfile(!pedited || spot_edited->wavcompre, "Locallab", "Wavcompre_" + index_str, spot.wavcompre, keyFile); + saveToKeyfile(!pedited || spot_edited->origlc, "Locallab", "Origlc_" + index_str, spot.origlc, keyFile); + saveToKeyfile(!pedited || spot_edited->localcontMethod, "Locallab", "localcontMethod_" + index_str, spot.localcontMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->localedgMethod, "Locallab", "localedgMethod_" + index_str, spot.localedgMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->localneiMethod, "Locallab", "localneiMethod_" + index_str, spot.localneiMethod, keyFile); + saveToKeyfile(!pedited || spot_edited->locwavcurve, "Locallab", "LocwavCurve_" + index_str, spot.locwavcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->csthreshold, "Locallab", "CSThreshold_" + index_str, spot.csthreshold.toVector(), keyFile); + saveToKeyfile(!pedited || spot_edited->loclevwavcurve, "Locallab", "LoclevwavCurve_" + index_str, spot.loclevwavcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->locconwavcurve, "Locallab", "LocconwavCurve_" + index_str, spot.locconwavcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->loccompwavcurve, "Locallab", "LoccompwavCurve_" + index_str, spot.loccompwavcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->loccomprewavcurve, "Locallab", "LoccomprewavCurve_" + index_str, spot.loccomprewavcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->locedgwavcurve, "Locallab", "LocedgwavCurve_" + index_str, spot.locedgwavcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmasklccurve, "Locallab", "CCmasklcCurve_" + index_str, spot.CCmasklccurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmasklccurve, "Locallab", "LLmasklcCurve_" + index_str, spot.LLmasklccurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmasklccurve, "Locallab", "HHmasklcCurve_" + index_str, spot.HHmasklccurve, keyFile); + saveToKeyfile(!pedited || spot_edited->enalcMask, "Locallab", "EnalcMask_" + index_str, spot.enalcMask, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmasklc, "Locallab", "Blendmasklc_" + index_str, spot.blendmasklc, keyFile); + saveToKeyfile(!pedited || spot_edited->radmasklc, "Locallab", "Radmasklc_" + index_str, spot.radmasklc, keyFile); + saveToKeyfile(!pedited || spot_edited->chromasklc, "Locallab", "Chromasklc_" + index_str, spot.chromasklc, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmasklccurve, "Locallab", "LmasklcCurve_" + index_str, spot.Lmasklccurve, keyFile); + } + // Contrast by detail levels + if ((!pedited || spot_edited->visicbdl) && spot.visicbdl) { + saveToKeyfile(!pedited || spot_edited->expcbdl, "Locallab", "Expcbdl_" + index_str, spot.expcbdl, keyFile); + saveToKeyfile(!pedited || spot_edited->complexcbdl, "Locallab", "Complexcbdl_" + index_str, spot.complexcbdl, keyFile); + + for (int j = 0; j < 6; j++) { + saveToKeyfile(!pedited || spot_edited->mult[j], "Locallab", "Mult" + std::to_string(j) + "_" + index_str, spot.mult[j], keyFile); + } + + saveToKeyfile(!pedited || spot_edited->chromacbdl, "Locallab", "Chromacbdl_" + index_str, spot.chromacbdl, keyFile); + saveToKeyfile(!pedited || spot_edited->threshold, "Locallab", "Threshold_" + index_str, spot.threshold, keyFile); + saveToKeyfile(!pedited || spot_edited->sensicb, "Locallab", "Sensicb_" + index_str, spot.sensicb, keyFile); + saveToKeyfile(!pedited || spot_edited->clarityml, "Locallab", "Clarityml_" + index_str, spot.clarityml, keyFile); + saveToKeyfile(!pedited || spot_edited->contresid, "Locallab", "Contresid_" + index_str, spot.contresid, keyFile); + saveToKeyfile(!pedited || spot_edited->blurcbdl, "Locallab", "Blurcbdl_" + index_str, spot.blurcbdl, keyFile); + saveToKeyfile(!pedited || spot_edited->softradiuscb, "Locallab", "Softradiuscb_" + index_str, spot.softradiuscb, keyFile); + saveToKeyfile(!pedited || spot_edited->enacbMask, "Locallab", "EnacbMask_" + index_str, spot.enacbMask, keyFile); + saveToKeyfile(!pedited || spot_edited->CCmaskcbcurve, "Locallab", "CCmaskcbCurve_" + index_str, spot.CCmaskcbcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->LLmaskcbcurve, "Locallab", "LLmaskcbCurve_" + index_str, spot.LLmaskcbcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->HHmaskcbcurve, "Locallab", "HHmaskcbCurve_" + index_str, spot.HHmaskcbcurve, keyFile); + saveToKeyfile(!pedited || spot_edited->blendmaskcb, "Locallab", "Blendmaskcb_" + index_str, spot.blendmaskcb, keyFile); + saveToKeyfile(!pedited || spot_edited->radmaskcb, "Locallab", "Radmaskcb_" + index_str, spot.radmaskcb, keyFile); + saveToKeyfile(!pedited || spot_edited->chromaskcb, "Locallab", "Chromaskcb_" + index_str, spot.chromaskcb, keyFile); + saveToKeyfile(!pedited || spot_edited->gammaskcb, "Locallab", "Gammaskcb_" + index_str, spot.gammaskcb, keyFile); + saveToKeyfile(!pedited || spot_edited->slomaskcb, "Locallab", "Slomaskcb_" + index_str, spot.slomaskcb, keyFile); + saveToKeyfile(!pedited || spot_edited->lapmaskcb, "Locallab", "Lapmaskcb_" + index_str, spot.lapmaskcb, keyFile); + saveToKeyfile(!pedited || spot_edited->Lmaskcbcurve, "Locallab", "LmaskcbCurve_" + index_str, spot.Lmaskcbcurve, keyFile); + } + // Log encoding + if ((!pedited || spot_edited->visilog) && spot.visilog) { + saveToKeyfile(!pedited || spot_edited->explog, "Locallab", "Explog_" + index_str, spot.explog, keyFile); + saveToKeyfile(!pedited || spot_edited->autocompute, "Locallab", "Autocompute_" + index_str, spot.autocompute, keyFile); + saveToKeyfile(!pedited || spot_edited->sourceGray, "Locallab", "SourceGray_" + index_str, spot.sourceGray, keyFile); + saveToKeyfile(!pedited || spot_edited->targetGray, "Locallab", "TargetGray_" + index_str, spot.targetGray, keyFile); + saveToKeyfile(!pedited || spot_edited->Autogray, "Locallab", "Autogray_" + index_str, spot.Autogray, keyFile); + saveToKeyfile(!pedited || spot_edited->fullimage, "Locallab", "Fullimage_" + index_str, spot.fullimage, keyFile); + saveToKeyfile(!pedited || spot_edited->blackEv, "Locallab", "BlackEv_" + index_str, spot.blackEv, keyFile); + saveToKeyfile(!pedited || spot_edited->whiteEv, "Locallab", "WhiteEv_" + index_str, spot.whiteEv, keyFile); + saveToKeyfile(!pedited || spot_edited->detail, "Locallab", "Detail_" + index_str, spot.detail, keyFile); + saveToKeyfile(!pedited || spot_edited->sensilog, "Locallab", "Sensilog_" + index_str, spot.sensilog, keyFile); + saveToKeyfile(!pedited || spot_edited->baselog, "Locallab", "Baselog_" + index_str, spot.baselog, keyFile); + saveToKeyfile(!pedited || spot_edited->strlog, "Locallab", "Strlog_" + index_str, spot.strlog, keyFile); + saveToKeyfile(!pedited || spot_edited->anglog, "Locallab", "Anglog_" + index_str, spot.anglog, keyFile); + } + } + } + // Post-crop vignette saveToKeyfile(!pedited || pedited->pcvignette.enabled, "PCVignette", "Enabled", pcvignette.enabled, keyFile); saveToKeyfile(!pedited || pedited->pcvignette.strength, "PCVignette", "Strength", pcvignette.strength, keyFile); @@ -4219,14 +6417,17 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) if (keyFile.has_group("Sharpening")) { assignFromKeyfile(keyFile, "Sharpening", "Enabled", pedited, sharpening.enabled, pedited->sharpening.enabled); + if (ppVersion >= 334) { assignFromKeyfile(keyFile, "Sharpening", "Contrast", pedited, sharpening.contrast, pedited->sharpening.contrast); } else { sharpening.contrast = 0; + if (pedited) { pedited->sharpening.contrast = true; } } + assignFromKeyfile(keyFile, "Sharpening", "Radius", pedited, sharpening.radius, pedited->sharpening.radius); assignFromKeyfile(keyFile, "Sharpening", "BlurRadius", pedited, sharpening.blurradius, pedited->sharpening.blurradius); assignFromKeyfile(keyFile, "Sharpening", "Amount", pedited, sharpening.amount, pedited->sharpening.amount); @@ -4271,10 +6472,12 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "SharpenMicro", "Enabled", pedited, sharpenMicro.enabled, pedited->sharpenMicro.enabled); assignFromKeyfile(keyFile, "SharpenMicro", "Matrix", pedited, sharpenMicro.matrix, pedited->sharpenMicro.matrix); assignFromKeyfile(keyFile, "SharpenMicro", "Strength", pedited, sharpenMicro.amount, pedited->sharpenMicro.amount); + if (ppVersion >= 334) { assignFromKeyfile(keyFile, "SharpenMicro", "Contrast", pedited, sharpenMicro.contrast, pedited->sharpenMicro.contrast); } else { sharpenMicro.contrast = 0; + if (pedited) { pedited->sharpenMicro.contrast = true; } @@ -4471,7 +6674,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "FattalToneMapping", "Anchor", pedited, fattal.anchor, pedited->fattal.anchor); } - if (keyFile.has_group ("Shadows & Highlights") && ppVersion >= 333) { + if (keyFile.has_group("Shadows & Highlights") && ppVersion >= 333) { assignFromKeyfile(keyFile, "Shadows & Highlights", "Enabled", pedited, sh.enabled, pedited->sh.enabled); assignFromKeyfile(keyFile, "Shadows & Highlights", "Highlights", pedited, sh.highlights, pedited->sh.highlights); assignFromKeyfile(keyFile, "Shadows & Highlights", "HighlightTonalWidth", pedited, sh.htonalwidth, pedited->sh.htonalwidth); @@ -4622,8 +6825,21 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } if (keyFile.has_group("Perspective")) { + assignFromKeyfile(keyFile, "Perspective", "Method", pedited, perspective.method, pedited->perspective.method); assignFromKeyfile(keyFile, "Perspective", "Horizontal", pedited, perspective.horizontal, pedited->perspective.horizontal); assignFromKeyfile(keyFile, "Perspective", "Vertical", pedited, perspective.vertical, pedited->perspective.vertical); + assignFromKeyfile(keyFile, "Perspective", "CameraShiftHorizontal", pedited, perspective.camera_shift_horiz, pedited->perspective.camera_shift_horiz); + assignFromKeyfile(keyFile, "Perspective", "CameraShiftVertical", pedited, perspective.camera_shift_vert, pedited->perspective.camera_shift_vert); + assignFromKeyfile(keyFile, "Perspective", "CameraPitch", pedited, perspective.camera_pitch, pedited->perspective.camera_pitch); + assignFromKeyfile(keyFile, "Perspective", "CameraRoll", pedited, perspective.camera_roll, pedited->perspective.camera_roll); + assignFromKeyfile(keyFile, "Perspective", "CameraCropFactor", pedited, perspective.camera_crop_factor, pedited->perspective.camera_crop_factor); + assignFromKeyfile(keyFile, "Perspective", "CameraFocalLength", pedited, perspective.camera_focal_length, pedited->perspective.camera_focal_length); + assignFromKeyfile(keyFile, "Perspective", "CameraYaw", pedited, perspective.camera_yaw, pedited->perspective.camera_yaw); + assignFromKeyfile(keyFile, "Perspective", "ProjectionPitch", pedited, perspective.projection_pitch, pedited->perspective.projection_pitch); + assignFromKeyfile(keyFile, "Perspective", "ProjectionRotate", pedited, perspective.projection_rotate, pedited->perspective.projection_rotate); + assignFromKeyfile(keyFile, "Perspective", "ProjectionShiftHorizontal", pedited, perspective.projection_shift_horiz, pedited->perspective.projection_shift_horiz); + assignFromKeyfile(keyFile, "Perspective", "ProjectionShiftVertical", pedited, perspective.projection_shift_vert, pedited->perspective.projection_shift_vert); + assignFromKeyfile(keyFile, "Perspective", "ProjectionYaw", pedited, perspective.projection_yaw, pedited->perspective.projection_yaw); } if (keyFile.has_group("Gradient")) { @@ -4635,6 +6851,580 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Gradient", "CenterY", pedited, gradient.centerY, pedited->gradient.centerY); } + if (keyFile.has_group("Locallab")) { + assignFromKeyfile(keyFile, "Locallab", "Enabled", pedited, locallab.enabled, pedited->locallab.enabled); + assignFromKeyfile(keyFile, "Locallab", "Selspot", pedited, locallab.selspot, pedited->locallab.selspot); + + Glib::ustring ppName; + bool peName; + int i = 0; + + while (assignFromKeyfile(keyFile, "Locallab", "Name_" + std::to_string(i), pedited, ppName, peName)) { + const std::string index_str = std::to_string(i); + + // Create new LocallabSpot and LocallabParamsEdited + LocallabParams::LocallabSpot spot; + spot.name = ppName; + LocallabParamsEdited::LocallabSpotEdited spotEdited(false); + spotEdited.name = peName; + + // Control spot settings + assignFromKeyfile(keyFile, "Locallab", "Isvisible_" + index_str, pedited, spot.isvisible, spotEdited.isvisible); + assignFromKeyfile(keyFile, "Locallab", "Shape_" + index_str, pedited, spot.shape, spotEdited.shape); + assignFromKeyfile(keyFile, "Locallab", "SpotMethod_" + index_str, pedited, spot.spotMethod, spotEdited.spotMethod); + assignFromKeyfile(keyFile, "Locallab", "wavMethod_" + index_str, pedited, spot.wavMethod, spotEdited.wavMethod); + assignFromKeyfile(keyFile, "Locallab", "SensiExclu_" + index_str, pedited, spot.sensiexclu, spotEdited.sensiexclu); + assignFromKeyfile(keyFile, "Locallab", "StructExclu_" + index_str, pedited, spot.structexclu, spotEdited.structexclu); + assignFromKeyfile(keyFile, "Locallab", "Struc_" + index_str, pedited, spot.struc, spotEdited.struc); + assignFromKeyfile(keyFile, "Locallab", "ShapeMethod_" + index_str, pedited, spot.shapeMethod, spotEdited.shapeMethod); + assignFromKeyfile(keyFile, "Locallab", "Loc_" + index_str, pedited, spot.loc, spotEdited.loc); + assignFromKeyfile(keyFile, "Locallab", "CenterX_" + index_str, pedited, spot.centerX, spotEdited.centerX); + assignFromKeyfile(keyFile, "Locallab", "CenterY_" + index_str, pedited, spot.centerY, spotEdited.centerY); + assignFromKeyfile(keyFile, "Locallab", "Circrad_" + index_str, pedited, spot.circrad, spotEdited.circrad); + assignFromKeyfile(keyFile, "Locallab", "QualityMethod_" + index_str, pedited, spot.qualityMethod, spotEdited.qualityMethod); + assignFromKeyfile(keyFile, "Locallab", "ComplexMethod_" + index_str, pedited, spot.complexMethod, spotEdited.complexMethod); + assignFromKeyfile(keyFile, "Locallab", "Transit_" + index_str, pedited, spot.transit, spotEdited.transit); + assignFromKeyfile(keyFile, "Locallab", "Feather_" + index_str, pedited, spot.feather, spotEdited.feather); + assignFromKeyfile(keyFile, "Locallab", "Thresh_" + index_str, pedited, spot.thresh, spotEdited.thresh); + assignFromKeyfile(keyFile, "Locallab", "Iter_" + index_str, pedited, spot.iter, spotEdited.iter); + assignFromKeyfile(keyFile, "Locallab", "Balan_" + index_str, pedited, spot.balan, spotEdited.balan); + assignFromKeyfile(keyFile, "Locallab", "Balanh_" + index_str, pedited, spot.balanh, spotEdited.balanh); + assignFromKeyfile(keyFile, "Locallab", "Colorde_" + index_str, pedited, spot.colorde, spotEdited.colorde); + assignFromKeyfile(keyFile, "Locallab", "Colorscope_" + index_str, pedited, spot.colorscope, spotEdited.colorscope); + assignFromKeyfile(keyFile, "Locallab", "Transitweak_" + index_str, pedited, spot.transitweak, spotEdited.transitweak); + assignFromKeyfile(keyFile, "Locallab", "Transitgrad_" + index_str, pedited, spot.transitgrad, spotEdited.transitgrad); + assignFromKeyfile(keyFile, "Locallab", "Avoid_" + index_str, pedited, spot.avoid, spotEdited.avoid); + assignFromKeyfile(keyFile, "Locallab", "Blwh_" + index_str, pedited, spot.blwh, spotEdited.blwh); + assignFromKeyfile(keyFile, "Locallab", "Recurs_" + index_str, pedited, spot.recurs, spotEdited.recurs); + assignFromKeyfile(keyFile, "Locallab", "Laplac_" + index_str, pedited, spot.laplac, spotEdited.laplac); + assignFromKeyfile(keyFile, "Locallab", "Deltae_" + index_str, pedited, spot.deltae, spotEdited.deltae); + assignFromKeyfile(keyFile, "Locallab", "Shortc_" + index_str, pedited, spot.shortc, spotEdited.shortc); + assignFromKeyfile(keyFile, "Locallab", "Savrest_" + index_str, pedited, spot.savrest, spotEdited.savrest); + assignFromKeyfile(keyFile, "Locallab", "Scopemask_" + index_str, pedited, spot.scopemask, spotEdited.scopemask); + assignFromKeyfile(keyFile, "Locallab", "Lumask_" + index_str, pedited, spot.lumask, spotEdited.lumask); + // Color & Light + spot.visicolor = assignFromKeyfile(keyFile, "Locallab", "Expcolor_" + index_str, pedited, spot.expcolor, spotEdited.expcolor); + + if (spot.visicolor) { + spotEdited.visicolor = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexcolor_" + index_str, pedited, spot.complexcolor, spotEdited.complexcolor); + assignFromKeyfile(keyFile, "Locallab", "Curvactiv_" + index_str, pedited, spot.curvactiv, spotEdited.curvactiv); + assignFromKeyfile(keyFile, "Locallab", "Lightness_" + index_str, pedited, spot.lightness, spotEdited.lightness); + assignFromKeyfile(keyFile, "Locallab", "Contrast_" + index_str, pedited, spot.contrast, spotEdited.contrast); + assignFromKeyfile(keyFile, "Locallab", "Chroma_" + index_str, pedited, spot.chroma, spotEdited.chroma); + assignFromKeyfile(keyFile, "Locallab", "labgridALow_" + index_str, pedited, spot.labgridALow, spotEdited.labgridALow); + assignFromKeyfile(keyFile, "Locallab", "labgridBLow_" + index_str, pedited, spot.labgridBLow, spotEdited.labgridBLow); + assignFromKeyfile(keyFile, "Locallab", "labgridAHigh_" + index_str, pedited, spot.labgridAHigh, spotEdited.labgridAHigh); + assignFromKeyfile(keyFile, "Locallab", "labgridBHigh_" + index_str, pedited, spot.labgridBHigh, spotEdited.labgridBHigh); + assignFromKeyfile(keyFile, "Locallab", "labgridALowmerg_" + index_str, pedited, spot.labgridALowmerg, spotEdited.labgridALowmerg); + assignFromKeyfile(keyFile, "Locallab", "labgridBLowmerg_" + index_str, pedited, spot.labgridBLowmerg, spotEdited.labgridBLowmerg); + assignFromKeyfile(keyFile, "Locallab", "labgridAHighmerg_" + index_str, pedited, spot.labgridAHighmerg, spotEdited.labgridAHighmerg); + assignFromKeyfile(keyFile, "Locallab", "labgridBHighmerg_" + index_str, pedited, spot.labgridBHighmerg, spotEdited.labgridBHighmerg); + assignFromKeyfile(keyFile, "Locallab", "Strengthgrid_" + index_str, pedited, spot.strengthgrid, spotEdited.strengthgrid); + assignFromKeyfile(keyFile, "Locallab", "Sensi_" + index_str, pedited, spot.sensi, spotEdited.sensi); + assignFromKeyfile(keyFile, "Locallab", "Structcol_" + index_str, pedited, spot.structcol, spotEdited.structcol); + assignFromKeyfile(keyFile, "Locallab", "Strcol_" + index_str, pedited, spot.strcol, spotEdited.strcol); + assignFromKeyfile(keyFile, "Locallab", "Strcolab_" + index_str, pedited, spot.strcolab, spotEdited.strcolab); + assignFromKeyfile(keyFile, "Locallab", "Strcolh_" + index_str, pedited, spot.strcolh, spotEdited.strcolh); + assignFromKeyfile(keyFile, "Locallab", "Angcol_" + index_str, pedited, spot.angcol, spotEdited.angcol); + assignFromKeyfile(keyFile, "Locallab", "Blurcolde_" + index_str, pedited, spot.blurcolde, spotEdited.blurcolde); + assignFromKeyfile(keyFile, "Locallab", "Blurcol_" + index_str, pedited, spot.blurcol, spotEdited.blurcol); + assignFromKeyfile(keyFile, "Locallab", "Contcol_" + index_str, pedited, spot.contcol, spotEdited.contcol); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskcol_" + index_str, pedited, spot.blendmaskcol, spotEdited.blendmaskcol); + assignFromKeyfile(keyFile, "Locallab", "Radmaskcol_" + index_str, pedited, spot.radmaskcol, spotEdited.radmaskcol); + assignFromKeyfile(keyFile, "Locallab", "Chromaskcol_" + index_str, pedited, spot.chromaskcol, spotEdited.chromaskcol); + assignFromKeyfile(keyFile, "Locallab", "Gammaskcol_" + index_str, pedited, spot.gammaskcol, spotEdited.gammaskcol); + assignFromKeyfile(keyFile, "Locallab", "Slomaskcol_" + index_str, pedited, spot.slomaskcol, spotEdited.slomaskcol); + assignFromKeyfile(keyFile, "Locallab", "shadmaskcol_" + index_str, pedited, spot.shadmaskcol, spotEdited.shadmaskcol); + assignFromKeyfile(keyFile, "Locallab", "strumaskcol_" + index_str, pedited, spot.strumaskcol, spotEdited.strumaskcol); + assignFromKeyfile(keyFile, "Locallab", "Lapmaskcol_" + index_str, pedited, spot.lapmaskcol, spotEdited.lapmaskcol); + assignFromKeyfile(keyFile, "Locallab", "QualityCurveMethod_" + index_str, pedited, spot.qualitycurveMethod, spotEdited.qualitycurveMethod); + assignFromKeyfile(keyFile, "Locallab", "gridMethod_" + index_str, pedited, spot.gridMethod, spotEdited.gridMethod); + assignFromKeyfile(keyFile, "Locallab", "Merg_Method_" + index_str, pedited, spot.merMethod, spotEdited.merMethod); + assignFromKeyfile(keyFile, "Locallab", "ToneMethod_" + index_str, pedited, spot.toneMethod, spotEdited.toneMethod); + assignFromKeyfile(keyFile, "Locallab", "mergecolMethod_" + index_str, pedited, spot.mergecolMethod, spotEdited.mergecolMethod); + assignFromKeyfile(keyFile, "Locallab", "LLCurve_" + index_str, pedited, spot.llcurve, spotEdited.llcurve); + assignFromKeyfile(keyFile, "Locallab", "LCCurve_" + index_str, pedited, spot.lccurve, spotEdited.lccurve); + assignFromKeyfile(keyFile, "Locallab", "CCCurve_" + index_str, pedited, spot.cccurve, spotEdited.cccurve); + assignFromKeyfile(keyFile, "Locallab", "CLCurve_" + index_str, pedited, spot.clcurve, spotEdited.clcurve); + assignFromKeyfile(keyFile, "Locallab", "RGBCurve_" + index_str, pedited, spot.rgbcurve, spotEdited.rgbcurve); + assignFromKeyfile(keyFile, "Locallab", "LHCurve_" + index_str, pedited, spot.LHcurve, spotEdited.LHcurve); + assignFromKeyfile(keyFile, "Locallab", "HHCurve_" + index_str, pedited, spot.HHcurve, spotEdited.HHcurve); + assignFromKeyfile(keyFile, "Locallab", "Invers_" + index_str, pedited, spot.invers, spotEdited.invers); + assignFromKeyfile(keyFile, "Locallab", "Special_" + index_str, pedited, spot.special, spotEdited.special); + assignFromKeyfile(keyFile, "Locallab", "Toolcol_" + index_str, pedited, spot.toolcol, spotEdited.toolcol); + assignFromKeyfile(keyFile, "Locallab", "EnaColorMask_" + index_str, pedited, spot.enaColorMask, spotEdited.enaColorMask); + assignFromKeyfile(keyFile, "Locallab", "FftColorMask_" + index_str, pedited, spot.fftColorMask, spotEdited.fftColorMask); + assignFromKeyfile(keyFile, "Locallab", "CCmaskCurve_" + index_str, pedited, spot.CCmaskcurve, spotEdited.CCmaskcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskCurve_" + index_str, pedited, spot.LLmaskcurve, spotEdited.LLmaskcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskCurve_" + index_str, pedited, spot.HHmaskcurve, spotEdited.HHmaskcurve); + assignFromKeyfile(keyFile, "Locallab", "HHhmaskCurve_" + index_str, pedited, spot.HHhmaskcurve, spotEdited.HHhmaskcurve); + assignFromKeyfile(keyFile, "Locallab", "Softradiuscol_" + index_str, pedited, spot.softradiuscol, spotEdited.softradiuscol); + assignFromKeyfile(keyFile, "Locallab", "Opacol_" + index_str, pedited, spot.opacol, spotEdited.opacol); + assignFromKeyfile(keyFile, "Locallab", "Mercol_" + index_str, pedited, spot.mercol, spotEdited.mercol); + assignFromKeyfile(keyFile, "Locallab", "Merlucol_" + index_str, pedited, spot.merlucol, spotEdited.merlucol); + assignFromKeyfile(keyFile, "Locallab", "Conthrcol_" + index_str, pedited, spot.conthrcol, spotEdited.conthrcol); + assignFromKeyfile(keyFile, "Locallab", "LmaskCurve_" + index_str, pedited, spot.Lmaskcurve, spotEdited.Lmaskcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskcolCurvewav_" + index_str, pedited, spot.LLmaskcolcurvewav, spotEdited.LLmaskcolcurvewav); + + if (keyFile.has_key("Locallab", "CSThresholdcol_" + index_str)) { + const std::vector thresh = keyFile.get_integer_list("Locallab", "CSThresholdcol_" + index_str); + + if (thresh.size() >= 4) { + spot.csthresholdcol.setValues(thresh[0], thresh[1], min(thresh[2], 10), min(thresh[3], 10)); + } + + spotEdited.csthresholdcol = true; + } + + // Exposure + spot.visiexpose = assignFromKeyfile(keyFile, "Locallab", "Expexpose_" + index_str, pedited, spot.expexpose, spotEdited.expexpose); + + if (spot.visiexpose) { + spotEdited.visiexpose = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexexpose_" + index_str, pedited, spot.complexexpose, spotEdited.complexexpose); + assignFromKeyfile(keyFile, "Locallab", "Expcomp_" + index_str, pedited, spot.expcomp, spotEdited.expcomp); + assignFromKeyfile(keyFile, "Locallab", "Hlcompr_" + index_str, pedited, spot.hlcompr, spotEdited.hlcompr); + assignFromKeyfile(keyFile, "Locallab", "Hlcomprthresh_" + index_str, pedited, spot.hlcomprthresh, spotEdited.hlcomprthresh); + assignFromKeyfile(keyFile, "Locallab", "Black_" + index_str, pedited, spot.black, spotEdited.black); + assignFromKeyfile(keyFile, "Locallab", "Shadex_" + index_str, pedited, spot.shadex, spotEdited.shadex); + assignFromKeyfile(keyFile, "Locallab", "Shcompr_" + index_str, pedited, spot.shcompr, spotEdited.shcompr); + assignFromKeyfile(keyFile, "Locallab", "Expchroma_" + index_str, pedited, spot.expchroma, spotEdited.expchroma); + assignFromKeyfile(keyFile, "Locallab", "Sensiex_" + index_str, pedited, spot.sensiex, spotEdited.sensiex); + assignFromKeyfile(keyFile, "Locallab", "Structexp_" + index_str, pedited, spot.structexp, spotEdited.structexp); + assignFromKeyfile(keyFile, "Locallab", "Blurexpde_" + index_str, pedited, spot.blurexpde, spotEdited.blurexpde); + assignFromKeyfile(keyFile, "Locallab", "Strexp_" + index_str, pedited, spot.strexp, spotEdited.strexp); + assignFromKeyfile(keyFile, "Locallab", "Angexp_" + index_str, pedited, spot.angexp, spotEdited.angexp); + assignFromKeyfile(keyFile, "Locallab", "ExCurve_" + index_str, pedited, spot.excurve, spotEdited.excurve); + assignFromKeyfile(keyFile, "Locallab", "Inversex_" + index_str, pedited, spot.inversex, spotEdited.inversex); + assignFromKeyfile(keyFile, "Locallab", "EnaExpMask_" + index_str, pedited, spot.enaExpMask, spotEdited.enaExpMask); + assignFromKeyfile(keyFile, "Locallab", "EnaExpMaskaft_" + index_str, pedited, spot.enaExpMaskaft, spotEdited.enaExpMaskaft); + assignFromKeyfile(keyFile, "Locallab", "CCmaskexpCurve_" + index_str, pedited, spot.CCmaskexpcurve, spotEdited.CCmaskexpcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskexpCurve_" + index_str, pedited, spot.LLmaskexpcurve, spotEdited.LLmaskexpcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskexpCurve_" + index_str, pedited, spot.HHmaskexpcurve, spotEdited.HHmaskexpcurve); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskexp_" + index_str, pedited, spot.blendmaskexp, spotEdited.blendmaskexp); + assignFromKeyfile(keyFile, "Locallab", "Radmaskexp_" + index_str, pedited, spot.radmaskexp, spotEdited.radmaskexp); + assignFromKeyfile(keyFile, "Locallab", "Chromaskexp_" + index_str, pedited, spot.chromaskexp, spotEdited.chromaskexp); + assignFromKeyfile(keyFile, "Locallab", "Gammaskexp_" + index_str, pedited, spot.gammaskexp, spotEdited.gammaskexp); + assignFromKeyfile(keyFile, "Locallab", "Slomaskexp_" + index_str, pedited, spot.slomaskexp, spotEdited.slomaskexp); + assignFromKeyfile(keyFile, "Locallab", "Lapmaskexp_" + index_str, pedited, spot.lapmaskexp, spotEdited.lapmaskexp); + assignFromKeyfile(keyFile, "Locallab", "Strmaskexp_" + index_str, pedited, spot.strmaskexp, spotEdited.strmaskexp); + assignFromKeyfile(keyFile, "Locallab", "Angmaskexp_" + index_str, pedited, spot.angmaskexp, spotEdited.angmaskexp); + assignFromKeyfile(keyFile, "Locallab", "Softradiusexp_" + index_str, pedited, spot.softradiusexp, spotEdited.softradiusexp); + assignFromKeyfile(keyFile, "Locallab", "LmaskexpCurve_" + index_str, pedited, spot.Lmaskexpcurve, spotEdited.Lmaskexpcurve); + assignFromKeyfile(keyFile, "Locallab", "ExpMethod_" + index_str, pedited, spot.expMethod, spotEdited.expMethod); + assignFromKeyfile(keyFile, "Locallab", "ExnoiseMethod_" + index_str, pedited, spot.exnoiseMethod, spotEdited.exnoiseMethod); + assignFromKeyfile(keyFile, "Locallab", "Laplacexp_" + index_str, pedited, spot.laplacexp, spotEdited.laplacexp); + assignFromKeyfile(keyFile, "Locallab", "Balanexp_" + index_str, pedited, spot.balanexp, spotEdited.balanexp); + assignFromKeyfile(keyFile, "Locallab", "Linearexp_" + index_str, pedited, spot.linear, spotEdited.linear); + assignFromKeyfile(keyFile, "Locallab", "Gamm_" + index_str, pedited, spot.gamm, spotEdited.gamm); + assignFromKeyfile(keyFile, "Locallab", "Fatamount_" + index_str, pedited, spot.fatamount, spotEdited.fatamount); + assignFromKeyfile(keyFile, "Locallab", "Fatdetail_" + index_str, pedited, spot.fatdetail, spotEdited.fatdetail); + assignFromKeyfile(keyFile, "Locallab", "Fatanchor_" + index_str, pedited, spot.fatanchor, spotEdited.fatanchor); + assignFromKeyfile(keyFile, "Locallab", "Fatlevel_" + index_str, pedited, spot.fatlevel, spotEdited.fatlevel); + // Shadow highlight + spot.visishadhigh = assignFromKeyfile(keyFile, "Locallab", "Expshadhigh_" + index_str, pedited, spot.expshadhigh, spotEdited.expshadhigh); + + if (spot.visishadhigh) { + spotEdited.visishadhigh = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexshadhigh_" + index_str, pedited, spot.complexshadhigh, spotEdited.complexshadhigh); + assignFromKeyfile(keyFile, "Locallab", "ShMethod_" + index_str, pedited, spot.shMethod, spotEdited.shMethod); + + for (int j = 0; j < 5; j ++) { + assignFromKeyfile(keyFile, "Locallab", "Multsh" + std::to_string(j) + "_" + index_str, pedited, spot.multsh[j], spotEdited.multsh[j]); + } + + assignFromKeyfile(keyFile, "Locallab", "Expshadhigh_" + index_str, pedited, spot.expshadhigh, spotEdited.expshadhigh); + assignFromKeyfile(keyFile, "Locallab", "highlights_" + index_str, pedited, spot.highlights, spotEdited.highlights); + assignFromKeyfile(keyFile, "Locallab", "h_tonalwidth_" + index_str, pedited, spot.h_tonalwidth, spotEdited.h_tonalwidth); + assignFromKeyfile(keyFile, "Locallab", "shadows_" + index_str, pedited, spot.shadows, spotEdited.shadows); + assignFromKeyfile(keyFile, "Locallab", "s_tonalwidth_" + index_str, pedited, spot.s_tonalwidth, spotEdited.s_tonalwidth); + assignFromKeyfile(keyFile, "Locallab", "sh_radius_" + index_str, pedited, spot.sh_radius, spotEdited.sh_radius); + assignFromKeyfile(keyFile, "Locallab", "sensihs_" + index_str, pedited, spot.sensihs, spotEdited.sensihs); + assignFromKeyfile(keyFile, "Locallab", "EnaSHMask_" + index_str, pedited, spot.enaSHMask, spotEdited.enaSHMask); + assignFromKeyfile(keyFile, "Locallab", "CCmaskSHCurve_" + index_str, pedited, spot.CCmaskSHcurve, spotEdited.CCmaskSHcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskSHCurve_" + index_str, pedited, spot.LLmaskSHcurve, spotEdited.LLmaskSHcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskSHCurve_" + index_str, pedited, spot.HHmaskSHcurve, spotEdited.HHmaskSHcurve); + assignFromKeyfile(keyFile, "Locallab", "BlendmaskSH_" + index_str, pedited, spot.blendmaskSH, spotEdited.blendmaskSH); + assignFromKeyfile(keyFile, "Locallab", "RadmaskSH_" + index_str, pedited, spot.radmaskSH, spotEdited.radmaskSH); + assignFromKeyfile(keyFile, "Locallab", "BlurSHde_" + index_str, pedited, spot.blurSHde, spotEdited.blurSHde); + assignFromKeyfile(keyFile, "Locallab", "StrSH_" + index_str, pedited, spot.strSH, spotEdited.strSH); + assignFromKeyfile(keyFile, "Locallab", "AngSH_" + index_str, pedited, spot.angSH, spotEdited.angSH); + assignFromKeyfile(keyFile, "Locallab", "Inverssh_" + index_str, pedited, spot.inverssh, spotEdited.inverssh); + assignFromKeyfile(keyFile, "Locallab", "ChromaskSH_" + index_str, pedited, spot.chromaskSH, spotEdited.chromaskSH); + assignFromKeyfile(keyFile, "Locallab", "GammaskSH_" + index_str, pedited, spot.gammaskSH, spotEdited.gammaskSH); + assignFromKeyfile(keyFile, "Locallab", "SlomaskSH_" + index_str, pedited, spot.slomaskSH, spotEdited.slomaskSH); + assignFromKeyfile(keyFile, "Locallab", "LapmaskSH_" + index_str, pedited, spot.lapmaskSH, spotEdited.lapmaskSH); + assignFromKeyfile(keyFile, "Locallab", "DetailSH_" + index_str, pedited, spot.detailSH, spotEdited.detailSH); + assignFromKeyfile(keyFile, "Locallab", "LmaskSHCurve_" + index_str, pedited, spot.LmaskSHcurve, spotEdited.LmaskSHcurve); + assignFromKeyfile(keyFile, "Locallab", "FatamountSH_" + index_str, pedited, spot.fatamountSH, spotEdited.fatamountSH); + assignFromKeyfile(keyFile, "Locallab", "FatanchorSH_" + index_str, pedited, spot.fatanchorSH, spotEdited.fatanchorSH); + assignFromKeyfile(keyFile, "Locallab", "GamSH_" + index_str, pedited, spot.gamSH, spotEdited.gamSH); + assignFromKeyfile(keyFile, "Locallab", "SloSH_" + index_str, pedited, spot.sloSH, spotEdited.sloSH); + // Vibrance + spot.visivibrance = assignFromKeyfile(keyFile, "Locallab", "Expvibrance_" + index_str, pedited, spot.expvibrance, spotEdited.expvibrance); + + if (spot.visivibrance) { + spotEdited.visivibrance = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexvibrance_" + index_str, pedited, spot.complexvibrance, spotEdited.complexvibrance); + assignFromKeyfile(keyFile, "Locallab", "Saturated_" + index_str, pedited, spot.saturated, spotEdited.saturated); + assignFromKeyfile(keyFile, "Locallab", "Pastels_" + index_str, pedited, spot.pastels, spotEdited.pastels); + assignFromKeyfile(keyFile, "Locallab", "Warm_" + index_str, pedited, spot.warm, spotEdited.warm); + + if (keyFile.has_key("Locallab", "PSThreshold_" + index_str)) { + const std::vector thresh = keyFile.get_integer_list("Locallab", "PSThreshold_" + index_str); + + if (thresh.size() >= 2) { + spot.psthreshold.setValues(thresh[0], thresh[1]); + } + + spotEdited.psthreshold = true; + } + + assignFromKeyfile(keyFile, "Locallab", "ProtectSkins_" + index_str, pedited, spot.protectskins, spotEdited.protectskins); + assignFromKeyfile(keyFile, "Locallab", "AvoidColorShift_" + index_str, pedited, spot.avoidcolorshift, spotEdited.avoidcolorshift); + assignFromKeyfile(keyFile, "Locallab", "PastSatTog_" + index_str, pedited, spot.pastsattog, spotEdited.pastsattog); + assignFromKeyfile(keyFile, "Locallab", "Sensiv_" + index_str, pedited, spot.sensiv, spotEdited.sensiv); + assignFromKeyfile(keyFile, "Locallab", "SkinTonesCurve_" + index_str, pedited, spot.skintonescurve, spotEdited.skintonescurve); + assignFromKeyfile(keyFile, "Locallab", "CCmaskvibCurve_" + index_str, pedited, spot.CCmaskvibcurve, spotEdited.CCmaskvibcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskvibCurve_" + index_str, pedited, spot.LLmaskvibcurve, spotEdited.LLmaskvibcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskvibCurve_" + index_str, pedited, spot.HHmaskvibcurve, spotEdited.HHmaskvibcurve); + assignFromKeyfile(keyFile, "Locallab", "EnavibMask_" + index_str, pedited, spot.enavibMask, spotEdited.enavibMask); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskvib_" + index_str, pedited, spot.blendmaskvib, spotEdited.blendmaskvib); + assignFromKeyfile(keyFile, "Locallab", "Radmaskvib_" + index_str, pedited, spot.radmaskvib, spotEdited.radmaskvib); + assignFromKeyfile(keyFile, "Locallab", "Chromaskvib_" + index_str, pedited, spot.chromaskvib, spotEdited.chromaskvib); + assignFromKeyfile(keyFile, "Locallab", "Gammaskvib_" + index_str, pedited, spot.gammaskvib, spotEdited.gammaskvib); + assignFromKeyfile(keyFile, "Locallab", "Slomaskvib_" + index_str, pedited, spot.slomaskvib, spotEdited.slomaskvib); + assignFromKeyfile(keyFile, "Locallab", "Lapmaskvib_" + index_str, pedited, spot.lapmaskvib, spotEdited.lapmaskvib); + assignFromKeyfile(keyFile, "Locallab", "Strvib_" + index_str, pedited, spot.strvib, spotEdited.strvib); + assignFromKeyfile(keyFile, "Locallab", "Strvibab_" + index_str, pedited, spot.strvibab, spotEdited.strvibab); + assignFromKeyfile(keyFile, "Locallab", "Strvibh_" + index_str, pedited, spot.strvibh, spotEdited.strvibh); + assignFromKeyfile(keyFile, "Locallab", "Angvib_" + index_str, pedited, spot.angvib, spotEdited.angvib); + assignFromKeyfile(keyFile, "Locallab", "LmaskvibCurve_" + index_str, pedited, spot.Lmaskvibcurve, spotEdited.Lmaskvibcurve); + // Soft Light + spot.visisoft = assignFromKeyfile(keyFile, "Locallab", "Expsoft_" + index_str, pedited, spot.expsoft, spotEdited.expsoft); + + if (spot.visisoft) { + spotEdited.visisoft = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexsoft_" + index_str, pedited, spot.complexsoft, spotEdited.complexsoft); + assignFromKeyfile(keyFile, "Locallab", "Streng_" + index_str, pedited, spot.streng, spotEdited.streng); + assignFromKeyfile(keyFile, "Locallab", "Sensisf_" + index_str, pedited, spot.sensisf, spotEdited.sensisf); + assignFromKeyfile(keyFile, "Locallab", "Laplace_" + index_str, pedited, spot.laplace, spotEdited.laplace); + assignFromKeyfile(keyFile, "Locallab", "SoftMethod_" + index_str, pedited, spot.softMethod, spotEdited.softMethod); + // Blur & Noise + spot.visiblur = assignFromKeyfile(keyFile, "Locallab", "Expblur_" + index_str, pedited, spot.expblur, spotEdited.expblur); + + if (spot.visiblur) { + spotEdited.visiblur = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexblur_" + index_str, pedited, spot.complexblur, spotEdited.complexblur); + assignFromKeyfile(keyFile, "Locallab", "Radius_" + index_str, pedited, spot.radius, spotEdited.radius); + assignFromKeyfile(keyFile, "Locallab", "Strength_" + index_str, pedited, spot.strength, spotEdited.strength); + assignFromKeyfile(keyFile, "Locallab", "Sensibn_" + index_str, pedited, spot.sensibn, spotEdited.sensibn); + assignFromKeyfile(keyFile, "Locallab", "Iteramed_" + index_str, pedited, spot.itera, spotEdited.itera); + assignFromKeyfile(keyFile, "Locallab", "Guidbl_" + index_str, pedited, spot.guidbl, spotEdited.guidbl); + assignFromKeyfile(keyFile, "Locallab", "Strbl_" + index_str, pedited, spot.strbl, spotEdited.strbl); + assignFromKeyfile(keyFile, "Locallab", "Isogr_" + index_str, pedited, spot.isogr, spotEdited.isogr); + assignFromKeyfile(keyFile, "Locallab", "Strengr_" + index_str, pedited, spot.strengr, spotEdited.strengr); + assignFromKeyfile(keyFile, "Locallab", "Scalegr_" + index_str, pedited, spot.scalegr, spotEdited.scalegr); + assignFromKeyfile(keyFile, "Locallab", "Epsbl_" + index_str, pedited, spot.epsbl, spotEdited.epsbl); + assignFromKeyfile(keyFile, "Locallab", "BlMethod_" + index_str, pedited, spot.blMethod, spotEdited.blMethod); + assignFromKeyfile(keyFile, "Locallab", "ChroMethod_" + index_str, pedited, spot.chroMethod, spotEdited.chroMethod); + assignFromKeyfile(keyFile, "Locallab", "BlurMethod_" + index_str, pedited, spot.blurMethod, spotEdited.blurMethod); + assignFromKeyfile(keyFile, "Locallab", "MedMethod_" + index_str, pedited, spot.medMethod, spotEdited.medMethod); + assignFromKeyfile(keyFile, "Locallab", "activlum_" + index_str, pedited, spot.activlum, spotEdited.activlum); + assignFromKeyfile(keyFile, "Locallab", "noiselumf_" + index_str, pedited, spot.noiselumf, spotEdited.noiselumf); + assignFromKeyfile(keyFile, "Locallab", "noiselumf0_" + index_str, pedited, spot.noiselumf0, spotEdited.noiselumf0); + assignFromKeyfile(keyFile, "Locallab", "noiselumf2_" + index_str, pedited, spot.noiselumf2, spotEdited.noiselumf2); + assignFromKeyfile(keyFile, "Locallab", "noiselumc_" + index_str, pedited, spot.noiselumc, spotEdited.noiselumc); + assignFromKeyfile(keyFile, "Locallab", "noiselumdetail_" + index_str, pedited, spot.noiselumdetail, spotEdited.noiselumdetail); + assignFromKeyfile(keyFile, "Locallab", "noiselequal_" + index_str, pedited, spot.noiselequal, spotEdited.noiselequal); + assignFromKeyfile(keyFile, "Locallab", "noisechrof_" + index_str, pedited, spot.noisechrof, spotEdited.noisechrof); + assignFromKeyfile(keyFile, "Locallab", "noisechroc_" + index_str, pedited, spot.noisechroc, spotEdited.noisechroc); + assignFromKeyfile(keyFile, "Locallab", "noisechrodetail_" + index_str, pedited, spot.noisechrodetail, spotEdited.noisechrodetail); + assignFromKeyfile(keyFile, "Locallab", "Adjblur_" + index_str, pedited, spot.adjblur, spotEdited.adjblur); + assignFromKeyfile(keyFile, "Locallab", "Bilateral_" + index_str, pedited, spot.bilateral, spotEdited.bilateral); + assignFromKeyfile(keyFile, "Locallab", "Sensiden_" + index_str, pedited, spot.sensiden, spotEdited.sensiden); + assignFromKeyfile(keyFile, "Locallab", "Detailthr_" + index_str, pedited, spot.detailthr, spotEdited.detailthr); + assignFromKeyfile(keyFile, "Locallab", "LocwavCurveden_" + index_str, pedited, spot.locwavcurveden, spotEdited.locwavcurveden); + assignFromKeyfile(keyFile, "Locallab", "Showmasktyp_" + index_str, pedited, spot.showmaskblMethodtyp, spotEdited.showmaskblMethodtyp); + assignFromKeyfile(keyFile, "Locallab", "CCmaskblCurve_" + index_str, pedited, spot.CCmaskblcurve, spotEdited.CCmaskblcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskblCurve_" + index_str, pedited, spot.LLmaskblcurve, spotEdited.LLmaskblcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskblCurve_" + index_str, pedited, spot.HHmaskblcurve, spotEdited.HHmaskblcurve); + assignFromKeyfile(keyFile, "Locallab", "EnablMask_" + index_str, pedited, spot.enablMask, spotEdited.enablMask); + assignFromKeyfile(keyFile, "Locallab", "Fftwbl_" + index_str, pedited, spot.fftwbl, spotEdited.fftwbl); + assignFromKeyfile(keyFile, "Locallab", "Toolbl_" + index_str, pedited, spot.toolbl, spotEdited.toolbl); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskbl_" + index_str, pedited, spot.blendmaskbl, spotEdited.blendmaskbl); + assignFromKeyfile(keyFile, "Locallab", "Radmaskbl_" + index_str, pedited, spot.radmaskbl, spotEdited.radmaskbl); + assignFromKeyfile(keyFile, "Locallab", "Chromaskbl_" + index_str, pedited, spot.chromaskbl, spotEdited.chromaskbl); + assignFromKeyfile(keyFile, "Locallab", "Gammaskbl_" + index_str, pedited, spot.gammaskbl, spotEdited.gammaskbl); + assignFromKeyfile(keyFile, "Locallab", "Slomaskbl_" + index_str, pedited, spot.slomaskbl, spotEdited.slomaskbl); + assignFromKeyfile(keyFile, "Locallab", "Lapmaskbl_" + index_str, pedited, spot.lapmaskbl, spotEdited.lapmaskbl); + assignFromKeyfile(keyFile, "Locallab", "shadmaskbl_" + index_str, pedited, spot.shadmaskbl, spotEdited.shadmaskbl); + assignFromKeyfile(keyFile, "Locallab", "shadmaskblsha_" + index_str, pedited, spot.shadmaskblsha, spotEdited.shadmaskblsha); + assignFromKeyfile(keyFile, "Locallab", "strumaskbl_" + index_str, pedited, spot.strumaskbl, spotEdited.strumaskbl); + assignFromKeyfile(keyFile, "Locallab", "LmaskblCurve_" + index_str, pedited, spot.Lmaskblcurve, spotEdited.Lmaskblcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskblCurvewav_" + index_str, pedited, spot.LLmaskblcurvewav, spotEdited.LLmaskblcurvewav); + + if (keyFile.has_key("Locallab", "CSThresholdblur_" + index_str)) { + const std::vector thresh = keyFile.get_integer_list("Locallab", "CSThresholdblur_" + index_str); + + if (thresh.size() >= 4) { + spot.csthresholdblur.setValues(thresh[0], thresh[1], min(thresh[2], 10), min(thresh[3], 10)); + } + + spotEdited.csthresholdblur = true; + } + // Tone Mapping + spot.visitonemap = assignFromKeyfile(keyFile, "Locallab", "Exptonemap_" + index_str, pedited, spot.exptonemap, spotEdited.exptonemap); + + if (spot.visitonemap) { + spotEdited.visitonemap = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complextonemap_" + index_str, pedited, spot.complextonemap, spotEdited.complextonemap); + assignFromKeyfile(keyFile, "Locallab", "Stren_" + index_str, pedited, spot.stren, spotEdited.stren); + assignFromKeyfile(keyFile, "Locallab", "Gamma_" + index_str, pedited, spot.gamma, spotEdited.gamma); + assignFromKeyfile(keyFile, "Locallab", "Estop_" + index_str, pedited, spot.estop, spotEdited.estop); + assignFromKeyfile(keyFile, "Locallab", "Scaltm_" + index_str, pedited, spot.scaltm, spotEdited.scaltm); + assignFromKeyfile(keyFile, "Locallab", "Rewei_" + index_str, pedited, spot.rewei, spotEdited.rewei); + assignFromKeyfile(keyFile, "Locallab", "Satur_" + index_str, pedited, spot.satur, spotEdited.satur); + assignFromKeyfile(keyFile, "Locallab", "Sensitm_" + index_str, pedited, spot.sensitm, spotEdited.sensitm); + assignFromKeyfile(keyFile, "Locallab", "Softradiustm_" + index_str, pedited, spot.softradiustm, spotEdited.softradiustm); + assignFromKeyfile(keyFile, "Locallab", "Amount_" + index_str, pedited, spot.amount, spotEdited.amount); + assignFromKeyfile(keyFile, "Locallab", "Equiltm_" + index_str, pedited, spot.equiltm, spotEdited.equiltm); + assignFromKeyfile(keyFile, "Locallab", "CCmasktmCurve_" + index_str, pedited, spot.CCmasktmcurve, spotEdited.CCmasktmcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmasktmCurve_" + index_str, pedited, spot.LLmasktmcurve, spotEdited.LLmasktmcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmasktmCurve_" + index_str, pedited, spot.HHmasktmcurve, spotEdited.HHmasktmcurve); + assignFromKeyfile(keyFile, "Locallab", "EnatmMask_" + index_str, pedited, spot.enatmMask, spotEdited.enatmMask); + assignFromKeyfile(keyFile, "Locallab", "EnatmMaskaft_" + index_str, pedited, spot.enatmMaskaft, spotEdited.enatmMaskaft); + assignFromKeyfile(keyFile, "Locallab", "Blendmasktm_" + index_str, pedited, spot.blendmasktm, spotEdited.blendmasktm); + assignFromKeyfile(keyFile, "Locallab", "Radmasktm_" + index_str, pedited, spot.radmasktm, spotEdited.radmasktm); + assignFromKeyfile(keyFile, "Locallab", "Chromasktm_" + index_str, pedited, spot.chromasktm, spotEdited.chromasktm); + assignFromKeyfile(keyFile, "Locallab", "Gammasktm_" + index_str, pedited, spot.gammasktm, spotEdited.gammasktm); + assignFromKeyfile(keyFile, "Locallab", "Slomasktm_" + index_str, pedited, spot.slomasktm, spotEdited.slomasktm); + assignFromKeyfile(keyFile, "Locallab", "Lapmasktm_" + index_str, pedited, spot.lapmasktm, spotEdited.lapmasktm); + assignFromKeyfile(keyFile, "Locallab", "LmasktmCurve_" + index_str, pedited, spot.Lmasktmcurve, spotEdited.Lmasktmcurve); + // Retinex + spot.visireti = assignFromKeyfile(keyFile, "Locallab", "Expreti_" + index_str, pedited, spot.expreti, spotEdited.expreti); + + if (spot.visireti) { + spotEdited.visireti = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexreti_" + index_str, pedited, spot.complexreti, spotEdited.complexreti); + assignFromKeyfile(keyFile, "Locallab", "retinexMethod_" + index_str, pedited, spot.retinexMethod, spotEdited.retinexMethod); + assignFromKeyfile(keyFile, "Locallab", "Str_" + index_str, pedited, spot.str, spotEdited.str); + assignFromKeyfile(keyFile, "Locallab", "Chrrt_" + index_str, pedited, spot.chrrt, spotEdited.chrrt); + assignFromKeyfile(keyFile, "Locallab", "Neigh_" + index_str, pedited, spot.neigh, spotEdited.neigh); + assignFromKeyfile(keyFile, "Locallab", "Vart_" + index_str, pedited, spot.vart, spotEdited.vart); + assignFromKeyfile(keyFile, "Locallab", "Offs_" + index_str, pedited, spot.offs, spotEdited.offs); + assignFromKeyfile(keyFile, "Locallab", "Dehaz_" + index_str, pedited, spot.dehaz, spotEdited.dehaz); + assignFromKeyfile(keyFile, "Locallab", "Depth_" + index_str, pedited, spot.depth, spotEdited.depth); + assignFromKeyfile(keyFile, "Locallab", "Sensih_" + index_str, pedited, spot.sensih, spotEdited.sensih); + assignFromKeyfile(keyFile, "Locallab", "TgainCurve_" + index_str, pedited, spot.localTgaincurve, spotEdited.localTgaincurve); + assignFromKeyfile(keyFile, "Locallab", "TtransCurve_" + index_str, pedited, spot.localTtranscurve, spotEdited.localTtranscurve); + assignFromKeyfile(keyFile, "Locallab", "Inversret_" + index_str, pedited, spot.inversret, spotEdited.inversret); + assignFromKeyfile(keyFile, "Locallab", "Equilret_" + index_str, pedited, spot.equilret, spotEdited.equilret); + assignFromKeyfile(keyFile, "Locallab", "Loglin_" + index_str, pedited, spot.loglin, spotEdited.loglin); + assignFromKeyfile(keyFile, "Locallab", "Lumonly_" + index_str, pedited, spot.lumonly, spotEdited.lumonly); + assignFromKeyfile(keyFile, "Locallab", "Softradiusret_" + index_str, pedited, spot.softradiusret, spotEdited.softradiusret); + assignFromKeyfile(keyFile, "Locallab", "CCmaskretiCurve_" + index_str, pedited, spot.CCmaskreticurve, spotEdited.CCmaskreticurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskretiCurve_" + index_str, pedited, spot.LLmaskreticurve, spotEdited.LLmaskreticurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskretiCurve_" + index_str, pedited, spot.HHmaskreticurve, spotEdited.HHmaskreticurve); + assignFromKeyfile(keyFile, "Locallab", "EnaretiMask_" + index_str, pedited, spot.enaretiMask, spotEdited.enaretiMask); + assignFromKeyfile(keyFile, "Locallab", "EnaretiMasktmap_" + index_str, pedited, spot.enaretiMasktmap, spotEdited.enaretiMasktmap); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskreti_" + index_str, pedited, spot.blendmaskreti, spotEdited.blendmaskreti); + assignFromKeyfile(keyFile, "Locallab", "Radmaskreti_" + index_str, pedited, spot.radmaskreti, spotEdited.radmaskreti); + assignFromKeyfile(keyFile, "Locallab", "Chromaskreti_" + index_str, pedited, spot.chromaskreti, spotEdited.chromaskreti); + assignFromKeyfile(keyFile, "Locallab", "Gammaskreti_" + index_str, pedited, spot.gammaskreti, spotEdited.gammaskreti); + assignFromKeyfile(keyFile, "Locallab", "Slomaskreti_" + index_str, pedited, spot.slomaskreti, spotEdited.slomaskreti); + assignFromKeyfile(keyFile, "Locallab", "Lapmaskreti_" + index_str, pedited, spot.lapmaskreti, spotEdited.lapmaskreti); + assignFromKeyfile(keyFile, "Locallab", "Scalereti_" + index_str, pedited, spot.scalereti, spotEdited.scalereti); + assignFromKeyfile(keyFile, "Locallab", "Darkness_" + index_str, pedited, spot.darkness, spotEdited.darkness); + assignFromKeyfile(keyFile, "Locallab", "Lightnessreti_" + index_str, pedited, spot.lightnessreti, spotEdited.lightnessreti); + assignFromKeyfile(keyFile, "Locallab", "Limd_" + index_str, pedited, spot.limd, spotEdited.limd); + assignFromKeyfile(keyFile, "Locallab", "Cliptm_" + index_str, pedited, spot.cliptm, spotEdited.cliptm); + assignFromKeyfile(keyFile, "Locallab", "Fftwreti_" + index_str, pedited, spot.fftwreti, spotEdited.fftwreti); + assignFromKeyfile(keyFile, "Locallab", "LmaskretiCurve_" + index_str, pedited, spot.Lmaskreticurve, spotEdited.Lmaskreticurve); + // Sharpening + spot.visisharp = assignFromKeyfile(keyFile, "Locallab", "Expsharp_" + index_str, pedited, spot.expsharp, spotEdited.expsharp); + + if (spot.visisharp) { + spotEdited.visisharp = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexsharp_" + index_str, pedited, spot.complexsharp, spotEdited.complexsharp); + assignFromKeyfile(keyFile, "Locallab", "Sharcontrast_" + index_str, pedited, spot.sharcontrast, spotEdited.sharcontrast); + assignFromKeyfile(keyFile, "Locallab", "Sharradius_" + index_str, pedited, spot.sharradius, spotEdited.sharradius); + assignFromKeyfile(keyFile, "Locallab", "Sharamount_" + index_str, pedited, spot.sharamount, spotEdited.sharamount); + assignFromKeyfile(keyFile, "Locallab", "Shardamping_" + index_str, pedited, spot.shardamping, spotEdited.shardamping); + assignFromKeyfile(keyFile, "Locallab", "Shariter_" + index_str, pedited, spot.shariter, spotEdited.shariter); + assignFromKeyfile(keyFile, "Locallab", "Sharblur_" + index_str, pedited, spot.sharblur, spotEdited.sharblur); + assignFromKeyfile(keyFile, "Locallab", "Sensisha_" + index_str, pedited, spot.sensisha, spotEdited.sensisha); + assignFromKeyfile(keyFile, "Locallab", "Inverssha_" + index_str, pedited, spot.inverssha, spotEdited.inverssha); + // Local Contrast + spot.visicontrast = assignFromKeyfile(keyFile, "Locallab", "Expcontrast_" + index_str, pedited, spot.expcontrast, spotEdited.expcontrast); + + if (spot.visicontrast) { + spotEdited.visicontrast = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexcontrast_" + index_str, pedited, spot.complexcontrast, spotEdited.complexcontrast); + assignFromKeyfile(keyFile, "Locallab", "Lcradius_" + index_str, pedited, spot.lcradius, spotEdited.lcradius); + assignFromKeyfile(keyFile, "Locallab", "Lcamount_" + index_str, pedited, spot.lcamount, spotEdited.lcamount); + assignFromKeyfile(keyFile, "Locallab", "Lcdarkness_" + index_str, pedited, spot.lcdarkness, spotEdited.lcdarkness); + assignFromKeyfile(keyFile, "Locallab", "Lclightness_" + index_str, pedited, spot.lclightness, spotEdited.lclightness); + assignFromKeyfile(keyFile, "Locallab", "Sigmalc_" + index_str, pedited, spot.sigmalc, spotEdited.sigmalc); + assignFromKeyfile(keyFile, "Locallab", "Levelwav_" + index_str, pedited, spot.levelwav, spotEdited.levelwav); + assignFromKeyfile(keyFile, "Locallab", "Residcont_" + index_str, pedited, spot.residcont, spotEdited.residcont); + assignFromKeyfile(keyFile, "Locallab", "Residsha_" + index_str, pedited, spot.residsha, spotEdited.residsha); + assignFromKeyfile(keyFile, "Locallab", "Residshathr_" + index_str, pedited, spot.residshathr, spotEdited.residshathr); + assignFromKeyfile(keyFile, "Locallab", "Residhi_" + index_str, pedited, spot.residhi, spotEdited.residhi); + assignFromKeyfile(keyFile, "Locallab", "Residhithr_" + index_str, pedited, spot.residhithr, spotEdited.residhithr); + assignFromKeyfile(keyFile, "Locallab", "Residblur_" + index_str, pedited, spot.residblur, spotEdited.residblur); + assignFromKeyfile(keyFile, "Locallab", "Levelblur_" + index_str, pedited, spot.levelblur, spotEdited.levelblur); + assignFromKeyfile(keyFile, "Locallab", "Sigmabl_" + index_str, pedited, spot.sigmabl, spotEdited.sigmabl); + assignFromKeyfile(keyFile, "Locallab", "Residchro_" + index_str, pedited, spot.residchro, spotEdited.residchro); + assignFromKeyfile(keyFile, "Locallab", "Residcomp_" + index_str, pedited, spot.residcomp, spotEdited.residcomp); + assignFromKeyfile(keyFile, "Locallab", "Sigma_" + index_str, pedited, spot.sigma, spotEdited.sigma); + assignFromKeyfile(keyFile, "Locallab", "Offset_" + index_str, pedited, spot.offset, spotEdited.offset); + assignFromKeyfile(keyFile, "Locallab", "Sigmadr_" + index_str, pedited, spot.sigmadr, spotEdited.sigmadr); + assignFromKeyfile(keyFile, "Locallab", "Threswav_" + index_str, pedited, spot.threswav, spotEdited.threswav); + assignFromKeyfile(keyFile, "Locallab", "Chromalev_" + index_str, pedited, spot.chromalev, spotEdited.chromalev); + assignFromKeyfile(keyFile, "Locallab", "Chromablu_" + index_str, pedited, spot.chromablu, spotEdited.chromablu); + assignFromKeyfile(keyFile, "Locallab", "sigmadc_" + index_str, pedited, spot.sigmadc, spotEdited.sigmadc); + assignFromKeyfile(keyFile, "Locallab", "deltad_" + index_str, pedited, spot.deltad, spotEdited.deltad); + assignFromKeyfile(keyFile, "Locallab", "Fatres_" + index_str, pedited, spot.fatres, spotEdited.fatres); + assignFromKeyfile(keyFile, "Locallab", "ClariLres_" + index_str, pedited, spot.clarilres, spotEdited.clarilres); + assignFromKeyfile(keyFile, "Locallab", "ClariCres_" + index_str, pedited, spot.claricres, spotEdited.claricres); + assignFromKeyfile(keyFile, "Locallab", "Clarisoft_" + index_str, pedited, spot.clarisoft, spotEdited.clarisoft); + assignFromKeyfile(keyFile, "Locallab", "Sigmalc2_" + index_str, pedited, spot.sigmalc2, spotEdited.sigmalc2); + assignFromKeyfile(keyFile, "Locallab", "Strwav_" + index_str, pedited, spot.strwav, spotEdited.strwav); + assignFromKeyfile(keyFile, "Locallab", "Angwav_" + index_str, pedited, spot.angwav, spotEdited.angwav); + assignFromKeyfile(keyFile, "Locallab", "Strengthw_" + index_str, pedited, spot.strengthw, spotEdited.strengthw); + assignFromKeyfile(keyFile, "Locallab", "Sigmaed_" + index_str, pedited, spot.sigmaed, spotEdited.sigmaed); + assignFromKeyfile(keyFile, "Locallab", "Radiusw_" + index_str, pedited, spot.radiusw, spotEdited.radiusw); + assignFromKeyfile(keyFile, "Locallab", "Detailw_" + index_str, pedited, spot.detailw, spotEdited.detailw); + assignFromKeyfile(keyFile, "Locallab", "Gradw_" + index_str, pedited, spot.gradw, spotEdited.gradw); + assignFromKeyfile(keyFile, "Locallab", "Tloww_" + index_str, pedited, spot.tloww, spotEdited.tloww); + assignFromKeyfile(keyFile, "Locallab", "Thigw_" + index_str, pedited, spot.thigw, spotEdited.thigw); + assignFromKeyfile(keyFile, "Locallab", "Edgw_" + index_str, pedited, spot.edgw, spotEdited.edgw); + assignFromKeyfile(keyFile, "Locallab", "Basew_" + index_str, pedited, spot.basew, spotEdited.basew); + assignFromKeyfile(keyFile, "Locallab", "Sensilc_" + index_str, pedited, spot.sensilc, spotEdited.sensilc); + assignFromKeyfile(keyFile, "Locallab", "Fftwlc_" + index_str, pedited, spot.fftwlc, spotEdited.fftwlc); + assignFromKeyfile(keyFile, "Locallab", "Blurlc_" + index_str, pedited, spot.blurlc, spotEdited.blurlc); + assignFromKeyfile(keyFile, "Locallab", "Wavblur_" + index_str, pedited, spot.wavblur, spotEdited.wavblur); + assignFromKeyfile(keyFile, "Locallab", "Wavedg_" + index_str, pedited, spot.wavedg, spotEdited.wavedg); + assignFromKeyfile(keyFile, "Locallab", "Waveshow_" + index_str, pedited, spot.waveshow, spotEdited.waveshow); + assignFromKeyfile(keyFile, "Locallab", "Wavcont_" + index_str, pedited, spot.wavcont, spotEdited.wavcont); + assignFromKeyfile(keyFile, "Locallab", "Wavcomp_" + index_str, pedited, spot.wavcomp, spotEdited.wavcomp); + assignFromKeyfile(keyFile, "Locallab", "Wavgradl_" + index_str, pedited, spot.wavgradl, spotEdited.wavgradl); + assignFromKeyfile(keyFile, "Locallab", "Wavcompre_" + index_str, pedited, spot.wavcompre, spotEdited.wavcompre); + assignFromKeyfile(keyFile, "Locallab", "Origlc_" + index_str, pedited, spot.origlc, spotEdited.origlc); + assignFromKeyfile(keyFile, "Locallab", "localcontMethod_" + index_str, pedited, spot.localcontMethod, spotEdited.localcontMethod); + assignFromKeyfile(keyFile, "Locallab", "localedgMethod_" + index_str, pedited, spot.localedgMethod, spotEdited.localedgMethod); + assignFromKeyfile(keyFile, "Locallab", "localneiMethod_" + index_str, pedited, spot.localneiMethod, spotEdited.localneiMethod); + assignFromKeyfile(keyFile, "Locallab", "LocwavCurve_" + index_str, pedited, spot.locwavcurve, spotEdited.locwavcurve); + assignFromKeyfile(keyFile, "Locallab", "LoclevwavCurve_" + index_str, pedited, spot.loclevwavcurve, spotEdited.loclevwavcurve); + assignFromKeyfile(keyFile, "Locallab", "LocconwavCurve_" + index_str, pedited, spot.locconwavcurve, spotEdited.locconwavcurve); + assignFromKeyfile(keyFile, "Locallab", "LoccompwavCurve_" + index_str, pedited, spot.loccompwavcurve, spotEdited.loccompwavcurve); + assignFromKeyfile(keyFile, "Locallab", "LoccomprewavCurve_" + index_str, pedited, spot.loccomprewavcurve, spotEdited.loccomprewavcurve); + assignFromKeyfile(keyFile, "Locallab", "LocedgwavCurve_" + index_str, pedited, spot.locedgwavcurve, spotEdited.locedgwavcurve); + + if (keyFile.has_key("Locallab", "CSThreshold_" + index_str)) { + + const std::vector thresh = keyFile.get_integer_list("Locallab", "CSThreshold_" + index_str); + + if (thresh.size() >= 4) { + spot.csthreshold.setValues(thresh[0], thresh[1], min(thresh[2], 10), min(thresh[3], 10)); + } + + spotEdited.csthreshold = true; + } + + assignFromKeyfile(keyFile, "Locallab", "CCmasklcCurve_" + index_str, pedited, spot.CCmasklccurve, spotEdited.CCmasklccurve); + assignFromKeyfile(keyFile, "Locallab", "LLmasklcCurve_" + index_str, pedited, spot.LLmasklccurve, spotEdited.LLmasklccurve); + assignFromKeyfile(keyFile, "Locallab", "HHmasklcCurve_" + index_str, pedited, spot.HHmasklccurve, spotEdited.HHmasklccurve); + assignFromKeyfile(keyFile, "Locallab", "EnalcMask_" + index_str, pedited, spot.enalcMask, spotEdited.enalcMask); + assignFromKeyfile(keyFile, "Locallab", "Blendmasklc_" + index_str, pedited, spot.blendmasklc, spotEdited.blendmasklc); + assignFromKeyfile(keyFile, "Locallab", "Radmasklc_" + index_str, pedited, spot.radmasklc, spotEdited.radmasklc); + assignFromKeyfile(keyFile, "Locallab", "Chromasklc_" + index_str, pedited, spot.chromasklc, spotEdited.chromasklc); + assignFromKeyfile(keyFile, "Locallab", "LmasklcCurve_" + index_str, pedited, spot.Lmasklccurve, spotEdited.Lmasklccurve); + // Contrast by detail levels + spot.visicbdl = assignFromKeyfile(keyFile, "Locallab", "Expcbdl_" + index_str, pedited, spot.expcbdl, spotEdited.expcbdl); + + if (spot.visicbdl) { + spotEdited.visicbdl = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Complexcbdl_" + index_str, pedited, spot.complexcbdl, spotEdited.complexcbdl); + + for (int j = 0; j < 6; j ++) { + assignFromKeyfile(keyFile, "Locallab", "Mult" + std::to_string(j) + "_" + index_str, pedited, spot.mult[j], spotEdited.mult[j]); + } + + assignFromKeyfile(keyFile, "Locallab", "Chromacbdl_" + index_str, pedited, spot.chromacbdl, spotEdited.chromacbdl); + assignFromKeyfile(keyFile, "Locallab", "Threshold_" + index_str, pedited, spot.threshold, spotEdited.threshold); + assignFromKeyfile(keyFile, "Locallab", "Sensicb_" + index_str, pedited, spot.sensicb, spotEdited.sensicb); + assignFromKeyfile(keyFile, "Locallab", "Clarityml_" + index_str, pedited, spot.clarityml, spotEdited.clarityml); + assignFromKeyfile(keyFile, "Locallab", "Contresid_" + index_str, pedited, spot.contresid, spotEdited.contresid); + assignFromKeyfile(keyFile, "Locallab", "Blurcbdl_" + index_str, pedited, spot.blurcbdl, spotEdited.blurcbdl); + assignFromKeyfile(keyFile, "Locallab", "Softradiuscb_" + index_str, pedited, spot.softradiuscb, spotEdited.softradiuscb); + assignFromKeyfile(keyFile, "Locallab", "EnacbMask_" + index_str, pedited, spot.enacbMask, spotEdited.enacbMask); + assignFromKeyfile(keyFile, "Locallab", "CCmaskcbCurve_" + index_str, pedited, spot.CCmaskcbcurve, spotEdited.CCmaskcbcurve); + assignFromKeyfile(keyFile, "Locallab", "LLmaskcbCurve_" + index_str, pedited, spot.LLmaskcbcurve, spotEdited.LLmaskcbcurve); + assignFromKeyfile(keyFile, "Locallab", "HHmaskcbCurve_" + index_str, pedited, spot.HHmaskcbcurve, spotEdited.HHmaskcbcurve); + assignFromKeyfile(keyFile, "Locallab", "Blendmaskcb_" + index_str, pedited, spot.blendmaskcb, spotEdited.blendmaskcb); + assignFromKeyfile(keyFile, "Locallab", "Radmaskcb_" + index_str, pedited, spot.radmaskcb, spotEdited.radmaskcb); + assignFromKeyfile(keyFile, "Locallab", "Chromaskcb_" + index_str, pedited, spot.chromaskcb, spotEdited.chromaskcb); + assignFromKeyfile(keyFile, "Locallab", "Gammaskcb_" + index_str, pedited, spot.gammaskcb, spotEdited.gammaskcb); + assignFromKeyfile(keyFile, "Locallab", "Slomaskcb_" + index_str, pedited, spot.slomaskcb, spotEdited.slomaskcb); + assignFromKeyfile(keyFile, "Locallab", "Lapmaskcb_" + index_str, pedited, spot.lapmaskcb, spotEdited.lapmaskcb); + assignFromKeyfile(keyFile, "Locallab", "LmaskcbCurve_" + index_str, pedited, spot.Lmaskcbcurve, spotEdited.Lmaskcbcurve); + // Log encoding + spot.visilog = assignFromKeyfile(keyFile, "Locallab", "Explog_" + index_str, pedited, spot.explog, spotEdited.explog); + + if (spot.visilog) { + spotEdited.visilog = true; + } + + assignFromKeyfile(keyFile, "Locallab", "Autocompute_" + index_str, pedited, spot.autocompute, spotEdited.autocompute); + assignFromKeyfile(keyFile, "Locallab", "SourceGray_" + index_str, pedited, spot.sourceGray, spotEdited.sourceGray); + assignFromKeyfile(keyFile, "Locallab", "TargetGray_" + index_str, pedited, spot.targetGray, spotEdited.targetGray); + assignFromKeyfile(keyFile, "Locallab", "AutoGray_" + index_str, pedited, spot.Autogray, spotEdited.Autogray); + assignFromKeyfile(keyFile, "Locallab", "Fullimage_" + index_str, pedited, spot.fullimage, spotEdited.fullimage); + assignFromKeyfile(keyFile, "Locallab", "BlackEv_" + index_str, pedited, spot.blackEv, spotEdited.blackEv); + assignFromKeyfile(keyFile, "Locallab", "WhiteEv_" + index_str, pedited, spot.whiteEv, spotEdited.whiteEv); + assignFromKeyfile(keyFile, "Locallab", "Detail_" + index_str, pedited, spot.detail, spotEdited.detail); + assignFromKeyfile(keyFile, "Locallab", "Sensilog_" + index_str, pedited, spot.sensilog, spotEdited.sensilog); + assignFromKeyfile(keyFile, "Locallab", "Baselog_" + index_str, pedited, spot.baselog, spotEdited.baselog); + assignFromKeyfile(keyFile, "Locallab", "Strlog_" + index_str, pedited, spot.strlog, spotEdited.strlog); + assignFromKeyfile(keyFile, "Locallab", "Anglog_" + index_str, pedited, spot.anglog, spotEdited.anglog); + + // Append LocallabSpot and LocallabParamsEdited + locallab.spots.push_back(spot); + + if (pedited) { + pedited->locallab.spots.push_back(spotEdited); + } + + // Update increment + ++i; + } + } + if (keyFile.has_group("PCVignette")) { assignFromKeyfile(keyFile, "PCVignette", "Enabled", pedited, pcvignette.enabled, pedited->pcvignette.enabled); assignFromKeyfile(keyFile, "PCVignette", "Strength", pedited, pcvignette.strength, pedited->pcvignette.strength); @@ -4880,7 +7670,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) assignFromKeyfile(keyFile, "Wavelet", "ContrastCurve", pedited, wavelet.ccwcurve, pedited->wavelet.ccwcurve); assignFromKeyfile(keyFile, "Wavelet", "blcurve", pedited, wavelet.blcurve, pedited->wavelet.blcurve); assignFromKeyfile(keyFile, "Wavelet", "OpacityCurveRG", pedited, wavelet.opacityCurveRG, pedited->wavelet.opacityCurveRG); - assignFromKeyfile(keyFile, "Wavelet", "Levelshc", pedited, wavelet.opacityCurveSH, pedited->wavelet.opacityCurveSH); + assignFromKeyfile(keyFile, "Wavelet", "Levalshc", pedited, wavelet.opacityCurveSH, pedited->wavelet.opacityCurveSH); assignFromKeyfile(keyFile, "Wavelet", "OpacityCurveBY", pedited, wavelet.opacityCurveBY, pedited->wavelet.opacityCurveBY); assignFromKeyfile(keyFile, "Wavelet", "OpacityCurveW", pedited, wavelet.opacityCurveW, pedited->wavelet.opacityCurveW); assignFromKeyfile(keyFile, "Wavelet", "OpacityCurveWL", pedited, wavelet.opacityCurveWL, pedited->wavelet.opacityCurveWL); @@ -5434,9 +8224,11 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftEperIso", pedited, raw.bayersensor.pixelShiftEperIso, pedited->raw.bayersensor.pixelShiftEperIso); + if (ppVersion < 332) { raw.bayersensor.pixelShiftEperIso += 1.0; } + assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftSigma", pedited, raw.bayersensor.pixelShiftSigma, pedited->raw.bayersensor.pixelShiftSigma); assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftShowMotion", pedited, raw.bayersensor.pixelShiftShowMotion, pedited->raw.bayersensor.pixelShiftShowMotion); assignFromKeyfile(keyFile, "RAW Bayer", "PixelShiftShowMotionMaskOnly", pedited, raw.bayersensor.pixelShiftShowMotionMaskOnly, pedited->raw.bayersensor.pixelShiftShowMotionMaskOnly); @@ -5451,12 +8243,14 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) if (ppVersion < 336) { if (keyFile.has_key("RAW Bayer", "pixelShiftLmmse")) { - bool useLmmse = keyFile.get_boolean ("RAW Bayer", "pixelShiftLmmse"); + const bool useLmmse = keyFile.get_boolean("RAW Bayer", "pixelShiftLmmse"); + if (useLmmse) { raw.bayersensor.pixelShiftDemosaicMethod = raw.bayersensor.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::LMMSE); } else { raw.bayersensor.pixelShiftDemosaicMethod = raw.bayersensor.getPSDemosaicMethodString(RAWParams::BayerSensor::PSDemosaicMethod::AMAZE); } + if (pedited) { pedited->raw.bayersensor.pixelShiftDemosaicMethod = true; } @@ -5624,6 +8418,7 @@ bool ProcParams::operator ==(const ProcParams& other) const && lensProf == other.lensProf && perspective == other.perspective && gradient == other.gradient + && locallab == other.locallab && pcvignette == other.pcvignette && cacorrection == other.cacorrection && vignetting == other.vignetting diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 0c0631624..85be112e0 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -46,6 +46,16 @@ class WavOpacityCurveSH; class WavOpacityCurveRG; class WavOpacityCurveW; class WavOpacityCurveWL; +class LocretigainCurve; +class LocretigainCurverab; +class LocLHCurve; +class LocHHCurve; +class LocLLmaskCurve; +class LocCCmaskCurve; +class LocHHmaskCurve; +class LocLLmaskexpCurve; +class LocCCmaskexpCurve; +class LocHHmaskexpCurve; enum RenderingIntent : int { RI_PERCEPTUAL = INTENT_PERCEPTUAL, @@ -104,6 +114,12 @@ public: } } + template + typename std::enable_if::value, bool>::type operator !=(const Threshold& rhs) const + { + return !(*this == rhs); + } + T getBottom() const { return bottom_left; @@ -403,7 +419,6 @@ struct RGBCurvesParams { /** * Parameters of the Color Toning */ - struct ColorToningParams { bool enabled; bool autosat; @@ -491,6 +506,7 @@ struct ColorToningParams { void getCurves(ColorGradientCurve& colorCurveLUT, OpacityCurve& opacityCurveLUT, const double xyz_rgb[3][3], bool& opautili) const; }; + /** * Parameters of the sharpening */ @@ -530,8 +546,10 @@ struct SharpenEdgeParams { bool operator ==(const SharpenEdgeParams& other) const; bool operator !=(const SharpenEdgeParams& other) const; + }; + struct SharpenMicroParams { bool enabled; bool matrix; @@ -911,8 +929,21 @@ struct LensProfParams { * Parameters of the perspective correction */ struct PerspectiveParams { + Glib::ustring method; double horizontal; double vertical; + double camera_crop_factor; + double camera_focal_length; + double camera_pitch; + double camera_roll; + double camera_shift_horiz; + double camera_shift_vert; + double camera_yaw; + double projection_pitch; + double projection_rotate; + double projection_shift_horiz; + double projection_shift_vert; + double projection_yaw; PerspectiveParams(); @@ -937,6 +968,480 @@ struct GradientParams { bool operator !=(const GradientParams& other) const; }; +/** + * Parameters of the Local Lab + */ +struct LocallabParams { + struct LocallabSpot { + // Control spot settings + Glib::ustring name; + bool isvisible; + Glib::ustring shape; // ELI, RECT + Glib::ustring spotMethod; // norm, exc + Glib::ustring wavMethod; // D2, D4, D6, D10, D14 + int sensiexclu; + int structexclu; + double struc; + Glib::ustring shapeMethod; // IND, SYM, INDSL, SYMSL + std::vector loc; // For ellipse/rectangle: {locX, locXL, locY, locYT} + int centerX; + int centerY; + int circrad; + Glib::ustring qualityMethod; // none, std, enh, enhsup, contr, sob2 + Glib::ustring complexMethod; // sim, mod, all + double transit; + double feather; + double thresh; + double iter; + double balan; + double balanh; + double colorde; + double colorscope; + double transitweak; + double transitgrad; + bool avoid; + bool blwh; + bool recurs; + bool laplac; + bool deltae; + bool shortc; + bool savrest; + int scopemask; + int lumask; + // Color & Light + bool visicolor; + bool expcolor; + int complexcolor; + bool curvactiv; + int lightness; + int contrast; + int chroma; + double labgridALow; + double labgridBLow; + double labgridAHigh; + double labgridBHigh; + double labgridALowmerg; + double labgridBLowmerg; + double labgridAHighmerg; + double labgridBHighmerg; + int strengthgrid; + int sensi; + int structcol; + double strcol; + double strcolab; + double strcolh; + double angcol; + int blurcolde; + double blurcol; + double contcol; + int blendmaskcol; + double radmaskcol; + double chromaskcol; + double gammaskcol; + double slomaskcol; + int shadmaskcol; + double strumaskcol; + double lapmaskcol; + Glib::ustring qualitycurveMethod; // none, std + Glib::ustring gridMethod; // one, two + Glib::ustring merMethod; // mone, mtwo, mthr, mfou, mfiv + Glib::ustring toneMethod; // one, two, thr, fou + Glib::ustring mergecolMethod; // one, two, thr, fou, fiv, six, sev, sev0, sev1, sev2, hei, nin, ten, ele, twe, thi, for, hue, sat, col, lum + std::vector llcurve; + std::vector lccurve; + std::vector cccurve; + std::vector clcurve; + std::vector rgbcurve; + std::vector LHcurve; + std::vector HHcurve; + bool invers; + bool special; + bool toolcol; + bool enaColorMask; + bool fftColorMask; + std::vector CCmaskcurve; + std::vector LLmaskcurve; + std::vector HHmaskcurve; + std::vector HHhmaskcurve; + double softradiuscol; + double opacol; + double mercol; + double merlucol; + double conthrcol; + std::vector Lmaskcurve; + std::vector LLmaskcolcurvewav; + Threshold csthresholdcol; + // Exposure + bool visiexpose; + bool expexpose; + int complexexpose; + double expcomp; + int hlcompr; + int hlcomprthresh; + int black; + int shadex; + int shcompr; + int expchroma; + int sensiex; + int structexp; + int blurexpde; + double strexp; + double angexp; + std::vector excurve; + bool inversex; + bool enaExpMask; + bool enaExpMaskaft; + std::vector CCmaskexpcurve; + std::vector LLmaskexpcurve; + std::vector HHmaskexpcurve; + int blendmaskexp; + double radmaskexp; + double chromaskexp; + double gammaskexp; + double slomaskexp; + double lapmaskexp; + double strmaskexp; + double angmaskexp; + double softradiusexp; + std::vector Lmaskexpcurve; + Glib::ustring expMethod; // std, pde + Glib::ustring exnoiseMethod; // none, med, medhi + double laplacexp; + double balanexp; + double linear; + double gamm; + double fatamount; + double fatdetail; + double fatanchor; + double fatlevel; + // Shadow highlight + bool visishadhigh; + bool expshadhigh; + int complexshadhigh; + Glib::ustring shMethod; // std, tone + int multsh[5]; + int highlights; + int h_tonalwidth; + int shadows; + int s_tonalwidth; + int sh_radius; + int sensihs; + bool enaSHMask; + std::vector CCmaskSHcurve; + std::vector LLmaskSHcurve; + std::vector HHmaskSHcurve; + int blendmaskSH; + double radmaskSH; + int blurSHde; + double strSH; + double angSH; + bool inverssh; + double chromaskSH; + double gammaskSH; + double slomaskSH; + double lapmaskSH; + int detailSH; + std::vector LmaskSHcurve; + double fatamountSH; + double fatanchorSH; + double gamSH; + double sloSH; + // Vibrance + bool visivibrance; + bool expvibrance; + int complexvibrance; + int saturated; + int pastels; + int warm; + Threshold psthreshold; + bool protectskins; + bool avoidcolorshift; + bool pastsattog; + int sensiv; + std::vector skintonescurve; + std::vector CCmaskvibcurve; + std::vector LLmaskvibcurve; + std::vector HHmaskvibcurve; + bool enavibMask; + int blendmaskvib; + double radmaskvib; + double chromaskvib; + double gammaskvib; + double slomaskvib; + double lapmaskvib; + double strvib; + double strvibab; + double strvibh; + double angvib; + std::vector Lmaskvibcurve; + // Soft Light + bool visisoft; + bool expsoft; + int complexsoft; + int streng; + int sensisf; + double laplace; + Glib::ustring softMethod; // soft, reti + // Blur & Noise + bool visiblur; + bool expblur; + int complexblur; + double radius; + int strength; + int sensibn; + int itera; + int guidbl; + int strbl; + int isogr; + int strengr; + int scalegr; + int epsbl; + Glib::ustring blMethod; // blur, med, guid + Glib::ustring chroMethod; // lum, chr, all + Glib::ustring blurMethod; // norm, inv + Glib::ustring medMethod; // none, 33, 55, 77, 99 + bool activlum; + double noiselumf; + double noiselumf0; + double noiselumf2; + double noiselumc; + double noiselumdetail; + int noiselequal; + double noisechrof; + double noisechroc; + double noisechrodetail; + int adjblur; + int bilateral; + int sensiden; + int detailthr; + std::vector locwavcurveden; + Glib::ustring showmaskblMethodtyp; + std::vector CCmaskblcurve; + std::vector LLmaskblcurve; + std::vector HHmaskblcurve; + bool enablMask; + bool fftwbl; + bool toolbl; + int blendmaskbl; + double radmaskbl; + double chromaskbl; + double gammaskbl; + double slomaskbl; + double lapmaskbl; + int shadmaskbl; + int shadmaskblsha; + double strumaskbl; + std::vector Lmaskblcurve; + std::vector LLmaskblcurvewav; + Threshold csthresholdblur; + // Tone Mapping + bool visitonemap; + bool exptonemap; + int complextonemap; + double stren; + double gamma; + double estop; + double scaltm; + int rewei; + double satur; + int sensitm; + double softradiustm; + double amount; + bool equiltm; + std::vector CCmasktmcurve; + std::vector LLmasktmcurve; + std::vector HHmasktmcurve; + bool enatmMask; + bool enatmMaskaft; + int blendmasktm; + double radmasktm; + double chromasktm; + double gammasktm; + double slomasktm; + double lapmasktm; + std::vector Lmasktmcurve; + // Retinex + bool visireti; + bool expreti; + int complexreti; + Glib::ustring retinexMethod; // low, uni, high + double str; + double chrrt; + double neigh; + double vart; + double offs; + int dehaz; + int depth; + int sensih; + std::vector localTgaincurve; + std::vector localTtranscurve; + bool inversret; + bool equilret; + bool loglin; + bool lumonly; + double softradiusret; + std::vector CCmaskreticurve; + std::vector LLmaskreticurve; + std::vector HHmaskreticurve; + bool enaretiMask; + bool enaretiMasktmap; + int blendmaskreti; + double radmaskreti; + double chromaskreti; + double gammaskreti; + double slomaskreti; + double lapmaskreti; + double scalereti; + double darkness; + double lightnessreti; + double limd; + double cliptm; + bool fftwreti; + std::vector Lmaskreticurve; + // Sharpening + bool visisharp; + bool expsharp; + int complexsharp; + int sharcontrast; + double sharradius; + int sharamount; + int shardamping; + int shariter; + double sharblur; + int sensisha; + bool inverssha; + // Local Contrast + bool visicontrast; + bool expcontrast; + int complexcontrast; + int lcradius; + double lcamount; + double lcdarkness; + double lclightness; + double sigmalc; + int levelwav; + double residcont; + double residsha; + double residshathr; + double residhi; + double residhithr; + double residblur; + double levelblur; + double sigmabl; + double residchro; + double residcomp; + double sigma; + double offset; + double sigmadr; + double threswav; + double chromalev; + double chromablu; + double sigmadc; + double deltad; + double fatres; + double clarilres; + double claricres; + double clarisoft; + double sigmalc2; + double strwav; + double angwav; + double strengthw; + double sigmaed; + double radiusw; + double detailw; + double gradw; + double tloww; + double thigw; + double edgw; + double basew; + int sensilc; + bool fftwlc; + bool blurlc; + bool wavblur; + bool wavedg; + bool waveshow; + bool wavcont; + bool wavcomp; + bool wavgradl; + bool wavcompre; + bool origlc; + Glib::ustring localcontMethod; // loc, wav + Glib::ustring localedgMethod; // fir, sec, thr + Glib::ustring localneiMethod; // none, low, high + std::vector locwavcurve; + Threshold csthreshold; + std::vector loclevwavcurve; + std::vector locconwavcurve; + std::vector loccompwavcurve; + std::vector loccomprewavcurve; + std::vector locedgwavcurve; + std::vector CCmasklccurve; + std::vector LLmasklccurve; + std::vector HHmasklccurve; + bool enalcMask; + int blendmasklc; + double radmasklc; + double chromasklc; + std::vector Lmasklccurve; + // Contrast by detail levels + bool visicbdl; + bool expcbdl; + int complexcbdl; + double mult[6]; + double chromacbdl; + double threshold; + int sensicb; + double clarityml; + int contresid; + double blurcbdl; + double softradiuscb; + bool enacbMask; + std::vector CCmaskcbcurve; + std::vector LLmaskcbcurve; + std::vector HHmaskcbcurve; + int blendmaskcb; + double radmaskcb; + double chromaskcb; + double gammaskcb; + double slomaskcb; + double lapmaskcb; + std::vector Lmaskcbcurve; + // Log encoding + bool visilog; + bool explog; + bool autocompute; + double sourceGray; + double targetGray; + bool Autogray; + bool fullimage; + double blackEv; + double whiteEv; + double detail; + int sensilog; + double baselog; + double strlog; + double anglog; + + LocallabSpot(); + + bool operator ==(const LocallabSpot& other) const; + bool operator !=(const LocallabSpot& other) const; + }; + + static const double LABGRIDL_CORR_MAX; + static const double LABGRIDL_CORR_SCALE; + static const double LABGRIDL_DIRECT_SCALE; + + bool enabled; + int selspot; + std::vector spots; + + LocallabParams(); + + bool operator ==(const LocallabParams& other) const; + bool operator !=(const LocallabParams& other) const; +}; + /** * Parameters of the post-crop vignette filter */ @@ -1638,6 +2143,7 @@ public: LensProfParams lensProf; ///< Lens correction profile parameters PerspectiveParams perspective; ///< Perspective correction parameters GradientParams gradient; ///< Gradient filter parameters + LocallabParams locallab; ///< Local lab parameters PCVignetteParams pcvignette; ///< Post-crop vignette filter parameters CACorrParams cacorrection; ///< Lens c/a correction parameters VignettingParams vignetting; ///< Lens vignetting correction parameters diff --git a/rtengine/rawimage.cc b/rtengine/rawimage.cc index c2df70468..e3a747048 100644 --- a/rtengine/rawimage.cc +++ b/rtengine/rawimage.cc @@ -20,7 +20,7 @@ namespace rtengine { -RawImage::RawImage( const Glib::ustring &name ) +RawImage::RawImage(const Glib::ustring &name) : data(nullptr) , prefilters(0) , filename(name) @@ -36,31 +36,31 @@ RawImage::RawImage( const Glib::ustring &name ) RawImage::~RawImage() { - if(ifp) { + if (ifp) { fclose(ifp); ifp = nullptr; } - if( image ) { + if (image) { free(image); } - if(allocation) { + if (allocation) { delete [] allocation; allocation = nullptr; } - if(float_raw_image) { + if (float_raw_image) { delete [] float_raw_image; float_raw_image = nullptr; } - if(data) { + if (data) { delete [] data; data = nullptr; } - if(profile_data) { + if (profile_data) { delete [] profile_data; profile_data = nullptr; } @@ -82,15 +82,38 @@ eSensorType RawImage::getSensorType() const /* Similar to dcraw scale_colors for coeff. calculation, but without actual pixels scaling. * need pixels in data[][] available */ -void RawImage::get_colorsCoeff( float *pre_mul_, float *scale_mul_, float *cblack_, bool forceAutoWB) +void RawImage::get_colorsCoeff(float *pre_mul_, float *scale_mul_, float *cblack_, bool forceAutoWB) { + if (!pre_mul_ && !scale_mul_ && !forceAutoWB) { + // only black levels + if (isXtrans()) { + // for xtrans files dcraw stores black levels in cblack[6] .. cblack[41], but all are equal, so we just use cblack[6] + for (int c = 0; c < 4; c++) { + cblack_[c] = (float) this->get_cblack(6); + } + } else if ((this->get_cblack(4) + 1) / 2 == 1 && (this->get_cblack(5) + 1) / 2 == 1) { + for (int c = 0; c < 4; c++) { + cblack_[c] = this->get_cblack(c); + } + + for (int c = 0; c < 4; c++) { + cblack_[FC(c / 2, c % 2)] = this->get_cblack(6 + c / 2 % this->get_cblack(4) * this->get_cblack(5) + c % 2 % this->get_cblack(5)); + } + } else { + for (int c = 0; c < 4; c++) { + cblack_[c] = (float) this->get_cblack(c); + } + } + return; + } + unsigned sum[8], c; unsigned W = this->get_width(); unsigned H = this->get_height(); float val; double dsum[8], dmin, dmax; - if(isXtrans()) { + if (isXtrans()) { // for xtrans files dcraw stores black levels in cblack[6] .. cblack[41], but all are equal, so we just use cblack[6] for (int c = 0; c < 4; c++) { cblack_[c] = (float) this->get_cblack(6); @@ -100,6 +123,7 @@ void RawImage::get_colorsCoeff( float *pre_mul_, float *scale_mul_, float *cblac for (int c = 0; c < 4; c++) { cblack_[c] = this->get_cblack(c); } + for (int c = 0; c < 4; c++) { cblack_[FC(c / 2, c % 2)] = this->get_cblack(6 + c / 2 % this->get_cblack(4) * this->get_cblack(5) + c % 2 % this->get_cblack(5)); pre_mul_[c] = this->get_pre_mul(c); @@ -112,9 +136,10 @@ void RawImage::get_colorsCoeff( float *pre_mul_, float *scale_mul_, float *cblac } if (this->get_cam_mul(0) == -1 || forceAutoWB) { - if(!data) { // this happens only for thumbnail creation when get_cam_mul(0) == -1 + if (!data) { // this happens only for thumbnail creation when get_cam_mul(0) == -1 compress_image(0, false); } + memset(dsum, 0, sizeof dsum); constexpr float blackThreshold = 8.f; @@ -194,11 +219,11 @@ skip_block2: } } - for(int c = 0; c < 4; c++) { + for (int c = 0; c < 4; c++) { dsum[c] -= static_cast(cblack_[c]) * dsum[c + 4]; } - } else if(isXtrans()) { + } else if (isXtrans()) { #ifdef _OPENMP #pragma omp parallel #endif @@ -313,8 +338,7 @@ skip_block: if (sum[0] && sum[1] && sum[2] && sum[3]) for (int c = 0; c < 4; c++) { pre_mul_[c] = (float) sum[c + 4] / sum[c]; - } - else if (this->get_cam_mul(0) && this->get_cam_mul(2)) { + } else if (this->get_cam_mul(0) && this->get_cam_mul(2)) { pre_mul_[0] = this->get_cam_mul(0); pre_mul_[1] = this->get_cam_mul(1); pre_mul_[2] = this->get_cam_mul(2); @@ -412,17 +436,17 @@ skip_block: } } -int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, ProgressListener *plistener, double progressRange) +int RawImage::loadRaw(bool loadData, unsigned int imageNum, bool closeFile, ProgressListener *plistener, double progressRange) { ifname = filename.c_str(); image = nullptr; verbose = settings->verbose; oprof = nullptr; - if(!ifp) { - ifp = gfopen (ifname); // Maps to either file map or direct fopen + if (!ifp) { + ifp = gfopen(ifname); // Maps to either file map or direct fopen } else { - fseek (ifp, 0, SEEK_SET); + fseek(ifp, 0, SEEK_SET); } if (!ifp) { @@ -458,7 +482,7 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro return 2; } - if(!strcmp(make,"Fujifilm") && raw_height * raw_width * 2u != raw_size) { + if (!strcmp(make, "Fujifilm") && raw_height * raw_width * 2u != raw_size) { if (raw_width * raw_height * 7u / 4u == raw_size) { load_raw = &RawImage::fuji_14bit_load_raw; } else { @@ -478,13 +502,13 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro this->rotate_deg = 0; } - if( loadData ) { + if (loadData) { use_camera_wb = 1; shrink = 0; if (settings->verbose) { - printf ("Loading %s %s image from %s...\n", make, model, filename.c_str()); + printf("Loading %s %s image from %s...\n", make, model, filename.c_str()); } iheight = height; @@ -492,16 +516,15 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro if (filters || colors == 1) { raw_image = (ushort *) calloc ((static_cast(raw_height) + 7u) * static_cast(raw_width), 2); - merror (raw_image, "main()"); + merror(raw_image, "main()"); } // dcraw needs this global variable to hold pixel data image = (dcrawImage_t)calloc (static_cast(height) * static_cast(width) * sizeof * image + meta_length, 1); - meta_data = (char *) (image + static_cast(height) * static_cast(width)); - if(!image) { return 200; } + meta_data = (char *) (image + static_cast(height) * static_cast(width)); /* Issue 2467 if (setjmp (failure)) { @@ -512,7 +535,7 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro } */ // Load raw pixels data - fseek (ifp, data_offset, SEEK_SET); + fseek(ifp, data_offset, SEEK_SET); (this->*load_raw)(); if (!float_raw_image) { // apply baseline exposure only for float DNGs @@ -530,13 +553,15 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro if (cc && cc->has_rawCrop()) { int lm, tm, w, h; cc->get_rawCrop(lm, tm, w, h); - if(isXtrans()) { - shiftXtransMatrix(6 - ((top_margin - tm)%6), 6 - ((left_margin - lm)%6)); + + if (isXtrans()) { + shiftXtransMatrix(6 - ((top_margin - tm) % 6), 6 - ((left_margin - lm) % 6)); } else { - if(((int)top_margin - tm) & 1) { // we have an odd border difference + if (((int)top_margin - tm) & 1) { // we have an odd border difference filters = (filters << 4) | (filters >> 28); // left rotate filters by 4 bits } } + left_margin = lm; top_margin = tm; @@ -566,7 +591,7 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro } crop_masked_pixels(); - free (raw_image); + free(raw_image); raw_image = nullptr; } else { if (get_maker() == "Sigma" && cc && cc->has_rawCrop()) { // foveon images @@ -594,8 +619,8 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro // Load embedded profile if (profile_length) { profile_data = new char[profile_length]; - fseek ( ifp, profile_offset, SEEK_SET); - fread ( profile_data, 1, profile_length, ifp); + fseek(ifp, profile_offset, SEEK_SET); + fread(profile_data, 1, profile_length, ifp); } /* @@ -625,10 +650,10 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro if (RT_whitelevel_from_constant == ThreeValBool::T) { maximum_c4[i] = cc->get_WhiteLevel(i, iso_speed, aperture); - if(tiff_bps > 0 && maximum_c4[i] > 0 && !isFoveon()) { + if (tiff_bps > 0 && maximum_c4[i] > 0 && !isFoveon()) { unsigned compare = ((uint64_t)1 << tiff_bps) - 1; // use uint64_t to avoid overflow if tiff_bps == 32 - while(static_cast(maximum_c4[i]) > compare) { + while (static_cast(maximum_c4[i]) > compare) { maximum_c4[i] >>= 1; } } @@ -637,11 +662,10 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro } if (black_c4[0] == -1) { - if(isXtrans()) + if (isXtrans()) for (int c = 0; c < 4; c++) { black_c4[c] = cblack[6]; - } - else + } else // RT constants not set, bring in the DCRAW single channel black constant for (int c = 0; c < 4; c++) { @@ -677,7 +701,7 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro } } - if ( closeFile ) { + if (closeFile) { fclose(ifp); ifp = nullptr; } @@ -691,7 +715,7 @@ int RawImage::loadRaw (bool loadData, unsigned int imageNum, bool closeFile, Pro float** RawImage::compress_image(unsigned int frameNum, bool freeImage) { - if( !image ) { + if (!image) { return nullptr; } @@ -727,7 +751,7 @@ float** RawImage::compress_image(unsigned int frameNum, bool freeImage) } // copy pixel raw data: the compressed format earns space - if( float_raw_image ) { + if (float_raw_image) { #ifdef _OPENMP #pragma omp parallel for #endif @@ -783,50 +807,51 @@ float** RawImage::compress_image(unsigned int frameNum, bool freeImage) } } - if(freeImage) { + if (freeImage) { free(image); // we don't need this anymore image = nullptr; } + return data; } bool RawImage::is_supportedThumb() const { - return ( (thumb_width * thumb_height) > 0 && - ( write_thumb == &rtengine::RawImage::jpeg_thumb || - write_thumb == &rtengine::RawImage::ppm_thumb) && - !thumb_load_raw ); + return ((thumb_width * thumb_height) > 0 && + (write_thumb == &rtengine::RawImage::jpeg_thumb || + write_thumb == &rtengine::RawImage::ppm_thumb) && + !thumb_load_raw); } bool RawImage::is_jpegThumb() const { - return ( (thumb_width * thumb_height) > 0 && - write_thumb == &rtengine::RawImage::jpeg_thumb && - !thumb_load_raw ); + return ((thumb_width * thumb_height) > 0 && + write_thumb == &rtengine::RawImage::jpeg_thumb && + !thumb_load_raw); } bool RawImage::is_ppmThumb() const { - return ( (thumb_width * thumb_height) > 0 && - write_thumb == &rtengine::RawImage::ppm_thumb && - !thumb_load_raw ); + return ((thumb_width * thumb_height) > 0 && + write_thumb == &rtengine::RawImage::ppm_thumb && + !thumb_load_raw); } -void RawImage::getXtransMatrix( int XtransMatrix[6][6]) +void RawImage::getXtransMatrix(int XtransMatrix[6][6]) { - for(int row = 0; row < 6; row++) - for(int col = 0; col < 6; col++) { + for (int row = 0; row < 6; row++) + for (int col = 0; col < 6; col++) { XtransMatrix[row][col] = xtrans[row][col]; } } -void RawImage::getRgbCam (float rgbcam[3][4]) +void RawImage::getRgbCam(float rgbcam[3][4]) { - for(int row = 0; row < 3; row++) - for(int col = 0; col < 4; col++) { + for (int row = 0; row < 3; row++) + for (int col = 0; col < 4; col++) { rgbcam[row][col] = rgb_cam[row][col]; } diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 09aaed7ad..871267dac 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -33,11 +33,11 @@ class RawImage: public DCraw { public: - explicit RawImage( const Glib::ustring &name ); + explicit RawImage(const Glib::ustring &name); ~RawImage(); - int loadRaw (bool loadData, unsigned int imageNum = 0, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); - void get_colorsCoeff( float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB ); + int loadRaw(bool loadData, unsigned int imageNum = 0, bool closeFile = true, ProgressListener *plistener = nullptr, double progressRange = 1.0); + void get_colorsCoeff(float* pre_mul_, float* scale_mul_, float* cblack_, bool forceAutoWB); void set_prefilters() { if (isBayer() && get_colors() == 3) { @@ -55,6 +55,7 @@ public: unsigned int getFrameCount() const { return is_raw; } double getBaselineExposure() const { return RT_baseline_exposure; } + protected: Glib::ustring filename; // complete filename int rotate_deg; // 0,90,180,270 degree of rotation: info taken by dcraw from exif @@ -115,8 +116,8 @@ public: eSensorType getSensorType() const; - void getRgbCam (float rgbcam[3][4]); - void getXtransMatrix ( int xtransMatrix[6][6]); + void getRgbCam(float rgbcam[3][4]); + void getXtransMatrix(int xtransMatrix[6][6]); unsigned get_filters() const { return filters; @@ -137,7 +138,7 @@ public: return maximum; } } - unsigned short get_whiteSample( int r, int c ) const + unsigned short get_whiteSample(int r, int c) const { return white[r][c]; } @@ -171,13 +172,13 @@ public: return std::string(model); } - float get_cam_mul(int c )const + float get_cam_mul(int c)const { return cam_mul[c]; } - float get_pre_mul(int c )const + float get_pre_mul(int c)const { - if(std::isfinite(pre_mul[c])) { + if (std::isfinite(pre_mul[c])) { return pre_mul[c]; } else { std::cout << "Failure decoding '" << filename << "', please file a bug report including the raw file and the line below:" << std::endl; @@ -185,7 +186,7 @@ public: return 1.f; } } - float get_rgb_cam( int r, int c) const + float get_rgb_cam(int r, int c) const { return rgb_cam[r][c]; } @@ -258,6 +259,10 @@ public: { return float_raw_image; } + void set_filters(unsigned f) + { + filters = f; + } public: // dcraw functions @@ -267,7 +272,7 @@ public: } public: - bool ISRED (unsigned row, unsigned col) const + bool ISRED(unsigned row, unsigned col) const { return ((filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) == 0); } @@ -275,15 +280,15 @@ public: { return ((filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) == 1); } - bool ISBLUE (unsigned row, unsigned col) const + bool ISBLUE(unsigned row, unsigned col) const { return ((filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) == 2); } - unsigned FC (unsigned row, unsigned col) const + unsigned FC(unsigned row, unsigned col) const { return (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3); } - bool ISXTRANSRED (unsigned row, unsigned col) const + bool ISXTRANSRED(unsigned row, unsigned col) const { return ((xtrans[(row) % 6][(col) % 6]) == 0); } @@ -291,16 +296,16 @@ public: { return ((xtrans[(row) % 6][(col) % 6]) == 1); } - bool ISXTRANSBLUE (unsigned row, unsigned col) const + bool ISXTRANSBLUE(unsigned row, unsigned col) const { return ((xtrans[(row) % 6][(col) % 6]) == 2); } - unsigned XTRANSFC (unsigned row, unsigned col) const + unsigned XTRANSFC(unsigned row, unsigned col) const { return (xtrans[(row) % 6][(col) % 6]); } - unsigned DNGVERSION ( ) const + unsigned DNGVERSION() const { return dng_version; } diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 7d3f5ab2c..2c7ddd36a 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -2261,15 +2261,8 @@ void RawImageSource::retinex(const ColorManagementParams& cmp, const RetinexPara } float R, G, B; -#ifdef _DEBUG - bool neg = false; - bool more_rgb = false; - //gamut control : Lab values are in gamut - Color::gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f, neg, more_rgb); -#else //gamut control : Lab values are in gamut Color::gamutLchonly(HH, sincosval, Lprov1, Chprov1, R, G, B, wip, highlight, 0.15f, 0.96f); -#endif @@ -2410,10 +2403,11 @@ void RawImageSource::HLRecovery_Global(const ToneCurveParams &hrp) */ void RawImageSource::copyOriginalPixels(const RAWParams &raw, RawImage *src, RawImage *riDark, RawImage *riFlatFile, array2D &rawData) { - const float black[4] = { - static_cast(ri->get_cblack(0)), static_cast(ri->get_cblack(1)), - static_cast(ri->get_cblack(2)), static_cast(ri->get_cblack(3)) - }; + const auto tmpfilters = ri->get_filters(); + ri->set_filters(ri->prefilters); // we need 4 blacks for bayer processing + float black[4]; + ri->get_colorsCoeff(nullptr, nullptr, black, false); + ri->set_filters(tmpfilters); if (ri->getSensorType() == ST_BAYER || ri->getSensorType() == ST_FUJI_XTRANS) { if (!rawData) { @@ -4433,17 +4427,17 @@ void RawImageSource::ItcWB(bool extra, double &tempref, double &greenref, double 1) for the current raw file we create a table for each temp of RGB multipliers 2) then, I choose the "camera temp" to initialize calculation (why not) - 3) for this temp, I calculated XYZ values for the 201 spectral datas + 3) for this temp, I calculated XYZ values for the 201 spectral data 4) then I create for the image an "histogram", but for xyY (CIE 1931 color space or CIE 1964 (default)) - 5) for each pixel (in fact to accelerate only 1/5 for and 1/5 for y), I determine for each couple xy, the number of occurences, can be change by Itcwb_precis to 3 or 9 + 5) for each pixel (in fact to accelerate only 1/5 for and 1/5 for y), I determine for each couple xy, the number of occurrences, can be change by Itcwb_precis to 3 or 9 6) I sort this result in ascending order 7) in option we can sort in another manner to take into account chroma : chromax = x - white point x, chromay = y - white point y - 8) then I compare this result, with spectral datas found above in 3) with deltaE (limited to chroma) - 9) at this point we have xyY values that match Camera temp, and spectral datas associated + 8) then I compare this result, with spectral data found above in 3) with deltaE (limited to chroma) + 9) at this point we have xyY values that match Camera temp, and spectral data associated 10) then I recalculate RGB values from xyY histogram 11) after, I vary temp, between 2000K to 12000K 12) RGB values are recalculated from 10) with RGB multipliers, and then xyY are calculated for each temp - 13) spectral datas choose are recalculated with temp between 2000K to 12000K with matrix spectral calculation, that leads to xyY values + 13) spectral data choose are recalculated with temp between 2000K to 12000K with matrix spectral calculation, that leads to xyY values 14) I calculated for each couple xy, Student correlation (without Snedecor test) 15) the good result, is the best correlation 16) we have found the best temperature where color image and color references are correlate @@ -5000,7 +4994,7 @@ void RawImageSource::ItcWB(bool extra, double &tempref, double &greenref, double chrom wbchro[sizcu4]; const float swpr = Txyz[repref].XX + Txyz[repref].ZZ + 1.f; - const float xwpr = Txyz[repref].XX / swpr;//white point for tt in xy coordiantes + const float xwpr = Txyz[repref].XX / swpr;//white point for tt in xy coordinates const float ywpr = 1.f / swpr; for (int i = 0; i < sizcu4; ++i) { //take the max values @@ -5031,7 +5025,7 @@ void RawImageSource::ItcWB(bool extra, double &tempref, double &greenref, double std::sort(wbchro, wbchro + sizcu4, wbchro[0]); } - const int maxval = rtengine::LIM(settings->itcwb_thres, 10, 55);//max values of color to find correllation + const int maxval = rtengine::LIM(settings->itcwb_thres, 10, 55);//max values of color to find correlation sizcurr2ref = rtengine::min(sizcurr2ref, maxval); //keep about the biggest values, @@ -5045,7 +5039,7 @@ void RawImageSource::ItcWB(bool extra, double &tempref, double &greenref, double } } - //calculate deltaE xx to find best values of spectrals datas - limited to chroma values + //calculate deltaE xx to find best values of spectrals data - limited to chroma values int maxnb = rtengine::LIM(settings->itcwb_sizereference, 1, 5); if (settings->itcwb_thres > 55) { @@ -5213,8 +5207,8 @@ void RawImageSource::ItcWB(bool extra, double &tempref, double &greenref, double reff_spect_xxyy[2 * kkg + 1][tt] = reff_spect_xxyy_prov[2 * i + 1][tt]; } } - //now we have good spectral datas - //claculate student correlation + //now we have good spectral data + //calculate student correlation const float abstudgr = std::fabs(studentXY(xxyycurr_reduc, reff_spect_xxyy, 2 * w, 2 * kkg, tt)); if (abstudgr < minstudgr) { // find the minimum Student diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 712ecb073..79e52e7a7 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -46,7 +46,7 @@ private: static LUTf invGrad; // for fast_demosaic static LUTf initInvGrad (); static void colorSpaceConversion_ (Imagefloat* im, const procparams::ColorManagementParams& cmp, const ColorTemp &wb, double pre_mul[3], cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName); - int defTransform (int tran); + int defTransform (int tran); protected: MyMutex getImageMutex; // locks getImage @@ -107,10 +107,10 @@ protected: std::vector histMatchingCache; const std::unique_ptr histMatchingParams; - void processFalseColorCorrectionThread (Imagefloat* im, array2D &rbconv_Y, array2D &rbconv_I, array2D &rbconv_Q, array2D &rbout_I, array2D &rbout_Q, const int row_from, const int row_to); - void hlRecovery (const std::string &method, float* red, float* green, float* blue, int width, float* hlmax); - void transformRect (const PreviewProps &pp, int tran, int &sx1, int &sy1, int &width, int &height, int &fw); - void transformPosition (int x, int y, int tran, int& tx, int& ty); + void processFalseColorCorrectionThread(Imagefloat* im, array2D &rbconv_Y, array2D &rbconv_I, array2D &rbconv_Q, array2D &rbout_I, array2D &rbout_Q, const int row_from, const int row_to); + void hlRecovery(const std::string &method, float* red, float* green, float* blue, int width, float* hlmax); + void transformRect(const PreviewProps &pp, int tran, int &sx1, int &sy1, int &width, int &height, int &fw); + void transformPosition(int x, int y, int tran, int& tx, int& ty); void ItcWB(bool extra, double &tempref, double &greenref, double &tempitc, double &greenitc, float &studgood, array2D &redloc, array2D &greenloc, array2D &blueloc, int bfw, int bfh, double &avg_rm, double &avg_gm, double &avg_bm, const procparams::ColorManagementParams &cmp, const procparams::RAWParams &raw, const procparams::WBParams & wbpar); unsigned FC(int row, int col) const; @@ -190,11 +190,11 @@ public: void convertColorSpace(Imagefloat* image, const procparams::ColorManagementParams &cmp, const ColorTemp &wb) override; static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in); - static void colorSpaceConversion (Imagefloat* im, const procparams::ColorManagementParams& cmp, const ColorTemp &wb, double pre_mul[3], cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName) + static void colorSpaceConversion(Imagefloat* im, const procparams::ColorManagementParams& cmp, const ColorTemp &wb, double pre_mul[3], cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], const std::string &camName) { - colorSpaceConversion_ (im, cmp, wb, pre_mul, embedded, camprofile, cam, camName); + colorSpaceConversion_(im, cmp, wb, pre_mul, embedded, camprofile, cam, camName); } - static void inverse33 (const double (*coeff)[3], double (*icoeff)[3]); + static void inverse33(const double (*coeff)[3], double (*icoeff)[3]); void MSR(float** luminance, float **originalLuminance, float **exLuminance, const LUTf& mapcurve, bool mapcontlutili, int width, int height, const procparams::RetinexParams &deh, const RetinextransmissionCurve & dehatransmissionCurve, const RetinexgaintransmissionCurve & dehagaintransmissionCurve, float &minCD, float &maxCD, float &mini, float &maxi, float &Tmean, float &Tsigma, float &Tmin, float &Tmax); void HLRecovery_inpaint (float** red, float** green, float** blue) override; @@ -232,10 +232,10 @@ public: protected: typedef unsigned short ushort; - void processFalseColorCorrection (Imagefloat* i, const int steps); - inline void convert_row_to_YIQ (const float* const r, const float* const g, const float* const b, float* Y, float* I, float* Q, const int W); - inline void convert_row_to_RGB (float* r, float* g, float* b, const float* const Y, const float* const I, const float* const Q, const int W); - inline void convert_to_RGB (float &r, float &g, float &b, const float Y, const float I, const float Q); + void processFalseColorCorrection(Imagefloat* i, const int steps); + inline void convert_row_to_YIQ(const float* const r, const float* const g, const float* const b, float* Y, float* I, float* Q, const int W); + inline void convert_row_to_RGB(float* r, float* g, float* b, const float* const Y, const float* const I, const float* const Q, const int W); + inline void convert_to_RGB(float &r, float &g, float &b, const float Y, const float I, const float Q); inline void interpolate_row_g (float* agh, float* agv, int i); inline void interpolate_row_rb (float* ar, float* ab, float* pg, float* cg, float* ng, int i); @@ -265,7 +265,7 @@ protected: int findZeroPixels(PixelsMap &bpMap) const; void cfa_linedn (float linenoiselevel, bool horizontal, bool vertical, const CFALineDenoiseRowBlender &rowblender);//Emil's line denoise - void green_equilibrate_global (array2D &rawData); + void green_equilibrate_global(array2D &rawData); void green_equilibrate (const GreenEqulibrateThreshold &greenthresh, array2D &rawData);//Emil's green equilibration void nodemosaic(bool bw); @@ -282,8 +282,8 @@ protected: void rcd_demosaic(size_t chunkSize = 1, bool measure = false); void border_interpolate(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); - void fill_raw( float (*cache )[3], int x0, int y0, float** rawData); - void fill_border( float (*cache )[3], int border, int x0, int y0); + void fill_raw(float (*cache)[3], int x0, int y0, float** rawData); + void fill_border(float (*cache)[3], int border, int x0, int y0); void copy_to_buffer(float (*image2)[2], float (*image)[3]); void dcb_hid(float (*image)[3], int x0, int y0); void dcb_color(float (*image)[3], int x0, int y0); @@ -295,7 +295,7 @@ protected: void restore_from_buffer(float (*image)[3], float (*image2)[2]); void dcb_refinement(float (*image)[3], uint8_t *map, int x0, int y0); 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 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, size_t chunkSize = 1, bool measure = false); void fast_xtrans_interpolate (const array2D &rawData, array2D &red, array2D &green, array2D &blue); diff --git a/rtengine/refreshmap.cc b/rtengine/refreshmap.cc index b77eac29c..773ec8b68 100644 --- a/rtengine/refreshmap.cc +++ b/rtengine/refreshmap.cc @@ -74,7 +74,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { 0, // EvLDNEdgeTolerance: obsolete, 0, // EvCDNEnabled:obsolete, 0, // free entry - RGBCURVE|M_AUTOEXP, // EvDCPToneCurve, + RGBCURVE | M_AUTOEXP, // EvDCPToneCurve, ALLNORAW, // EvDCPIlluminant, RETINEX, // EvSHEnabled, RGBCURVE, // EvSHHighlights, @@ -419,8 +419,8 @@ int refreshmap[rtengine::NUMOFEVENTS] = { DIRPYREQUALIZER, // EvWavgreenlow DIRPYREQUALIZER, // EvWavbluelow DIRPYREQUALIZER, // EvWavNeutral - RGBCURVE|M_AUTOEXP, // EvDCPApplyLookTable, - RGBCURVE|M_AUTOEXP, // EvDCPApplyBaselineExposureOffset, + RGBCURVE | M_AUTOEXP, // EvDCPApplyLookTable, + RGBCURVE | M_AUTOEXP, // EvDCPApplyBaselineExposureOffset, ALLNORAW, // EvDCPApplyHueSatMap DIRPYREQUALIZER, // EvWavenacont DIRPYREQUALIZER, // EvWavenachrom @@ -470,7 +470,7 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RETINEX, // EvRetinexgaintransmission RETINEX, // EvLskal OUTPUTPROFILE, // EvOBPCompens - ALLNORAW, // EvWBtempBias + ALLNORAW, // EvWBtempBias DARKFRAME, // EvRawImageNum 0, // unused 0, // unused @@ -521,7 +521,439 @@ int refreshmap[rtengine::NUMOFEVENTS] = { RGBCURVE, // EvRGBEnabled LUMINANCECURVE, // EvLEnabled DEMOSAIC, // EvPdShrEnabled - CAPTURESHARPEN // EvPdShrMaskToggled + CAPTURESHARPEN, // EvPdShrMaskToggled + LUMINANCECURVE, // EvLocallabSpotDeleted + M_VOID, // EvLocallabSpotSelected + M_VOID, // EvLocallabSpotName + M_VOID, // EvLocallabSpotVisibility + LUMINANCECURVE, // EvLocallabSpotShape + LUMINANCECURVE, // EvLocallabSpotSpotMethod + LUMINANCECURVE, // EvLocallabSpotShapeMethod + LUMINANCECURVE, // EvLocallabSpotLocX + LUMINANCECURVE, // EvLocallabSpotLocXL + LUMINANCECURVE, // EvLocallabSpotLocY + LUMINANCECURVE, // EvLocallabSpotLocYT + LUMINANCECURVE, // EvLocallabSpotCenter + LUMINANCECURVE, // EvLocallabSpotCircrad + LUMINANCECURVE, // EvLocallabSpotQualityMethod + LUMINANCECURVE, // EvLocallabSpotTransit + LUMINANCECURVE, // EvLocallabSpotThresh + LUMINANCECURVE, // EvLocallabSpotIter + LUMINANCECURVE, // EvLocallabSpotSensiexclu + LUMINANCECURVE, // EvLocallabSpotStruc + LUMINANCECURVE, // EvlocallabEnabled + LUMINANCECURVE, // EvLocenacolor + LUMINANCECURVE, // Evlocallabcurvactiv + LUMINANCECURVE, // Evlocallablightness + LUMINANCECURVE, // Evlocallabcontrast + LUMINANCECURVE, // Evlocallabchroma + LUMINANCECURVE, // Evlocallabsensi + LUMINANCECURVE, // EvlocallabqualitycurveMethod + LUMINANCECURVE, // Evlocallabllshape + LUMINANCECURVE, // Evlocallabccshape + LUMINANCECURVE, // EvlocallabLHshape + LUMINANCECURVE, // EvlocallabHHshape + LUMINANCECURVE, // Evlocallabinvers + LUMINANCECURVE, // EvLocenaexpose + LUMINANCECURVE, // Evlocallabexpcomp + LUMINANCECURVE, // Evlocallabhlcompr + LUMINANCECURVE, // Evlocallabhlcomprthresh + LUMINANCECURVE, // Evlocallabblack + LUMINANCECURVE, // Evlocallabshcompr + LUMINANCECURVE, // Evlocallabwarm + LUMINANCECURVE, // Evlocallabsensiex + LUMINANCECURVE, // Evlocallabshapeexpos + LUMINANCECURVE, // EvLocenavibrance + LUMINANCECURVE, // EvlocallabSaturated + LUMINANCECURVE, // EvlocallabPastels + LUMINANCECURVE, // EvlocallabPastSatThreshold + LUMINANCECURVE, // EvlocallabProtectSkins + LUMINANCECURVE, // EvlocallabAvoidColorShift + LUMINANCECURVE, // EvlocallabPastSatTog + LUMINANCECURVE, // Evlocallabsensiv + LUMINANCECURVE, // EvlocallabSkinTonesCurve + LUMINANCECURVE, // EvLocenablur + LUMINANCECURVE, // Evlocallabradius + LUMINANCECURVE, // Evlocallabstrength + LUMINANCECURVE, // Evlocallabsensibn + LUMINANCECURVE, // EvlocallabblurMethod + LUMINANCECURVE, // Evlocallabactivlum + LUMINANCECURVE, // EvLocenatonemap + LUMINANCECURVE, // Evlocallabstren + LUMINANCECURVE, // Evlocallabgamma + LUMINANCECURVE, // Evlocallabestop + LUMINANCECURVE, // Evlocallabscaltm + LUMINANCECURVE, // Evlocallabrewei + LUMINANCECURVE, // Evlocallabsensitm + LUMINANCECURVE, // EvLocenareti + LUMINANCECURVE, // EvlocallabretinexMethod + LUMINANCECURVE, // Evlocallabstr + LUMINANCECURVE, // Evlocallabchrrt + LUMINANCECURVE, // Evlocallabneigh + LUMINANCECURVE, // Evlocallabvart + LUMINANCECURVE, // Evlocallabsensih + LUMINANCECURVE, // EvlocallabCTgainCurve + LUMINANCECURVE, // Evlocallabinversret + LUMINANCECURVE, // EvLocenasharp + LUMINANCECURVE, // Evlocallabsharradius + LUMINANCECURVE, // Evlocallabsharamount + LUMINANCECURVE, // Evlocallabshardamping + LUMINANCECURVE, // Evlocallabshariter + LUMINANCECURVE, // Evlocallabsensis + LUMINANCECURVE, // Evlocallabinverssha + LUMINANCECURVE, // EvLocenacbdl + LUMINANCECURVE, // EvlocallabEqualizer + LUMINANCECURVE, // Evlocallabchromacbdl + LUMINANCECURVE, // EvlocallabThresho + LUMINANCECURVE, // Evlocallabsensicb + LUMINANCECURVE, // EvLocenadenoi + LUMINANCECURVE, // Evlocallabnoiselumf + LUMINANCECURVE, // Evlocallabnoiselumc + LUMINANCECURVE, // Evlocallabnoiselumdetail + LUMINANCECURVE, // Evlocallabnoiselequal + LUMINANCECURVE, // Evlocallabnoisechrof + LUMINANCECURVE, // Evlocallabnoisechroc + LUMINANCECURVE, // Evlocallabnoisechrodetail + LUMINANCECURVE, // Evlocallabadjblur + LUMINANCECURVE, // Evlocallabbilateral + LUMINANCECURVE, // Evlocallabsensiden + LUMINANCECURVE, // Evlocallabavoid + LUMINANCECURVE, // Evlocallabsharcontrast + LUMINANCECURVE, // EvLocenacontrast + LUMINANCECURVE, // Evlocallablcradius + LUMINANCECURVE, // Evlocallablcamount + LUMINANCECURVE, // Evlocallablcdarkness + LUMINANCECURVE, // Evlocallablclightness + LUMINANCECURVE, // Evlocallabsensilc + LUMINANCECURVE, // Evlocallabdehaz + LUMINANCECURVE, // EvLocenasoft + LUMINANCECURVE, // EvLocallabstreng + LUMINANCECURVE, // EvLocallabsensisf + LUMINANCECURVE, // Evlocallabsharblur + LUMINANCECURVE, // EvLocenalabregion + LUMINANCECURVE, // EvlocallabshowmaskMethod + LUMINANCECURVE, // EvLocallabSpotSelectedWithMask + LUMINANCECURVE, // EvlocallabCCmaskshape + LUMINANCECURVE, // EvlocallabLLmaskshape + LUMINANCECURVE, // EvlocallabCCmaskexpshape + LUMINANCECURVE, // EvlocallabLLmaskexpshape + LUMINANCECURVE, // EvlocallabHHmaskshape + LUMINANCECURVE, // Evlocallabstructcol + LUMINANCECURVE, // Evlocallabstructexp + LUMINANCECURVE, // EvlocallabHHmaskexpshape + LUMINANCECURVE, // Evlocallabblendmaskcol + LUMINANCECURVE, // Evlocallabblendmaskexp + LUMINANCECURVE, // Evlocallabblurexpde + LUMINANCECURVE, // EvLocallabEnaColorMask + LUMINANCECURVE, // EvLocallabEnaExpMask + LUMINANCECURVE, // Evlocallabblurcolde + LUMINANCECURVE, // Evlocallabinversex + LUMINANCECURVE, // Evlocallabstructexclu + LUMINANCECURVE, // Evlocallabexpchroma + LUMINANCECURVE, // EvLocallabLabGridValue + LUMINANCECURVE, // EvLocallabLabstrengthgrid + LUMINANCECURVE, // EvLocallabgridMethod + LUMINANCECURVE, // EvLocenashadhigh + LUMINANCECURVE, // EvLocallabhighlights + LUMINANCECURVE, // EvLocallabh_tonalwidth + LUMINANCECURVE, // EvLocallabshadows + LUMINANCECURVE, // EvLocallabs_tonalwidth + LUMINANCECURVE, // EvLocallabsh_radius + LUMINANCECURVE, // EvLocallabsensihs + LUMINANCECURVE, // Evlocallabradmaskcol + LUMINANCECURVE, // Evlocallabradmaskexp + LUMINANCECURVE, // EvlocallabToolAdded + LUMINANCECURVE, // EvlocallabCCmaskSHshape + LUMINANCECURVE, // EvlocallabLLmaskSHshape + LUMINANCECURVE, // EvlocallabHHmaskSHshape + LUMINANCECURVE, // EvlocallabblendmaskSH + LUMINANCECURVE, // EvLocallabEnaSHMask + LUMINANCECURVE, // EvlocallabradmaskSH + LUMINANCECURVE, // EvlocallabblurSHde + LUMINANCECURVE, // Evlocallabinverssh + LUMINANCECURVE, // EvLocallabSpotbalan + LUMINANCECURVE, // EvLocallabchromaskexp + LUMINANCECURVE, // EvLocallabgammaskexp + LUMINANCECURVE, // EvLocallabslomaskexp + LUMINANCECURVE, // EvLocallabsoftradiusexp + LUMINANCECURVE, // EvLocallabchromaskcol + LUMINANCECURVE, // EvLocallabgammaskcol + LUMINANCECURVE, // EvLocallabslomaskcol + LUMINANCECURVE, // EvLocallabchromaskSH + LUMINANCECURVE, // EvLocallabgammaskSH + LUMINANCECURVE, // EvLocallabslomaskSH + LUMINANCECURVE, // EvLocallabsoftradiuscol + LUMINANCECURVE, // EvLocallabsoftradiusret + LUMINANCECURVE, // EvLocallabsoftradiuscb + LUMINANCECURVE, // EvLocallabSpotTransitweak + LUMINANCECURVE, // EvLocallabclarityml + LUMINANCECURVE, // EvLocallabcontresid + LUMINANCECURVE, // Evlocallabnoiselumf0 + LUMINANCECURVE, // Evlocallabnoiselumf2 + LUMINANCECURVE, // Evlocallabblurcbdl + LUMINANCECURVE, // Evlocallabblendmaskcb + LUMINANCECURVE, // Evlocallabradmaskcb + LUMINANCECURVE, // Evlocallabchromaskcb + LUMINANCECURVE, // Evlocallabgammaskcb + LUMINANCECURVE, // Evlocallabslomaskcb + LUMINANCECURVE, // EvlocallabCCmaskcbshape + LUMINANCECURVE, // EvlocallabLLmaskcbshape + LUMINANCECURVE, // EvlocallabHHmaskcbshape + LUMINANCECURVE, // EvLocallabEnacbMask + M_VOID, // EvlocallabToolRemovedWithoutRefresh + LUMINANCECURVE, // Evlocallabsoftradiustm + LUMINANCECURVE, // EvLocallabSpotTransitgrad + LUMINANCECURVE, // Evlocallabamount + LUMINANCECURVE, // Evlocallabsatur + LUMINANCECURVE, // EvlocallabCCmaskretishape + LUMINANCECURVE, // EvlocallabLLmaskretishape + LUMINANCECURVE, // EvlocallabHHmaskretishape + LUMINANCECURVE, // EvLocallabEnaretiMask + LUMINANCECURVE, // Evlocallabblendmaskreti + LUMINANCECURVE, // Evlocallabradmaskreti + LUMINANCECURVE, // Evlocallabchromaskreti + LUMINANCECURVE, // Evlocallabgammaskreti + LUMINANCECURVE, // Evlocallabslomaskreti + LUMINANCECURVE, // EvlocallabToolRemovedWithRefresh + LUMINANCECURVE, // EvLocallabEnaretiMasktmap + LUMINANCECURVE, // Evlocallabscalereti + LUMINANCECURVE, // Evlocallabdarkness + LUMINANCECURVE, // Evlocallablightnessreti + LUMINANCECURVE, // Evlocallablimd + LUMINANCECURVE, // Evlocallablaplace + LUMINANCECURVE, // EvlocallabsoftMethod + LUMINANCECURVE, // Evlocallabequilret + LUMINANCECURVE, // Evlocallabequiltm + LUMINANCECURVE, // Evlocallabfftwlc + LUMINANCECURVE, // Evlocallabfftwreti + LUMINANCECURVE, // EvlocallabshowmasksoftMethod + LUMINANCECURVE, // Evlocallabshadex + LUMINANCECURVE, // EvlocallabexpMethod + LUMINANCECURVE, // EvLocallablaplacexp + LUMINANCECURVE, // EvLocallabbalanexp + LUMINANCECURVE, // EvLocallablinear + LUMINANCECURVE, // EvlocallabCCmasktmshape + LUMINANCECURVE, // EvlocallabLLmasktmshape + LUMINANCECURVE, // EvlocallabHHmasktmshape + LUMINANCECURVE, // EvLocallabEnatmMask + LUMINANCECURVE, // Evlocallabblendmasktm + LUMINANCECURVE, // Evlocallabradmasktm + LUMINANCECURVE, // Evlocallabchromasktm + LUMINANCECURVE, // Evlocallabgammasktm + LUMINANCECURVE, // Evlocallabslomasktm + LUMINANCECURVE, // EvlocallabshowmasktmMethod + LUMINANCECURVE, // EvlocallablocalcontMethod + LUMINANCECURVE, // Evlocallabwavcurve + LUMINANCECURVE, // Evlocallablevelwav + LUMINANCECURVE, // Evlocallabresidcont + LUMINANCECURVE, // EvlocallabCCmaskblshape + LUMINANCECURVE, // EvlocallabLLmaskblshape + LUMINANCECURVE, // EvlocallabHHmaskblshape + LUMINANCECURVE, // EvLocallabEnablMask + LUMINANCECURVE, // EvlocallabshowmaskblMethod + LUMINANCECURVE, // Evlocallabblendmaskbl + LUMINANCECURVE, // Evlocallabradmaskbl + LUMINANCECURVE, // Evlocallabchromaskbl + LUMINANCECURVE, // Evlocallabgammaskbl + LUMINANCECURVE, // Evlocallabslomaskbl + LUMINANCECURVE, // EvlocallabblMethod + LUMINANCECURVE, // EvlocallabmedMethod + LUMINANCECURVE, // Evlocallabitera + LUMINANCECURVE, // Evlocallabguidbl + LUMINANCECURVE, // Evlocallabepsbl + LUMINANCECURVE, // EvlocallabshowmaskcolMethodinv + LUMINANCECURVE, // EvlocallabshowmaskexpMethodinv + LUMINANCECURVE, // EvlocallabshowmaskSHMethodinv + LUMINANCECURVE, // Evlocallabclarilres + LUMINANCECURVE, // Evlocallabclarisoft + LUMINANCECURVE, // Evlocallabclaricres + LUMINANCECURVE, // Evlocallabresidchro + LUMINANCECURVE, // Evlocallabgamm + LUMINANCECURVE, // Evlocallabfatamount + LUMINANCECURVE, // Evlocallabfatdetail + LUMINANCECURVE, // Evlocallabfatanchor + LUMINANCECURVE, // Evlocallabfatlevel + LUMINANCECURVE, // EvlocallabSpotCreated + LUMINANCECURVE, // EvlocallabexnoiseMethod + LUMINANCECURVE, // Evlocallabdepth + LUMINANCECURVE, // Evlocallabloglin + LUMINANCECURVE, // Evlocallablumonly + LUMINANCECURVE, // Evlocallaboffs + LUMINANCECURVE, // EvlocallabCTtransCurve + LUMINANCECURVE, // Evlocallabcliptm + LUMINANCECURVE, // Evlocallabenatmmaskaft + LUMINANCECURVE, // EvlocallabenaExpmaskaft + LUMINANCECURVE, // Evlocallablapmasktm + LUMINANCECURVE, // Evlocallablapmaskreti + LUMINANCECURVE, // Evlocallablapmaskexp + LUMINANCECURVE, // Evlocallablapmaskcol + LUMINANCECURVE, // EvlocallablapmaskSH + LUMINANCECURVE, // Evlocallablapmaskcb + LUMINANCECURVE, // Evlocallablapmaskbl + LUMINANCECURVE, // Evlocallablaplac + LUMINANCECURVE, // Evlocallabdetailthr + LUMINANCECURVE, // Evlocallabfftwbl + LUMINANCECURVE, // Evlocallabisogr + LUMINANCECURVE, // Evlocallabstrengr + LUMINANCECURVE, // Evlocallabscalegr + LUMINANCECURVE, // EvlocallabLmaskshape + LUMINANCECURVE, // EvlocallabLmaskexpshape + LUMINANCECURVE, // EvlocallabLmaskSHshape + LUMINANCECURVE, // EvlocallabLmasktmshape + LUMINANCECURVE, // EvlocallabLmaskretishape + LUMINANCECURVE, // EvlocallabLmaskcbshape + LUMINANCECURVE, // EvlocallabLmaskblshape + LUMINANCECURVE, // EvlocallabLLmaskblshapewav + LUMINANCECURVE, // Evlocallabshadmaskbl + LUMINANCECURVE, // EvlocallabLLmaskcolshapewav + LUMINANCECURVE, // Evlocallabshadmaskcol + LUMINANCECURVE, // EvlocallabcsThreshold + LUMINANCECURVE, // EvlocallabcsThresholdblur + LUMINANCECURVE, // EvlocallabcsThresholdcol + LUMINANCECURVE, // Evlocallabdeltae + LUMINANCECURVE, // EvLocallabSpotscopemask + LUMINANCECURVE, // EvlocallabshMethod + LUMINANCECURVE, // EvlocallabEqualizersh + LUMINANCECURVE, // EvlocallabdetailSH + LUMINANCECURVE, // EvlocallabfatamountSH + LUMINANCECURVE, // EvlocallabfatanchorSH + LUMINANCECURVE, // Evlocallabshortc + LUMINANCECURVE, // EvLocallabSpotlumask + LUMINANCECURVE, // EvlocallabgamSH + LUMINANCECURVE, // EvlocallabsloSH + LUMINANCECURVE, // Evlocallabsavrest + LUMINANCECURVE, // Evlocallabrecurs + LUMINANCECURVE, // EvLocallabmergecolMethod + LUMINANCECURVE, // EvLocallabopacol + LUMINANCECURVE, // Evlocallabrgbshape + LUMINANCECURVE, // EvLocallabtoneMethod + LUMINANCECURVE, // EvLocallabspecial + LUMINANCECURVE, // EvLocallabconthrcol + LUMINANCECURVE, // EvLocallabmerMethod + LUMINANCECURVE, // EvLocallabstrumaskcol + LUMINANCECURVE, // EvLocallabstrumaskbl + LUMINANCECURVE, // EvLocallabtoolcol + LUMINANCECURVE, // Evlocallabtoolbl + LUMINANCECURVE, // EvlocallabHHhmaskshape + LUMINANCECURVE, // EvlocallabCCmaskvibshape + LUMINANCECURVE, // EvlocallabLLmaskvibshape + LUMINANCECURVE, // EvlocallabHHmaskvibshape + LUMINANCECURVE, // EvlocallabshowmaskvibMethod + LUMINANCECURVE, // EvLocallabEnavibMask + LUMINANCECURVE, // Evlocallabblendmaskvi + LUMINANCECURVE, // Evlocallabradmaskvib + LUMINANCECURVE, // Evlocallabchromaskvib + LUMINANCECURVE, // Evlocallabgammaskvib + LUMINANCECURVE, // Evlocallabslomaskvib + LUMINANCECURVE, // Evlocallablapmaskvib + LUMINANCECURVE, // EvlocallabLmaskvibshape + LUMINANCECURVE, // EvLocallabLabGridmergValue + LUMINANCECURVE, // EvLocallabmercol + LUMINANCECURVE, // EvLocallabmerlucol + LUMINANCECURVE, // Evlocallabstrmaskexp + LUMINANCECURVE, // Evlocallabangmaskexp + LUMINANCECURVE, // Evlocallabstrexp + LUMINANCECURVE, // Evlocallabangexp + LUMINANCECURVE, // EvlocallabstrSH + LUMINANCECURVE, // EvlocallabangSH + LUMINANCECURVE, // Evlocallabstrcol + LUMINANCECURVE, // Evlocallabangcol + LUMINANCECURVE, // Evlocallabstrcolab + LUMINANCECURVE, // EvLocallabSpotfeather + LUMINANCECURVE, // Evlocallabstrcolh + LUMINANCECURVE, // Evlocallabstrvib + LUMINANCECURVE, // Evlocallabangvib + LUMINANCECURVE, // Evlocallabstrvibab + LUMINANCECURVE, // Evlocallabstrvibh + LUMINANCECURVE, // EvLocallabSpotcomplexMethod + LUMINANCECURVE, // Evlocallabclshape + LUMINANCECURVE, // Evlocallablcshape + LUMINANCECURVE, // Evlocallabblurcol + LUMINANCECURVE, // Evlocallabcontcol + LUMINANCECURVE, // EvLocallabfftColorMask + RGBCURVE | M_AUTOEXP, // EvLocenalog + AUTOEXP, // EvLocallabAuto + LUMINANCECURVE, // EvlocallabsourceGray + AUTOEXP, // EvlocallabsourceGrayAuto + AUTOEXP, // EvlocallabAutoGray + LUMINANCECURVE, // EvlocallabblackEv + LUMINANCECURVE, // EvlocallabwhiteEv + LUMINANCECURVE, // EvlocallabtargetGray + LUMINANCECURVE, // Evlocallabdetail + LUMINANCECURVE, // Evlocallabsensilog + AUTOEXP, // Evlocallabfullimage + LUMINANCECURVE, // Evlocallabbaselog + LUMINANCECURVE, // Evlocallabresidblur + LUMINANCECURVE, // Evlocallabblurlc + LUMINANCECURVE, // Evlocallablevelblur + LUMINANCECURVE, // EvlocallabwavCurvelev + LUMINANCECURVE, // EvlocallabwavCurvecon + LUMINANCECURVE, // Evlocallabsigma + LUMINANCECURVE, // Evlocallaboriglc + LUMINANCECURVE, // Evlocallabsigmadc + LUMINANCECURVE, // Evlocallabdeltad + LUMINANCECURVE, // EvlocallabwavCurvecomp + LUMINANCECURVE, // Evlocallabfatres + LUMINANCECURVE, // EvLocallabSpotbalanh + LUMINANCECURVE, // EvlocallabwavCurveden + LUMINANCECURVE, // EvlocallabHHmasklcshape + LUMINANCECURVE, // EvlocallabCCmasklcshape + LUMINANCECURVE, // EvlocallabLLmasklcshape + LUMINANCECURVE, // EvlocallabEnalcMask + LUMINANCECURVE, // EvlocallabshowmasklcMethod + LUMINANCECURVE, // Evlocallabblendmasklc + LUMINANCECURVE, // Evlocallabradmasklc + LUMINANCECURVE, // Evlocallabchromasklc + LUMINANCECURVE, // EvlocallabLmasklcshape + LUMINANCECURVE, // Evlocallabchromalev + LUMINANCECURVE, // Evlocallabchromablu + LUMINANCECURVE, // Evlocallaboffset + LUMINANCECURVE, // Evlocallabwavblur + LUMINANCECURVE, // Evlocallabwavcont + LUMINANCECURVE, // Evlocallabwavcomp + LUMINANCECURVE, // Evlocallabwavcompre + LUMINANCECURVE, // EvlocallabwavCurvecompre + LUMINANCECURVE, // Evlocallabresidcomp + LUMINANCECURVE, // Evlocallabthreswav + LUMINANCECURVE, // Evlocallabstrwav + LUMINANCECURVE, // Evlocallabangwav + LUMINANCECURVE, // Evlocallabwavgradl + LUMINANCECURVE, // Evlocallabstrlog + LUMINANCECURVE, // Evlocallabanglog + LUMINANCECURVE, // EvLocallabSpotcolorde + LUMINANCECURVE, // EvlocallabshowmasksharMethod + LUMINANCECURVE, // Evlocallabshowreset + LUMINANCECURVE, // Evlocallabstrengthw + LUMINANCECURVE, // Evlocallabradiusw + LUMINANCECURVE, // Evlocallabdetailw + LUMINANCECURVE, // Evlocallabgradw + LUMINANCECURVE, // Evlocallabtloww + LUMINANCECURVE, // Evlocallabthigw + LUMINANCECURVE, // EvlocallabwavCurveedg + LUMINANCECURVE, // EvlocallablocaledgMethod + LUMINANCECURVE, // Evlocallabwavedg + LUMINANCECURVE, // Evlocallabedgw + LUMINANCECURVE, // Evlocallabbasew + LUMINANCECURVE, // EvlocallablocalneiMethod + LUMINANCECURVE, // Evlocallabwaveshow + LUMINANCECURVE, // EvLocallabSpotwavMethod + LUMINANCECURVE, // EvlocallabchroMethod + LUMINANCECURVE, // Evlocallabstrbl + LUMINANCECURVE, // Evlocallabsigmadr + LUMINANCECURVE, // Evlocallabsigmabl + LUMINANCECURVE, // Evlocallabsigmaed + LUMINANCECURVE, // Evlocallabresidsha + LUMINANCECURVE, // Evlocallabresidshathr + LUMINANCECURVE, // Evlocallabresidhi + LUMINANCECURVE, // Evlocallabresidhithr + LUMINANCECURVE, // Evlocallabsigmalc + LUMINANCECURVE, // Evlocallabsigmalc2 + LUMINANCECURVE, // Evlocallabblwh + LUMINANCECURVE, // EvlocallabcomplexityWithRefresh + M_VOID, // EvlocallabcomplexityWithoutRefresh + LUMINANCECURVE, // EvLocallabSpotcolorscope + LUMINANCECURVE, //EvlocallabshowmasktypMethod + LUMINANCECURVE // Evlocallabshadmaskblsha }; @@ -553,6 +985,7 @@ void RefreshMapper::mapEvent(ProcEvent event, int action) int RefreshMapper::getAction(ProcEvent event) const { auto it = actions_.find(event); + if (it == actions_.end()) { return 0; } else { diff --git a/rtengine/rt_algo.h b/rtengine/rt_algo.h index 81147fd75..a72a7c56b 100644 --- a/rtengine/rt_algo.h +++ b/rtengine/rt_algo.h @@ -25,4 +25,8 @@ namespace rtengine { void findMinMaxPercentile(const float* data, size_t size, float minPrct, float& minOut, float maxPrct, float& maxOut, bool multiThread = true); void buildBlendMask(const float* const * luminance, float **blend, int W, int H, float &contrastThreshold, bool autoContrast = false, float ** clipmask = nullptr); +// implemented in tmo_fattal02 +void buildGradientsMask(int W, int H, float **luminance, float **out, + float amount, int nlevels, int detail_level, + float alfa, float beta, bool multithread); } diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index 028cedec9..7561d68aa 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -35,6 +35,7 @@ #include "../rtgui/threadutils.h" + /** * @file * This file contains the main functionality of the RawTherapee engine. @@ -375,6 +376,32 @@ public: virtual void minmaxChanged(double cdma, double cdmin, double mini, double maxi, double Tmean, double Tsigma, double Tmin, double Tmax) = 0; }; +class LocallabListener +{ +public: + struct locallabRef { + double huer; + double lumar; + double chromar; + }; + + struct locallabRetiMinMax { + double cdma; + double cdmin; + double mini; + double maxi; + double Tmean; + double Tsigma; + double Tmin; + double Tmax; + }; + + virtual ~LocallabListener() = default; + virtual void refChanged(const std::vector &ref, int selspot) = 0; + virtual void minmaxChanged(const std::vector &minmax, int selspot) = 0; + virtual void logencodChanged(const float blackev, const float whiteev, const float sourceg, const float targetg) = 0; +}; + class AutoColorTonListener { public: @@ -522,6 +549,8 @@ public: virtual void updateUnLock() = 0; + virtual void setLocallabMaskVisibility(bool previewDeltaE, int locallColorMask, int locallColorMaskinv, int locallExpMask, int locallExpMaskinv, int locallSHMask, int locallSHMaskinv, int locallvibMask, int locallsoftMask, int locallblMask, int localltmMask, int locallretiMask, int locallsharMask, int localllcMask, int locallcbMask) = 0; + /** Creates and returns a Crop instance that acts as a window on the image * @param editDataProvider pointer to the EditDataProvider that communicates with the EditSubscriber * @return a pointer to the Crop object that handles the image data trough its own pipeline */ @@ -557,6 +586,7 @@ public: virtual void setRetinexListener (RetinexListener* l) = 0; virtual void setWaveletListener (WaveletListener* l) = 0; virtual void setImageTypeListener (ImageTypeListener* l) = 0; + virtual void setLocallabListener (LocallabListener* l) = 0; virtual void setFilmNegListener (FilmNegListener* l) = 0; virtual void setMonitorProfile (const Glib::ustring& monitorProfile, RenderingIntent intent) = 0; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index e8126be36..4ebb95842 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1245,8 +1245,8 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT ipf.firstAnalysis (baseImg, params, hist16); - ipf.dehaze(baseImg); - ipf.ToneMapFattal02(baseImg); + ipf.dehaze(baseImg, params.dehaze); + ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0); // perform transform int origFW; @@ -1413,16 +1413,17 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT CurveFactory::complexsgnCurve (autili, butili, ccutili, cclutili, params.labCurve.acurve, params.labCurve.bcurve, params.labCurve.cccurve, params.labCurve.lccurve, curve1, curve2, satcurve, lhskcurve, 16); - ipf.chromiLuminanceCurve (nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); - ipf.vibrance (labView); + ipf.chromiLuminanceCurve (nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); + + ipf.vibrance (labView, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile); ipf.labColorCorrectionRegions(labView); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || !params.colorappearance.enabled) { ipf.EPDToneMap (labView, 5, 6); } - ipf.softLight(labView); + ipf.softLight(labView, params.softlight); if (params.colorappearance.enabled) { CurveFactory::curveLightBrightColor ( diff --git a/rtengine/settings.h b/rtengine/settings.h index 529336cbc..fde6fa132 100644 --- a/rtengine/settings.h +++ b/rtengine/settings.h @@ -63,7 +63,7 @@ public: bool gamutICC; // no longer used bool gamutLch; bool HistogramWorking; // true: histogram is display the value of the image computed in the Working profile - // false: histogram is display the value of the image computed in the Output profile + // false: histogram is display the value of the image computed in the Output profile int amchroma; int protectred; double protectredh; @@ -81,6 +81,15 @@ public: double level0_cbdl; double level123_cbdl; Glib::ustring lensfunDbDirectory; // The directory containing the lensfun database. If empty, the system defaults will be used, as described in https://lensfun.github.io/manual/latest/dbsearch.html + int cropsleep; + double reduchigh; + double reduclow; + bool detectshape; + bool fftwsigma; + int previewselection; + double cbdlsensi; +// bool showtooltip; + int itcwb_thres; bool itcwb_sort; int itcwb_greenrange; diff --git a/rtengine/shmap.h b/rtengine/shmap.h index b1b1e5eff..8e8f73c0e 100644 --- a/rtengine/shmap.h +++ b/rtengine/shmap.h @@ -29,6 +29,7 @@ namespace rtengine { class Imagefloat; +class LabImage; class SHMap : public NonCopyable @@ -40,6 +41,7 @@ public: SHMap (int w, int h); ~SHMap (); + void updateLab (LabImage* img, double radius, bool hq, int skip); void update (Imagefloat* img, double radius, double lumi[3], bool hq, int skip); void updateL (float** L, double radius, bool hq, int skip); @@ -47,6 +49,7 @@ public: private: int W, H; + void fillLuminanceLab( LabImage * img, float **luminance); void dirpyr_shmap(float ** data_fine, float ** data_coarse, int width, int height, const LUTf& rangefn, int level, int scale); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index a53902737..3c0e0aee8 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -880,8 +880,8 @@ private: ipf.firstAnalysis(baseImg, params, hist16); - ipf.dehaze(baseImg); - ipf.ToneMapFattal02(baseImg); + ipf.dehaze(baseImg, params.dehaze); + ipf.ToneMapFattal02(baseImg, params.fattal, 3, 0, nullptr, 0, 0, 0); // perform transform (excepted resizing) if (ipf.needsTransform(fw, fh, imgsrc->getRotateDegree(), imgsrc->getMetaData())) { @@ -971,6 +971,8 @@ private: } labView = new LabImage(fw, fh); + reservView = new LabImage(fw, fh); + lastorigView = new LabImage(fw, fh); if (params.blackwhite.enabled) { CurveFactory::curveBW(params.blackwhite.beforeCurve, params.blackwhite.afterCurve, hist16, dummy, customToneCurvebw1, customToneCurvebw2, 1); @@ -1077,6 +1079,323 @@ private: CurveFactory::complexsgnCurve(autili, butili, ccutili, cclutili, params.labCurve.acurve, params.labCurve.bcurve, params.labCurve.cccurve, params.labCurve.lccurve, curve1, curve2, satcurve, lhskcurve, 1); + + // bool locallutili = false; + // bool localcutili = false; + reservView->CopyFrom(labView); + lastorigView->CopyFrom(labView); + + if (params.locallab.enabled) { + MyTime t1, t2; + t1.set(); + + LUTf huerefs(500, -10000.f); + LUTf sobelrefs(500, -10000.f); + LUTi centerx(500, -10000); + LUTi centery(500, -10000); + LocretigainCurve locRETgainCurve; + LocretitransCurve locRETtransCurve; + LocLHCurve loclhCurve; + LocHHCurve lochhCurve; + LocCCmaskCurve locccmasCurve; + LocLLmaskCurve locllmasCurve; + LocHHmaskCurve lochhmasCurve; + LocHHmaskCurve lochhhmasCurve; + LocCCmaskCurve locccmasexpCurve; + LocLLmaskCurve locllmasexpCurve; + LocHHmaskCurve lochhmasexpCurve; + LocCCmaskCurve locccmasSHCurve; + LocLLmaskCurve locllmasSHCurve; + LocHHmaskCurve lochhmasSHCurve; + LocCCmaskCurve locccmasvibCurve; + LocLLmaskCurve locllmasvibCurve; + LocHHmaskCurve lochhmasvibCurve; + LocCCmaskCurve locccmaslcCurve; + LocLLmaskCurve locllmaslcCurve; + LocHHmaskCurve lochhmaslcCurve; + LocCCmaskCurve locccmascbCurve; + LocLLmaskCurve locllmascbCurve; + LocHHmaskCurve lochhmascbCurve; + LocCCmaskCurve locccmasretiCurve; + LocLLmaskCurve locllmasretiCurve; + LocHHmaskCurve lochhmasretiCurve; + LocCCmaskCurve locccmastmCurve; + LocLLmaskCurve locllmastmCurve; + LocHHmaskCurve lochhmastmCurve; + LocCCmaskCurve locccmasblCurve; + LocLLmaskCurve locllmasblCurve; + LocHHmaskCurve lochhmasblCurve; + LocwavCurve loclmasCurveblwav; + LocwavCurve loclmasCurvecolwav; + LocwavCurve locwavCurve; + LocwavCurve loclevwavCurve; + LocwavCurve locconwavCurve; + LocwavCurve loccompwavCurve; + LocwavCurve loccomprewavCurve; + LocwavCurve locedgwavCurve; + LocwavCurve locwavCurveden; + LUTf lllocalcurve(65536, 0); + LUTf lclocalcurve(65536, 0); + LUTf cllocalcurve(65536, 0); + LUTf cclocalcurve(65536, 0); + LUTf rgblocalcurve(65536, 0); + LUTf hltonecurveloc(65536, 0); + LUTf shtonecurveloc(65536, 0); + LUTf tonecurveloc(65536, 0); + LUTf lightCurveloc(32770, 0); + LUTf exlocalcurve(65536, 0); + LUTf lmasklocalcurve(65536, 0); + LUTf lmaskexplocalcurve(65536, 0); + LUTf lmaskSHlocalcurve(65536, 0); + LUTf lmaskviblocalcurve(65536, 0); + LUTf lmasktmlocalcurve(65536, 0); + LUTf lmaskretilocalcurve(65536, 0); + LUTf lmaskcblocalcurve(65536, 0); + LUTf lmaskbllocalcurve(65536, 0); + LUTf lmasklclocalcurve(65536, 0); + + // int maxspot = 1; + float** shbuffer = nullptr; + + for (size_t sp = 0; sp < params.locallab.spots.size(); sp++) { + if (params.locallab.spots.at(sp).inverssha) { + shbuffer = new float*[fh]; + + for (int i = 0; i < fh; i++) { + shbuffer[i] = new float[fw]; + } + } + + // Set local curves of current spot to LUT + bool LHutili = false; + bool HHutili = false; + bool locallutili = false; + bool localclutili = false; + bool locallcutili = false; + bool localcutili = false; + bool localrgbutili = false; + bool localexutili = false; + bool llmasutili = false; + bool lhmasutili = false; + bool lhhmasutili = false; + bool lcmasutili = false; + bool localmaskutili = false; + bool localmaskexputili = false; + bool localmaskSHutili = false; + bool localmaskvibutili = false; + bool localmasktmutili = false; + bool localmaskretiutili = false; + bool localmaskcbutili = false; + bool localmaskblutili = false; + bool localmasklcutili = false; + bool lcmasexputili = false; + bool lhmasexputili = false; + bool llmasexputili = false; + bool lcmasSHutili = false; + bool lhmasSHutili = false; + bool llmasSHutili = false; + bool lcmasvibutili = false; + bool lhmasvibutili = false; + bool llmasvibutili = false; + bool lcmaslcutili = false; + bool lhmaslcutili = false; + bool llmaslcutili = false; + bool lcmascbutili = false; + bool lhmascbutili = false; + bool llmascbutili = false; + bool lcmasretiutili = false; + bool lhmasretiutili = false; + bool llmasretiutili = false; + bool lcmastmutili = false; + bool lhmastmutili = false; + bool llmastmutili = false; + bool lcmasblutili = false; + bool lhmasblutili = false; + bool llmasblutili = false; + bool locwavutili = false; + bool locwavdenutili = false; + bool loclevwavutili = false; + bool locconwavutili = false; + bool loccompwavutili = false; + bool loccomprewavutili = false; + bool locedgwavutili = false; + bool lmasutiliblwav = false; + bool lmasutilicolwav = false; + locRETgainCurve.Set(params.locallab.spots.at(sp).localTgaincurve); + locRETtransCurve.Set(params.locallab.spots.at(sp).localTtranscurve); + loclhCurve.Set(params.locallab.spots.at(sp).LHcurve, LHutili); + lochhCurve.Set(params.locallab.spots.at(sp).HHcurve, HHutili); + locccmasCurve.Set(params.locallab.spots.at(sp).CCmaskcurve, lcmasutili); + locllmasCurve.Set(params.locallab.spots.at(sp).LLmaskcurve, llmasutili); + lochhmasCurve.Set(params.locallab.spots.at(sp).HHmaskcurve, lhmasutili); + lochhhmasCurve.Set(params.locallab.spots.at(sp).HHhmaskcurve, lhhmasutili); + locccmasexpCurve.Set(params.locallab.spots.at(sp).CCmaskexpcurve, lcmasexputili); + locllmasexpCurve.Set(params.locallab.spots.at(sp).LLmaskexpcurve, llmasexputili); + lochhmasexpCurve.Set(params.locallab.spots.at(sp).HHmaskexpcurve, lhmasexputili); + locccmasSHCurve.Set(params.locallab.spots.at(sp).CCmaskSHcurve, lcmasSHutili); + locllmasSHCurve.Set(params.locallab.spots.at(sp).LLmaskSHcurve, llmasSHutili); + lochhmasSHCurve.Set(params.locallab.spots.at(sp).HHmaskSHcurve, lhmasSHutili); + locccmasvibCurve.Set(params.locallab.spots.at(sp).CCmaskvibcurve, lcmasvibutili); + locllmasvibCurve.Set(params.locallab.spots.at(sp).LLmaskvibcurve, llmasvibutili); + lochhmasvibCurve.Set(params.locallab.spots.at(sp).HHmaskvibcurve, lhmasvibutili); + locccmascbCurve.Set(params.locallab.spots.at(sp).CCmaskcbcurve, lcmascbutili); + locllmascbCurve.Set(params.locallab.spots.at(sp).LLmaskcbcurve, llmascbutili); + lochhmascbCurve.Set(params.locallab.spots.at(sp).HHmaskcbcurve, lhmascbutili); + locccmasretiCurve.Set(params.locallab.spots.at(sp).CCmaskreticurve, lcmasretiutili); + locllmasretiCurve.Set(params.locallab.spots.at(sp).LLmaskreticurve, llmasretiutili); + lochhmasretiCurve.Set(params.locallab.spots.at(sp).HHmaskreticurve, lhmasretiutili); + locccmastmCurve.Set(params.locallab.spots.at(sp).CCmasktmcurve, lcmastmutili); + locllmastmCurve.Set(params.locallab.spots.at(sp).LLmasktmcurve, llmastmutili); + lochhmastmCurve.Set(params.locallab.spots.at(sp).HHmasktmcurve, lhmastmutili); + locccmasblCurve.Set(params.locallab.spots.at(sp).CCmaskblcurve, lcmasblutili); + locllmasblCurve.Set(params.locallab.spots.at(sp).LLmaskblcurve, llmasblutili); + lochhmasblCurve.Set(params.locallab.spots.at(sp).HHmaskblcurve, lhmasblutili); + loclmasCurveblwav.Set(params.locallab.spots.at(sp).LLmaskblcurvewav, lmasutiliblwav); + loclmasCurvecolwav.Set(params.locallab.spots.at(sp).LLmaskcolcurvewav, lmasutilicolwav); + + locwavCurve.Set(params.locallab.spots.at(sp).locwavcurve, locwavutili); + locwavCurveden.Set(params.locallab.spots.at(sp).locwavcurveden, locwavdenutili); + loclevwavCurve.Set(params.locallab.spots.at(sp).loclevwavcurve, loclevwavutili); + locconwavCurve.Set(params.locallab.spots.at(sp).locconwavcurve, locconwavutili); + loccompwavCurve.Set(params.locallab.spots.at(sp).loccompwavcurve, loccompwavutili); + loccomprewavCurve.Set(params.locallab.spots.at(sp).loccomprewavcurve, loccomprewavutili); + locedgwavCurve.Set(params.locallab.spots.at(sp).locedgwavcurve, locedgwavutili); + CurveFactory::curveLocal(locallutili, params.locallab.spots.at(sp).llcurve, lllocalcurve, 1); + CurveFactory::curveLocal(localclutili, params.locallab.spots.at(sp).clcurve, cllocalcurve, 1); + CurveFactory::curveLocal(locallcutili, params.locallab.spots.at(sp).lccurve, lclocalcurve, 1); + CurveFactory::curveCCLocal(localcutili, params.locallab.spots.at(sp).cccurve, cclocalcurve, 1); + CurveFactory::curveLocal(localrgbutili, params.locallab.spots.at(sp).rgbcurve, rgblocalcurve, 1); + CurveFactory::curveexLocal(localexutili, params.locallab.spots.at(sp).excurve, exlocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskutili, params.locallab.spots.at(sp).Lmaskcurve, lmasklocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskexputili, params.locallab.spots.at(sp).Lmaskexpcurve, lmaskexplocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskSHutili, params.locallab.spots.at(sp).LmaskSHcurve, lmaskSHlocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskvibutili, params.locallab.spots.at(sp).Lmaskvibcurve, lmaskviblocalcurve, 1); + CurveFactory::curvemaskLocal(localmasktmutili, params.locallab.spots.at(sp).Lmasktmcurve, lmasktmlocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskretiutili, params.locallab.spots.at(sp).Lmaskreticurve, lmaskretilocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskcbutili, params.locallab.spots.at(sp).Lmaskcbcurve, lmaskcblocalcurve, 1); + CurveFactory::curvemaskLocal(localmaskblutili, params.locallab.spots.at(sp).Lmaskblcurve, lmaskbllocalcurve, 1); + CurveFactory::curvemaskLocal(localmasklcutili, params.locallab.spots.at(sp).Lmasklccurve, lmasklclocalcurve, 1); + //provisory + double ecomp = params.locallab.spots.at(sp).expcomp; + double black = params.locallab.spots.at(sp).black; + double hlcompr = params.locallab.spots.at(sp).hlcompr; + double hlcomprthresh = params.locallab.spots.at(sp).hlcomprthresh; + double shcompr = params.locallab.spots.at(sp).shcompr; + double br = params.locallab.spots.at(sp).lightness; + double cont = params.locallab.spots.at(sp).contrast; + if(black < 0. && params.locallab.spots.at(sp).expMethod == "pde" ) { + black *= 1.5; + } + + // Reference parameters computation + double huere, chromare, lumare, huerefblu, chromarefblu, lumarefblu, sobelre; + int lastsav; + float avge; + if (params.locallab.spots.at(sp).spotMethod == "exc") { + ipf.calc_ref(sp, reservView, reservView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + } else { + ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + } + CurveFactory::complexCurvelocal(ecomp, black / 65535., hlcompr, hlcomprthresh, shcompr, br, cont, lumare, + hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, avge, + 1); + float minCD; + float maxCD; + float mini; + float maxi; + float Tmean; + float Tsigma; + float Tmin; + float Tmax; + + // No Locallab mask is shown in exported picture + ipf.Lab_Local(2, sp, (float**)shbuffer, labView, labView, reservView, lastorigView, 0, 0, fw, fh, 1, locRETgainCurve, locRETtransCurve, + lllocalcurve, locallutili, + cllocalcurve, localclutili, + lclocalcurve, locallcutili, + loclhCurve, lochhCurve, + lmasklocalcurve, localmaskutili, + lmaskexplocalcurve, localmaskexputili, + lmaskSHlocalcurve, localmaskSHutili, + lmaskviblocalcurve, localmaskvibutili, + lmasktmlocalcurve, localmasktmutili, + lmaskretilocalcurve, localmaskretiutili, + lmaskcblocalcurve, localmaskcbutili, + lmaskbllocalcurve, localmaskblutili, + lmasklclocalcurve, localmasklcutili, + locccmasCurve, lcmasutili, locllmasCurve, llmasutili, lochhmasCurve, lhmasutili, lochhhmasCurve, lhhmasutili, locccmasexpCurve, lcmasexputili, locllmasexpCurve, llmasexputili, lochhmasexpCurve, lhmasexputili, + locccmasSHCurve, lcmasSHutili, locllmasSHCurve, llmasSHutili, lochhmasSHCurve, lhmasSHutili, + locccmasvibCurve, lcmasvibutili, locllmasvibCurve, llmasvibutili, lochhmasvibCurve, lhmasvibutili, + locccmascbCurve, lcmascbutili, locllmascbCurve, llmascbutili, lochhmascbCurve, lhmascbutili, + locccmasretiCurve, lcmasretiutili, locllmasretiCurve, llmasretiutili, lochhmasretiCurve, lhmasretiutili, + locccmastmCurve, lcmastmutili, locllmastmCurve, llmastmutili, lochhmastmCurve, lhmastmutili, + locccmasblCurve, lcmasblutili, locllmasblCurve, llmasblutili, lochhmasblCurve, lhmasblutili, + locccmaslcCurve, lcmaslcutili, locllmaslcCurve, llmaslcutili, lochhmaslcCurve, lhmaslcutili, + loclmasCurveblwav,lmasutiliblwav, + loclmasCurvecolwav,lmasutilicolwav, + locwavCurve, locwavutili, + loclevwavCurve, loclevwavutili, + locconwavCurve, locconwavutili, + loccompwavCurve, loccompwavutili, + loccomprewavCurve, loccomprewavutili, + locwavCurveden, locwavdenutili, + locedgwavCurve, locedgwavutili, + LHutili, HHutili, cclocalcurve, localcutili, rgblocalcurve, localrgbutili, localexutili, exlocalcurve, hltonecurveloc, shtonecurveloc, tonecurveloc, lightCurveloc, + huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, lastsav, false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + minCD, maxCD, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + + lastorigView->CopyFrom(labView); + + if (params.locallab.spots.at(sp).spotMethod == "exc") { + ipf.calc_ref(sp, reservView, reservView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + } else { + ipf.calc_ref(sp, labView, labView, 0, 0, fw, fh, 1, huerefblu, chromarefblu, lumarefblu, huere, chromare, lumare, sobelre, avge, locwavCurveden, locwavdenutili); + } + + // Clear local curves + lllocalcurve.clear(); + lclocalcurve.clear(); + cllocalcurve.clear(); + cclocalcurve.clear(); + rgblocalcurve.clear(); + exlocalcurve.clear(); + hltonecurveloc.clear(); + lmasklocalcurve.clear(); + lmaskexplocalcurve.clear(); + lmaskSHlocalcurve.clear(); + lmaskviblocalcurve.clear(); + lmasktmlocalcurve.clear(); + lmaskretilocalcurve.clear(); + lmaskcblocalcurve.clear(); + lmaskbllocalcurve.clear(); + shtonecurveloc.clear(); + tonecurveloc.clear(); + lightCurveloc.clear(); + if (params.locallab.spots.at(sp).inverssha) { + + for (int i = 0; i < fh; i++) { + delete [] shbuffer[i]; + } + + delete [] shbuffer; + } + + + } + + t2.set(); + + if (settings->verbose) { + printf("Total local:- %d usec\n", t2.etime(t1)); + } + + } + + delete reservView; + reservView = nullptr; + delete lastorigView; + lastorigView = nullptr; + ipf.chromiLuminanceCurve(nullptr, 1, labView, labView, curve1, curve2, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili, cclutili, clcutili, dummy, dummy); if ((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) { @@ -1084,7 +1403,7 @@ private: } - ipf.vibrance(labView); + ipf.vibrance(labView, params.vibrance, params.toneCurve.hrenabled, params.icm.workingProfile); ipf.labColorCorrectionRegions(labView); // for all treatments Defringe, Sharpening, Contrast detail ,Microcontrast they are activated if "CIECAM" function are disabled @@ -1279,7 +1598,7 @@ private: wavCLVCurve.Reset(); } - ipf.softLight(labView); + ipf.softLight(labView, params.softlight); //Colorappearance and tone-mapping associated @@ -1423,6 +1742,8 @@ private: delete labView; labView = nullptr; +// delete reservView; +// reservView = nullptr; if (bwonly) { //force BW r=g=b @@ -1699,6 +2020,8 @@ private: ColorTemp currWB; Imagefloat *baseImg; LabImage* labView; + LabImage* reservView; + LabImage* lastorigView; LUTu hist16; diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index cee57c851..60fbc7b4c 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -97,60 +97,62 @@ class Array2Df: public array2D typedef array2D Super; public: Array2Df(): Super() {} - Array2Df (int w, int h): Super (w, h) {} + Array2Df(int w, int h): Super(w, h) {} + Array2Df(int w, int h, float **data): + Super(w, h, data, ARRAY2D_BYREFERENCE) {} - float &operator() (int w, int h) + float &operator()(int w, int h) { return (*this)[h][w]; } - const float &operator() (int w, int h) const + const float &operator()(int w, int h) const { return (*this)[h][w]; } - float &operator() (int i) + float &operator()(int i) { - return static_cast (*this)[i]; + return static_cast(*this)[i]; } - const float &operator() (int i) const + const float &operator()(int i) const { - return const_cast (*this).operator() (i); + return const_cast(*this).operator()(i); } int getRows() const { - return const_cast (*this).height(); + return const_cast(*this).height(); } int getCols() const { - return const_cast (*this).width(); + return const_cast(*this).width(); } float *data() { - return static_cast (*this); + return static_cast(*this); } const float *data() const { - return const_cast (*this).data(); + return const_cast(*this).data(); } }; // upper bound on image dimension used in tmo_fattal02 -- see the comment there const int RT_dimension_cap = 1920; -void rescale_bilinear (const Array2Df &src, Array2Df &dst, bool multithread); +void rescale_bilinear(const Array2Df &src, Array2Df &dst, bool multithread); /****************************************************************************** * Luminance HDR code (modifications are marked with an RT comment) ******************************************************************************/ -void downSample (const Array2Df& A, Array2Df& B) +void downSample(const Array2Df& A, Array2Df& B) { const int width = B.getCols(); const int height = B.getRows(); @@ -160,18 +162,18 @@ void downSample (const Array2Df& A, Array2Df& B) // speed improvements. The main issue is the pde solver and in case of the // fft solver uses optimised threaded fftw routines. //#pragma omp parallel for - for ( int y = 0 ; y < height ; y++ ) { - for ( int x = 0 ; x < width ; x++ ) { - float p = A (2 * x, 2 * y); - p += A (2 * x + 1, 2 * y); - p += A (2 * x, 2 * y + 1); - p += A (2 * x + 1, 2 * y + 1); - B (x, y) = p * 0.25f; // p / 4.0f; + for (int y = 0 ; y < height ; y++) { + for (int x = 0 ; x < width ; x++) { + float p = A(2 * x, 2 * y); + p += A(2 * x + 1, 2 * y); + p += A(2 * x, 2 * y + 1); + p += A(2 * x + 1, 2 * y + 1); + B(x, y) = p * 0.25f; // p / 4.0f; } } } -void gaussianBlur (const Array2Df& I, Array2Df& L, bool multithread) +void gaussianBlur(const Array2Df& I, Array2Df& L, bool multithread) { const int width = I.getCols(); const int height = I.getRows(); @@ -179,30 +181,30 @@ void gaussianBlur (const Array2Df& I, Array2Df& L, bool multithread) if (width < 3 || height < 3) { if (&I != &L) { for (int i = 0, n = width * height; i < n; ++i) { - L (i) = I (i); + L(i) = I(i); } } return; } - Array2Df T (width, height); + Array2Df T(width, height); //--- X blur #ifdef _OPENMP #pragma omp parallel for shared(I, T) if(multithread) #endif - for ( int y = 0 ; y < height ; y++ ) { - for ( int x = 1 ; x < width - 1 ; x++ ) { - float t = 2.f * I (x, y); - t += I (x - 1, y); - t += I (x + 1, y); - T (x, y) = t * 0.25f; // t / 4.f; + for (int y = 0 ; y < height ; y++) { + for (int x = 1 ; x < width - 1 ; x++) { + float t = 2.f * I(x, y); + t += I(x - 1, y); + t += I(x + 1, y); + T(x, y) = t * 0.25f; // t / 4.f; } - T (0, y) = ( 3.f * I (0, y) + I (1, y) ) * 0.25f; // / 4.f; - T (width - 1, y) = ( 3.f * I (width - 1, y) + I (width - 2, y) ) * 0.25f; // / 4.f; + T(0, y) = (3.f * I(0, y) + I(1, y)) * 0.25f; // / 4.f; + T(width - 1, y) = (3.f * I(width - 1, y) + I(width - 2, y)) * 0.25f; // / 4.f; } //--- Y blur @@ -210,66 +212,66 @@ void gaussianBlur (const Array2Df& I, Array2Df& L, bool multithread) #pragma omp parallel for if(multithread) #endif - for ( int x = 0 ; x < width - 7 ; x += 8 ) { - for ( int y = 1 ; y < height - 1 ; y++ ) { + for (int x = 0 ; x < width - 7 ; x += 8) { + for (int y = 1 ; y < height - 1 ; y++) { for (int xx = 0; xx < 8; ++xx) { - float t = 2.f * T (x + xx, y); - t += T (x + xx, y - 1); - t += T (x + xx, y + 1); - L (x + xx, y) = t * 0.25f; // t/4.0f; + float t = 2.f * T(x + xx, y); + t += T(x + xx, y - 1); + t += T(x + xx, y + 1); + L(x + xx, y) = t * 0.25f; // t/4.0f; } } for (int xx = 0; xx < 8; ++xx) { - L (x + xx, 0) = ( 3.f * T (x + xx, 0) + T (x + xx, 1) ) * 0.25f; // / 4.0f; - L (x + xx, height - 1) = ( 3.f * T (x + xx, height - 1) + T (x + xx, height - 2) ) * 0.25f; // / 4.0f; + L(x + xx, 0) = (3.f * T(x + xx, 0) + T(x + xx, 1)) * 0.25f; // / 4.0f; + L(x + xx, height - 1) = (3.f * T(x + xx, height - 1) + T(x + xx, height - 2)) * 0.25f; // / 4.0f; } } - for ( int x = width - (width % 8) ; x < width ; x++ ) { - for ( int y = 1 ; y < height - 1 ; y++ ) { - float t = 2.f * T (x, y); - t += T (x, y - 1); - t += T (x, y + 1); - L (x, y) = t * 0.25f; // t/4.0f; + for (int x = width - (width % 8) ; x < width ; x++) { + for (int y = 1 ; y < height - 1 ; y++) { + float t = 2.f * T(x, y); + t += T(x, y - 1); + t += T(x, y + 1); + L(x, y) = t * 0.25f; // t/4.0f; } - L (x, 0) = ( 3.f * T (x, 0) + T (x, 1) ) * 0.25f; // / 4.0f; - L (x, height - 1) = ( 3.f * T (x, height - 1) + T (x, height - 2) ) * 0.25f; // / 4.0f; + L(x, 0) = (3.f * T(x, 0) + T(x, 1)) * 0.25f; // / 4.0f; + L(x, height - 1) = (3.f * T(x, height - 1) + T(x, height - 2)) * 0.25f; // / 4.0f; } } -void createGaussianPyramids (Array2Df** pyramids, int nlevels, bool multithread) +void createGaussianPyramids(Array2Df** pyramids, int nlevels, bool multithread) { // pyramids[0] is already set int width = pyramids[0]->getCols(); int height = pyramids[0]->getRows(); - Array2Df* L = new Array2Df (width, height); - gaussianBlur ( *pyramids[0], *L, multithread ); + Array2Df* L = new Array2Df(width, height); + gaussianBlur(*pyramids[0], *L, multithread); - for ( int k = 1 ; k < nlevels ; k++ ) { + for (int k = 1 ; k < nlevels ; k++) { if (width > 2 && height > 2) { width /= 2; height /= 2; - pyramids[k] = new Array2Df (width, height); - downSample (*L, *pyramids[k]); + pyramids[k] = new Array2Df(width, height); + downSample(*L, *pyramids[k]); } else { // RT - now nlevels is fixed in tmo_fattal02 (see the comment in // there), so it might happen that we have to add some padding to // the gaussian pyramids - pyramids[k] = new Array2Df (width, height); + pyramids[k] = new Array2Df(width, height); for (int j = 0, n = width * height; j < n; ++j) { - (*pyramids[k]) (j) = (*L) (j); + (*pyramids[k])(j) = (*L)(j); } } if (k < nlevels - 1) { delete L; - L = new Array2Df (width, height); - gaussianBlur ( *pyramids[k], *L, multithread ); + L = new Array2Df(width, height); + gaussianBlur(*pyramids[k], *L, multithread); } } @@ -278,36 +280,36 @@ void createGaussianPyramids (Array2Df** pyramids, int nlevels, bool multithread) //-------------------------------------------------------------------- -float calculateGradients (Array2Df* H, Array2Df* G, int k, bool multithread) +float calculateGradients(Array2Df* H, Array2Df* G, int k, bool multithread) { const int width = H->getCols(); const int height = H->getRows(); - const float divider = pow ( 2.0f, k + 1 ); + const float divider = pow(2.0f, k + 1); double avgGrad = 0.0; // use double precision for large summations #ifdef _OPENMP #pragma omp parallel for reduction(+:avgGrad) if(multithread) #endif - for ( int y = 0 ; y < height ; y++ ) { + for (int y = 0 ; y < height ; y++) { int n = (y == 0 ? 0 : y - 1); int s = (y + 1 == height ? y : y + 1); - for ( int x = 0 ; x < width ; x++ ) { + for (int x = 0 ; x < width ; x++) { float gx, gy; int w, e; w = (x == 0 ? 0 : x - 1); e = (x + 1 == width ? x : x + 1); - gx = ((*H) (w, y) - (*H) (e, y)); + gx = ((*H)(w, y) - (*H)(e, y)); - gy = ((*H) (x, s) - (*H) (x, n)); + gy = ((*H)(x, s) - (*H)(x, n)); // note this implicitly assumes that H(-1)=H(0) // for the fft-pde slover this would need adjustment as H(-1)=H(1) // is assumed, which means gx=0.0, gy=0.0 at the boundaries // however, the impact is not visible so we ignore this here - (*G) (x, y) = sqrt (gx * gx + gy * gy) / divider; + (*G)(x, y) = sqrt(gx * gx + gy * gy) / divider; avgGrad += static_cast((*G) (x, y)); } } @@ -317,7 +319,7 @@ float calculateGradients (Array2Df* H, Array2Df* G, int k, bool multithread) //-------------------------------------------------------------------- -void upSample (const Array2Df& A, Array2Df& B) +void upSample(const Array2Df& A, Array2Df& B) { const int width = B.getCols(); const int height = B.getRows(); @@ -325,14 +327,14 @@ void upSample (const Array2Df& A, Array2Df& B) const int aheight = A.getRows(); //#pragma omp parallel for shared(A, B) - for ( int y = 0 ; y < height ; y++ ) { - for ( int x = 0 ; x < width ; x++ ) { - int ax = static_cast (x * 0.5f); //x / 2.f; - int ay = static_cast (y * 0.5f); //y / 2.f; + for (int y = 0 ; y < height ; y++) { + for (int x = 0 ; x < width ; x++) { + int ax = static_cast(x * 0.5f); //x / 2.f; + int ay = static_cast(y * 0.5f); //y / 2.f; ax = (ax < awidth) ? ax : awidth - 1; ay = (ay < aheight) ? ay : aheight - 1; - B (x, y) = A (ax, ay); + B(x, y) = A(ax, ay); } } @@ -352,24 +354,25 @@ void upSample (const Array2Df& A, Array2Df& B) } -void calculateFiMatrix (Array2Df* FI, Array2Df* gradients[], - float avgGrad[], int nlevels, int detail_level, - float alfa, float beta, float noise, bool multithread) +void calculateFiMatrix(Array2Df* FI, Array2Df* gradients[], + float avgGrad[], int nlevels, int detail_level, + float alfa, float beta, float noise, bool multithread) { int width = gradients[nlevels - 1]->getCols(); int height = gradients[nlevels - 1]->getRows(); Array2Df** fi = new Array2Df*[nlevels]; - fi[nlevels - 1] = new Array2Df (width, height); + fi[nlevels - 1] = new Array2Df(width, height); #ifdef _OPENMP #pragma omp parallel for shared(fi) if(multithread) #endif - for ( int k = 0 ; k < width * height ; k++ ) { - (*fi[nlevels - 1]) (k) = 1.0f; + + for (int k = 0 ; k < width * height ; k++) { + (*fi[nlevels - 1])(k) = 1.0f; } - for ( int k = nlevels - 1; k >= 0 ; k-- ) { + for (int k = nlevels - 1; k >= 0 ; k--) { width = gradients[k]->getCols(); height = gradients[k]->getRows(); @@ -379,50 +382,51 @@ void calculateFiMatrix (Array2Df* FI, Array2Df* gradients[], #ifdef _OPENMP #pragma omp parallel for shared(fi,avgGrad) if(multithread) #endif - for ( int y = 0; y < height; y++ ) { - for ( int x = 0; x < width; x++ ) { + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { float grad = ((*gradients[k]) (x, y) < 1e-4f) ? 1e-4f : (*gradients[k]) (x, y); float a = alfa * avgGrad[k]; - float value = pow ((grad + noise) / a, beta - 1.0f); + float value = pow((grad + noise) / a, beta - 1.0f); - (*fi[k]) (x, y) *= value; + (*fi[k])(x, y) *= value; } } } // create next level - if ( k > 1 ) { + if (k > 1) { width = gradients[k - 1]->getCols(); height = gradients[k - 1]->getRows(); - fi[k - 1] = new Array2Df (width, height); + fi[k - 1] = new Array2Df(width, height); } else { fi[0] = FI; // highest level -> result } if (k > 0) { - upSample (*fi[k], *fi[k - 1]); // upsample to next level - gaussianBlur (*fi[k - 1], *fi[k - 1], multithread); + upSample(*fi[k], *fi[k - 1]); // upsample to next level + gaussianBlur(*fi[k - 1], *fi[k - 1], multithread); } } - for ( int k = 1 ; k < nlevels ; k++ ) { + for (int k = 1 ; k < nlevels ; k++) { delete fi[k]; } delete[] fi; } -void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread); +void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, int algo); -void tmo_fattal02 (size_t width, - size_t height, - const Array2Df& Y, - Array2Df& L, - float alfa, - float beta, - float noise, - int detail_level, - bool multithread) +void tmo_fattal02(size_t width, + size_t height, + const Array2Df& Y, + Array2Df& L, + float alfa, + float beta, + float noise, + int detail_level, + bool multithread, int algo) { // #ifdef TIMER_PROFILING // msec_timer stop_watch; @@ -431,13 +435,14 @@ void tmo_fattal02 (size_t width, // static const float black_point = 0.1f; // static const float white_point = 0.5f; static const float gamma = 1.0f; // 0.8f; +//paramet // static const int detail_level = 3; - if ( detail_level < 0 ) { + if (detail_level < 0) { detail_level = 0; } - if ( detail_level > 3 ) { + if (detail_level > 3) { detail_level = 3; } @@ -462,21 +467,26 @@ void tmo_fattal02 (size_t width, int size = width * height; - +//paramet // find max value, normalize to range 0..100 and take logarithm // float minLum = Y (0, 0); - float maxLum = Y (0, 0); + float maxLum = Y(0, 0); #ifdef _OPENMP #pragma omp parallel for reduction(max:maxLum) if(multithread) #endif - for ( int i = 0 ; i < size ; i++ ) { - maxLum = std::max (maxLum, Y (i)); + for (int i = 0 ; i < size ; i++) { + maxLum = std::max(maxLum, Y(i)); } - Array2Df* H = new Array2Df (width, height); + Array2Df* H = new Array2Df(width, height); float temp = 100.f / maxLum; + + if (algo == 1) { + temp = 1.f; + } + #ifdef _OPENMP #pragma omp parallel if(multithread) #endif @@ -495,13 +505,13 @@ void tmo_fattal02 (size_t width, #ifdef __SSE2__ for (; j < width - 3; j += 4) { - STVFU ((*H)[i][j], xlogf (tempv * LVFU (Y[i][j]) + epsv)); + STVFU((*H)[i][j], xlogf(tempv * LVFU(Y[i][j]) + epsv)); } #endif for (; j < width; ++j) { - (*H)[i][j] = xlogf (temp * Y[i][j] + eps); + (*H)[i][j] = xlogf(temp * Y[i][j] + eps); } } } @@ -528,13 +538,13 @@ void tmo_fattal02 (size_t width, * improved */ int fullwidth = width; int fullheight = height; - int dim = std::max (width, height); + int dim = std::max(width, height); Array2Df *fullH = nullptr; if (dim > RT_dimension_cap) { float s = float (RT_dimension_cap) / float (dim); - Array2Df *HH = new Array2Df (width * s, height * s); - rescale_bilinear (*H, *HH, multithread); + Array2Df *HH = new Array2Df(width * s, height * s); + rescale_bilinear(*H, *HH, multithread); fullH = H; H = HH; width = H->getCols(); @@ -547,25 +557,27 @@ void tmo_fattal02 (size_t width, Array2Df* pyramids[nlevels]; pyramids[0] = H; - createGaussianPyramids (pyramids, nlevels, multithread); + createGaussianPyramids(pyramids, nlevels, multithread); // calculate gradients and its average values on pyramid levels Array2Df* gradients[nlevels]; float avgGrad[nlevels]; - for ( int k = 0 ; k < nlevels ; k++ ) { - gradients[k] = new Array2Df (pyramids[k]->getCols(), pyramids[k]->getRows()); - avgGrad[k] = calculateGradients (pyramids[k], gradients[k], k, multithread); - if(k != 0) // pyramids[0] is H. Will be deleted later + for (int k = 0 ; k < nlevels ; k++) { + gradients[k] = new Array2Df(pyramids[k]->getCols(), pyramids[k]->getRows()); + avgGrad[k] = calculateGradients(pyramids[k], gradients[k], k, multithread); + + if (k != 0) { // pyramids[0] is H. Will be deleted later delete pyramids[k]; + } } // calculate fi matrix - Array2Df* FI = new Array2Df (width, height); - calculateFiMatrix (FI, gradients, avgGrad, nlevels, detail_level, alfa, beta, noise, multithread); + Array2Df* FI = new Array2Df(width, height); + calculateFiMatrix(FI, gradients, avgGrad, nlevels, detail_level, alfa, beta, noise, multithread); - for ( int i = 0 ; i < nlevels ; i++ ) { + for (int i = 0 ; i < nlevels ; i++) { delete gradients[i]; } @@ -573,8 +585,8 @@ void tmo_fattal02 (size_t width, if (fullH) { delete H; H = fullH; - Array2Df *FI2 = new Array2Df (fullwidth, fullheight); - rescale_bilinear (*FI, *FI2, multithread); + Array2Df *FI2 = new Array2Df(fullwidth, fullheight); + rescale_bilinear(*FI, *FI2, multithread); delete FI; FI = FI2; width = fullwidth; @@ -584,7 +596,7 @@ void tmo_fattal02 (size_t width, /** RT */ // attenuate gradients - Array2Df* Gx = new Array2Df (width, height); + Array2Df* Gx = new Array2Df(width, height); Array2Df* Gy = &L; // use L as buffer for Gy // the fft solver solves the Poisson pde but with slightly different @@ -595,11 +607,11 @@ void tmo_fattal02 (size_t width, #pragma omp parallel for if(multithread) #endif - for ( size_t y = 0 ; y < height ; y++ ) { + for (size_t y = 0 ; y < height ; y++) { // sets index+1 based on the boundary assumption H(N+1)=H(N-1) unsigned int yp1 = (y + 1 >= height ? height - 2 : y + 1); - for ( size_t x = 0 ; x < width ; x++ ) { + for (size_t x = 0 ; x < width ; x++) { // sets index+1 based on the boundary assumption H(N+1)=H(N-1) unsigned int xp1 = (x + 1 >= width ? width - 2 : x + 1); // forward differences in H, so need to use between-points approx of FI @@ -615,24 +627,24 @@ void tmo_fattal02 (size_t width, #pragma omp parallel for if(multithread) #endif - for ( size_t y = 0; y < height; ++y ) { - for ( size_t x = 0; x < width; ++x ) { - (*FI) (x, y) = (*Gx) (x, y) + (*Gy) (x, y); + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + (*FI)(x, y) = (*Gx)(x, y) + (*Gy)(x, y); - if ( x > 0 ) { - (*FI) (x, y) -= (*Gx) (x - 1, y); + if (x > 0) { + (*FI)(x, y) -= (*Gx)(x - 1, y); } - if ( y > 0 ) { - (*FI) (x, y) -= (*Gy) (x, y - 1); + if (y > 0) { + (*FI)(x, y) -= (*Gy)(x, y - 1); } if (x == 0) { - (*FI) (x, y) += (*Gx) (x, y); + (*FI)(x, y) += (*Gx)(x, y); } if (y == 0) { - (*FI) (x, y) += (*Gy) (x, y); + (*FI)(x, y) += (*Gy)(x, y); } } @@ -642,8 +654,8 @@ void tmo_fattal02 (size_t width, // solve pde and exponentiate (ie recover compressed image) { - MyMutex::MyLock lock (*fftwMutex); - solve_pde_fft (FI, &L, Gx, multithread); + MyMutex::MyLock lock(*fftwMutex); + solve_pde_fft(FI, &L, Gx, multithread, algo); } delete Gx; delete FI; @@ -653,7 +665,7 @@ void tmo_fattal02 (size_t width, #endif { #ifdef __SSE2__ - vfloat gammav = F2V (gamma); + vfloat gammav = F2V(gamma); #endif #ifdef _OPENMP #pragma omp for schedule(dynamic,16) @@ -664,13 +676,13 @@ void tmo_fattal02 (size_t width, #ifdef __SSE2__ for (; j < width - 3; j += 4) { - STVFU (L[i][j], xexpf (gammav * LVFU (L[i][j]))); + STVFU(L[i][j], xexpf(gammav * LVFU(L[i][j]))); } #endif for (; j < width; j++) { - L[i][j] = xexpf ( gamma * L[i][j]); + L[i][j] = xexpf(gamma * L[i][j]); } } } @@ -724,11 +736,11 @@ void tmo_fattal02 (size_t width, // returns T = EVy A EVx^tr // note, modifies input data -void transform_ev2normal (Array2Df *A, Array2Df *T, bool multithread) +void transform_ev2normal(Array2Df *A, Array2Df *T, bool multithread) { int width = A->getCols(); int height = A->getRows(); - assert ((int)T->getCols() == width && (int)T->getRows() == height); + assert((int)T->getCols() == width && (int)T->getRows() == height); // the discrete cosine transform is not exactly the transform needed // need to scale input values to get the right transformation @@ -736,19 +748,19 @@ void transform_ev2normal (Array2Df *A, Array2Df *T, bool multithread) #pragma omp parallel for if(multithread) #endif - for (int y = 1 ; y < height - 1 ; y++ ) - for (int x = 1 ; x < width - 1 ; x++ ) { - (*A) (x, y) *= 0.25f; + for (int y = 1 ; y < height - 1 ; y++) + for (int x = 1 ; x < width - 1 ; x++) { + (*A)(x, y) *= 0.25f; } - for (int x = 1 ; x < width - 1 ; x++ ) { - (*A) (x, 0) *= 0.5f; - (*A) (x, height - 1) *= 0.5f; + for (int x = 1 ; x < width - 1 ; x++) { + (*A)(x, 0) *= 0.5f; + (*A)(x, height - 1) *= 0.5f; } - for (int y = 1 ; y < height - 1 ; y++ ) { + for (int y = 1 ; y < height - 1 ; y++) { (*A) (0, y) *= 0.5f; - (*A) (width - 1, y) *= 0.5f; + (*A)(width - 1, y) *= 0.5f; } // note, fftw provides its own memory allocation routines which @@ -762,26 +774,26 @@ void transform_ev2normal (Array2Df *A, Array2Df *T, bool multithread) // executes 2d discrete cosine transform fftwf_plan p; - p = fftwf_plan_r2r_2d (height, width, A->data(), T->data(), - FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); - fftwf_execute (p); - fftwf_destroy_plan (p); + p = fftwf_plan_r2r_2d(height, width, A->data(), T->data(), + FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); + fftwf_execute(p); + fftwf_destroy_plan(p); } // returns T = EVy^-1 * A * (EVx^-1)^tr -void transform_normal2ev (Array2Df *A, Array2Df *T, bool multithread) +void transform_normal2ev(Array2Df *A, Array2Df *T, bool multithread) { int width = A->getCols(); int height = A->getRows(); - assert ((int)T->getCols() == width && (int)T->getRows() == height); + assert((int)T->getCols() == width && (int)T->getRows() == height); // executes 2d discrete cosine transform fftwf_plan p; - p = fftwf_plan_r2r_2d (height, width, A->data(), T->data(), - FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); - fftwf_execute (p); - fftwf_destroy_plan (p); + p = fftwf_plan_r2r_2d(height, width, A->data(), T->data(), + FFTW_REDFT00, FFTW_REDFT00, FFTW_ESTIMATE); + fftwf_execute(p); + fftwf_destroy_plan(p); // need to scale the output matrix to get the right transform float factor = (1.0f / ((height - 1) * (width - 1))); @@ -789,30 +801,30 @@ void transform_normal2ev (Array2Df *A, Array2Df *T, bool multithread) #pragma omp parallel for if(multithread) #endif - for (int y = 0 ; y < height ; y++ ) - for (int x = 0 ; x < width ; x++ ) { - (*T) (x, y) *= factor; + for (int y = 0 ; y < height ; y++) + for (int x = 0 ; x < width ; x++) { + (*T)(x, y) *= factor; } - for (int x = 0 ; x < width ; x++ ) { - (*T) (x, 0) *= 0.5f; - (*T) (x, height - 1) *= 0.5f; + for (int x = 0 ; x < width ; x++) { + (*T)(x, 0) *= 0.5f; + (*T)(x, height - 1) *= 0.5f; } - for (int y = 0 ; y < height ; y++ ) { - (*T) (0, y) *= 0.5f; - (*T) (width - 1, y) *= 0.5f; + for (int y = 0 ; y < height ; y++) { + (*T)(0, y) *= 0.5f; + (*T)(width - 1, y) *= 0.5f; } } // returns the eigenvalues of the 1d laplace operator -std::vector get_lambda (int n) +std::vector get_lambda(int n) { - assert (n > 1); - std::vector v (n); + assert(n > 1); + std::vector v(n); for (int i = 0; i < n; i++) { - v[i] = -4.0 * SQR (sin ((double)i / (2 * (n - 1)) * RT_PI)); + v[i] = -4.0 * SQR(sin((double)i / (2 * (n - 1)) * RT_PI)); } return v; @@ -862,22 +874,22 @@ std::vector get_lambda (int n) // not modified and the equation might not have a solution but an // approximate solution with a minimum error is then calculated // double precision version -void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/*, pfs::Progress &ph, +void solve_pde_fft(Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread, int algo)/*, pfs::Progress &ph, bool adjust_bound)*/ { // ph.setValue(20); //DEBUG_STR << "solve_pde_fft: solving Laplace U = F ..." << std::endl; int width = F->getCols(); int height = F->getRows(); - assert ((int)U->getCols() == width && (int)U->getRows() == height); - assert (buf->getCols() == width && buf->getRows() == height); + assert((int)U->getCols() == width && (int)U->getRows() == height); + assert(buf->getCols() == width && buf->getRows() == height); // activate parallel execution of fft routines #ifdef RT_FFTW3F_OMP if (multithread) { fftwf_init_threads(); - fftwf_plan_with_nthreads ( omp_get_max_threads() ); + fftwf_plan_with_nthreads(omp_get_max_threads()); } // #else @@ -897,29 +909,29 @@ void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/* // transforms F into eigenvector space: Ftr = //DEBUG_STR << "solve_pde_fft: transform F to ev space (fft)" << std::endl; Array2Df* F_tr = buf; - transform_normal2ev (F, F_tr, multithread); + transform_normal2ev(F, F_tr, multithread); // TODO: F no longer needed so could release memory, but as it is an // input parameter we won't do that // in the eigenvector space the solution is very simple - std::vector l1 = get_lambda (height); - std::vector l2 = get_lambda (width); + std::vector l1 = get_lambda(height); + std::vector l2 = get_lambda(width); #ifdef _OPENMP #pragma omp parallel for if(multithread) #endif - for (int y = 0 ; y < height ; y++ ) { - for (int x = 0 ; x < width ; x++ ) { + for (int y = 0 ; y < height ; y++) { + for (int x = 0 ; x < width ; x++) { (*F_tr) (x, y) = static_cast((*F_tr) (x, y)) / (l1[y] + l2[x]); } } - (*F_tr) (0, 0) = 0.f; // any value ok, only adds a const to the solution + (*F_tr)(0, 0) = 0.f; // any value ok, only adds a const to the solution // transforms F_tr back to the normal space - transform_ev2normal (F_tr, U, multithread); + transform_ev2normal(F_tr, U, multithread); // the solution U as calculated will satisfy something like int U = 0 // since for any constant c, U-c is also a solution and we are mainly @@ -927,21 +939,23 @@ void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/* // a solution which has no positive values: U_new(x,y)=U(x,y)-max // (not really needed but good for numerics as we later take exp(U)) //DEBUG_STR << "solve_pde_fft: removing constant from solution" << std::endl; - float maxVal = 0.f; + if (algo == 0) { + float maxVal = 0.f; #ifdef _OPENMP - #pragma omp parallel for reduction(max:maxVal) if(multithread) + #pragma omp parallel for reduction(max:maxVal) if(multithread) #endif - for (int i = 0; i < width * height; i++) { - maxVal = std::max(maxVal, (*U)(i)); - } + for (int i = 0; i < width * height; i++) { + maxVal = std::max(maxVal, (*U)(i)); + } #ifdef _OPENMP - #pragma omp parallel for if(multithread) + #pragma omp parallel for if(multithread) #endif - for (int i = 0; i < width * height; i++) { - (*U) (i) -= maxVal; + for (int i = 0; i < width * height; i++) { + (*U)(i) -= maxVal; + } } } @@ -975,27 +989,27 @@ void solve_pde_fft (Array2Df *F, Array2Df *U, Array2Df *buf, bool multithread)/* * RT code from here on *****************************************************************************/ -inline void rescale_bilinear (const Array2Df &src, Array2Df &dst, bool multithread) +inline void rescale_bilinear(const Array2Df &src, Array2Df &dst, bool multithread) { rescaleBilinear(src, dst, multithread); } -inline void rescale_nearest (const Array2Df &src, Array2Df &dst, bool multithread) +inline void rescale_nearest(const Array2Df &src, Array2Df &dst, bool multithread) { rescaleNearest(src, dst, multithread); } -inline float luminance (float r, float g, float b, TMatrix ws) +inline float luminance(float r, float g, float b, TMatrix ws) { return Color::rgbLuminance(r, g, b, ws); } -inline int round_up_pow2 (int dim) +inline int round_up_pow2(int dim) { // from https://graphics.stanford.edu/~seander/bithacks.html - assert (dim > 0); + assert(dim > 0); unsigned int v = dim; v--; v |= v >> 1; @@ -1007,7 +1021,7 @@ inline int round_up_pow2 (int dim) return v; } -inline int find_fast_dim (int dim) +inline int find_fast_dim(int dim) { // as per the FFTW docs: // @@ -1019,7 +1033,7 @@ inline int find_fast_dim (int dim) // the above form. This is not exhaustive, but should be ok for pictures // up to 100MPix at least - int d1 = round_up_pow2 (dim); + int d1 = round_up_pow2(dim); std::vector d = { d1 / 128 * 65, d1 / 64 * 33, @@ -1041,72 +1055,93 @@ inline int find_fast_dim (int dim) } } - assert (false); + assert(false); return dim; } + + } // namespace -void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) +void ImProcFunctions::ToneMapFattal02(Imagefloat *rgb, const FattalToneMappingParams &fatParams, int detail_level, int Lalone, float **Lum, int WW, int HH, int algo) +//algo allows to use ART algorithme algo = 0 RT, algo = 1 ART +//Lalone allows to use L without RGB values in RT mode { - if (!params->fattal.enabled) { + if (!fatParams.enabled) { return; } - + BENCHFUN - const int detail_level = 3; +// const int detail_level = 3; float alpha = 1.f; - if (params->fattal.threshold < 0) { - alpha += (params->fattal.threshold * 0.9f) / 100.f; - } else if (params->fattal.threshold > 0) { - alpha += params->fattal.threshold / 100.f; + if (fatParams.threshold < 0) { + alpha += (fatParams.threshold * 0.9f) / 100.f; + } else if (fatParams.threshold > 0) { + alpha += fatParams.threshold / 100.f; } - float beta = 1.f - (params->fattal.amount * 0.3f) / 100.f; + float beta = 1.f - (fatParams.amount * 0.3f) / 100.f; // sanity check if (alpha <= 0 || beta <= 0) { return; } - int w = rgb->getWidth(); - int h = rgb->getHeight(); + int w; + int h; - Array2Df Yr (w, h); + if (Lalone != 0) { + w = WW; + h = HH; + } else { + w = rgb->getWidth(); + h = rgb->getHeight(); + } + + Array2Df Yr(w, h); constexpr float epsilon = 1e-4f; constexpr float luminance_noise_floor = 65.535f; constexpr float min_luminance = 1.f; - TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix (params->icm.workingProfile); - + TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix(params->icm.workingProfile); #ifdef _OPENMP #pragma omp parallel for if(multiThread) #endif + for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - Yr (x, y) = std::max (luminance (rgb->r (y, x), rgb->g (y, x), rgb->b (y, x), ws), min_luminance); // clip really black pixels + if (Lalone != 0) { + Yr(x, y) = std::max(2.f * Lum[y][x], min_luminance); // clip really black pixels + } else { + Yr(x, y) = std::max(luminance(rgb->r(y, x), rgb->g(y, x), rgb->b(y, x), ws), min_luminance); // clip really black pixels + } } } float oldMedian; - const float percentile = float(LIM(params->fattal.anchor, 1, 100)) / 100.f; - findMinMaxPercentile (Yr.data(), static_cast(Yr.getRows()) * Yr.getCols(), percentile, oldMedian, percentile, oldMedian, multiThread); + float percentile = 1.f; + + if (algo == 0) { + percentile = float(LIM(fatParams.anchor, 1, 100)) / 100.f; + findMinMaxPercentile(Yr.data(), static_cast(Yr.getRows()) * Yr.getCols(), percentile, oldMedian, percentile, oldMedian, multiThread); + } + // median filter on the deep shadows, to avoid boosting noise // because w2 >= w and h2 >= h, we can use the L buffer as temporary buffer for Median_Denoise() - int w2 = find_fast_dim (w) + 1; - int h2 = find_fast_dim (h) + 1; - Array2Df L (w2, h2); + int w2 = find_fast_dim(w) + 1; + int h2 = find_fast_dim(h) + 1; + Array2Df L(w2, h2); { #ifdef _OPENMP int num_threads = multiThread ? omp_get_max_threads() : 1; #else int num_threads = 1; #endif - float r = float (std::max (w, h)) / float (RT_dimension_cap); + float r = float (std::max(w, h)) / float (RT_dimension_cap); Median med; if (r >= 3) { @@ -1119,7 +1154,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) med = Median::TYPE_3X3_STRONG; } - Median_Denoise (Yr, Yr, luminance_noise_floor, w, h, med, 1, num_threads, L); + Median_Denoise(Yr, Yr, luminance_noise_floor, w, h, med, 1, num_threads, L); } float noise = alpha * 0.01f; @@ -1129,19 +1164,72 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) << ", detail_level = " << detail_level << std::endl; } - rescale_nearest (Yr, L, multiThread); - tmo_fattal02 (w2, h2, L, L, alpha, beta, noise, detail_level, multiThread); + rescale_nearest(Yr, L, multiThread); + + tmo_fattal02(w2, h2, L, L, alpha, beta, noise, detail_level, multiThread, 0); const float hr = float(h2) / float(h); const float wr = float(w2) / float(w); - float newMedian; - findMinMaxPercentile (L.data(), static_cast(L.getRows()) * L.getCols(), percentile, newMedian, percentile, newMedian, multiThread); - const float scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan + float offset = 0.f; + float scale = 65535.f; + + if (algo == 0) { + float newMedian; + findMinMaxPercentile(L.data(), static_cast(L.getRows()) * L.getCols(), percentile, newMedian, percentile, newMedian, multiThread); + scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan + } else { + + scale = 65535.f; + { + float ratio = 0.f; + int ww, hh; + + if (w >= h) { + ratio = 200.f / w; + ww = 200; + hh = ratio * h; + } else { + ratio = 200.f / h; + hh = 200; + ww = ratio * w; + } + + Array2Df tmp(ww, hh); + int sz = ww * hh; + int idx = sz / 2; + int oidx = LIM(int(sz * 0.05f + 0.5f), 1, sz - 1); + rescale_nearest(Yr, tmp, multiThread); + std::sort(tmp.data(), tmp.data() + sz); + float oldMedian = tmp(idx); + float old_min = 0.f; + + for (int i = 0; i <= oidx; ++i) { + old_min += tmp(i); + } + + old_min /= oidx; + rescale_nearest(L, tmp, multiThread); + std::sort(tmp.data(), tmp.data() + sz); + float newMedian = tmp(idx); + scale = (oldMedian == 0.f || newMedian == 0.f) ? 65535.f : (oldMedian / newMedian); // avoid Nan + float new_min = 0.f; + + for (int i = 0; i <= oidx; ++i) { + new_min += tmp(i); + } + + new_min /= oidx; + offset = old_min - new_min; + } + + + } #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if(multiThread) #endif + for (int y = 0; y < h; y++) { int yy = y * hr + 1; @@ -1150,16 +1238,106 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) float Y = std::max(Yr(x, y), epsilon); float l = std::max(L(xx, yy), epsilon) * (scale / Y); - rgb->r(y, x) *= l; - rgb->g(y, x) *= l; - rgb->b(y, x) *= l; - assert(std::isfinite(rgb->r(y, x))); - assert(std::isfinite(rgb->g(y, x))); - assert(std::isfinite(rgb->b(y, x))); + if (Lalone == 0) { + float &r = rgb->r(y, x); + float &g = rgb->g(y, x); + float &b = rgb->b(y, x); + if(l > 1.f) { + r = max(r * l - offset, r); + g = max(g * l - offset, g); + b = max(b * l - offset, b); + } else { + r *= l; + g *= l; + b *= l; + } + assert(std::isfinite(rgb->r(y, x))); + assert(std::isfinite(rgb->g(y, x))); + assert(std::isfinite(rgb->b(y, x))); + } else { + if (Lalone == 1) { + Lum[y][x] *= (0.5f * l - offset); + } else if (Lalone == -1) { + Lum[y][x] *= (-0.5f * l + offset); + } + } } } + } +void buildGradientsMask(int W, int H, float **luminance, float **out, + float amount, int nlevels, int detail_level, + float alfa, float beta, bool multithread) +{ + Array2Df Y(W, H, luminance); + const float noise = alfa * 0.01f; + + Array2Df *pyramids[nlevels]; + pyramids[0] = &Y; + createGaussianPyramids(pyramids, nlevels, multithread); + + // calculate gradients and its average values on pyramid levels + Array2Df *gradients[nlevels]; + float avgGrad[nlevels]; + + for (int k = 0 ; k < nlevels ; k++) { + gradients[k] = new Array2Df(pyramids[k]->getCols(), pyramids[k]->getRows()); + avgGrad[k] = calculateGradients(pyramids[k], gradients[k], k, multithread); + + if (k != 0) { // pyramids[0] is Y + delete pyramids[k]; + } + } + + + // calculate fi matrix + Array2Df FI(W, H, out); + calculateFiMatrix(&FI, gradients, avgGrad, nlevels, detail_level, alfa, beta, noise, multithread); + + for (int i = 0 ; i < nlevels ; i++) { + delete gradients[i]; + } + + // rescale the mask + float m = out[0][0]; +#ifdef _OPENMP + # pragma omp parallel for reduction(max:m) if (multithread) +#endif + + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float v = std::abs(out[y][x]); + out[y][x] = v; + m = std::max(v, m); + } + } + + if (m > 0.f) { + const float f = amount / m; +#ifdef _OPENMP + # pragma omp parallel for reduction(max:m) if (multithread) +#endif + + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + out[y][x] *= f; + } + } + } + + // { + // Imagefloat tmp(W, H); + // for (int y = 0; y < H; ++y) { + // for (int x = 0; x < W; ++x) { + // tmp.r(y, x) = tmp.g(y, x) = tmp.b(y, x) = out[y][x] * 65535.f; + // } + // } + // std::ostringstream name; + // name << "/tmp/FI-" << W << "x" << H << ".tif"; + // tmp.saveAsTIFF(name.str(), 16); + // } +} } // namespace rtengine diff --git a/rtengine/xtrans_demosaic.cc b/rtengine/xtrans_demosaic.cc index b01b0ac1c..5f5afa073 100644 --- a/rtengine/xtrans_demosaic.cc +++ b/rtengine/xtrans_demosaic.cc @@ -45,7 +45,9 @@ void RawImageSource::cielab (const float (*rgb)[3], float* l, float* a, float *b if (!rgb) { static bool cbrtinit = false; if(!cbrtinit) { - #pragma omp parallel for +#ifdef _OPENMP + #pragma omp parallel for +#endif 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; diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index 95b46c2b9..c0038c067 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -3059,19 +3059,14 @@ void ExifManager::parse (bool isRaw, bool skipIgnored, bool parseJpeg) bool frameRootDetected = false; - if(!frameRootDetected) { - std::vector risTagList = root->findTags("RawImageSegmentation"); - if (!risTagList.empty()) { - for (auto ris : risTagList) { - frames.push_back(ris->getParent()); - frameRootDetected = true; + for (auto ris : root->findTags("RawImageSegmentation")) { + frames.push_back(ris->getParent()); + frameRootDetected = true; - #if PRINT_METADATA_TREE - printf("\n--------------- FRAME (RAWIMAGESEGMENTATION) ---------------\n\n"); - ris->getParent()->printAll (); - #endif - } - } +#if PRINT_METADATA_TREE + printf("\n--------------- FRAME (RAWIMAGESEGMENTATION) ---------------\n\n"); + ris->getParent()->printAll (); +#endif } if(!frameRootDetected) { diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 52459f712..cfadb7928 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -34,6 +34,7 @@ set(NONCLISOURCEFILES colorappearance.cc coloredbar.cc colortoning.cc + controlspotpanel.cc coordinateadjuster.cc crop.cc crophandler.cc @@ -90,6 +91,9 @@ set(NONCLISOURCEFILES lensgeom.cc lensprofile.cc localcontrast.cc + locallab.cc + locallabtools.cc + locallabtools2.cc lockablecolorpicker.cc lwbutton.cc lwbuttonset.cc diff --git a/rtgui/addsetids.h b/rtgui/addsetids.h index 7a267bb02..6f68c6ae7 100644 --- a/rtgui/addsetids.h +++ b/rtgui/addsetids.h @@ -18,6 +18,12 @@ enum { ADDSET_ROTATE_DEGREE, ADDSET_DIST_AMOUNT, ADDSET_PERSPECTIVE, + ADDSET_PERSP_CAM_ANGLE, + ADDSET_PERSP_CAM_FOCAL_LENGTH, + ADDSET_PERSP_CAM_SHIFT, + ADDSET_PERSP_PROJ_ANGLE, + ADDSET_PERSP_PROJ_ROTATE, + ADDSET_PERSP_PROJ_SHIFT, ADDSET_CA, ADDSET_VIGN_AMOUNT, ADDSET_VIGN_RADIUS, diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc index e5d926b69..4d7a08b5f 100644 --- a/rtgui/batchtoolpanelcoord.cc +++ b/rtgui/batchtoolpanelcoord.cc @@ -62,7 +62,8 @@ void BatchToolPanelCoordinator::selectionChanged (const std::vector& void BatchToolPanelCoordinator::closeSession (bool save) { - pparamsEdited.set (false); + // Should remain commented for Locallab to work + // pparamsEdited.set (false); for (size_t i = 0; i < selected.size(); i++) { selected[i]->removeThumbnailListener (this); @@ -139,7 +140,7 @@ void BatchToolPanelCoordinator::initSession () if (selected.size() == 1) { for (size_t i = 0; i < toolPanels.size(); i++) { - toolPanels.at(i)->setMultiImage(false); + toolPanels.at (i)->setMultiImage (false); } toneCurve->setAdjusterBehavior (false, false, false, false, false, false, false, false); @@ -151,7 +152,7 @@ void BatchToolPanelCoordinator::initSession () rotate->setAdjusterBehavior (false); resize->setAdjusterBehavior (false); distortion->setAdjusterBehavior (false); - perspective->setAdjusterBehavior (false); + perspective->setAdjusterBehavior (false, false, false, false, false, false, false); gradient->setAdjusterBehavior (false, false, false, false); pcvignette->setAdjusterBehavior (false, false, false); cacorrection->setAdjusterBehavior (false); @@ -177,14 +178,14 @@ void BatchToolPanelCoordinator::initSession () xtransprocess->setAdjusterBehavior(false, false); bayerpreprocess->setAdjusterBehavior (false, false); rawcacorrection->setAdjusterBehavior (false); - flatfield->setAdjusterBehavior(false); + flatfield->setAdjusterBehavior (false); rawexposure->setAdjusterBehavior (false); bayerrawexposure->setAdjusterBehavior (false); xtransrawexposure->setAdjusterBehavior (false); } else { for (size_t i = 0; i < toolPanels.size(); i++) { - toolPanels.at(i)->setMultiImage(true); + toolPanels.at (i)->setMultiImage (true); } toneCurve->setAdjusterBehavior (options.baBehav[ADDSET_TC_EXPCOMP], options.baBehav[ADDSET_TC_HLCOMPAMOUNT], options.baBehav[ADDSET_TC_HLCOMPTHRESH], options.baBehav[ADDSET_TC_BRIGHTNESS], options.baBehav[ADDSET_TC_BLACKLEVEL], options.baBehav[ADDSET_TC_SHCOMP], options.baBehav[ADDSET_TC_CONTRAST], options.baBehav[ADDSET_TC_SATURATION]); @@ -196,7 +197,7 @@ void BatchToolPanelCoordinator::initSession () rotate->setAdjusterBehavior (options.baBehav[ADDSET_ROTATE_DEGREE]); resize->setAdjusterBehavior (options.baBehav[ADDSET_RESIZE_SCALE]); distortion->setAdjusterBehavior (options.baBehav[ADDSET_DIST_AMOUNT]); - perspective->setAdjusterBehavior (options.baBehav[ADDSET_PERSPECTIVE]); + perspective->setAdjusterBehavior (options.baBehav[ADDSET_PERSPECTIVE], options.baBehav[ADDSET_PERSP_CAM_FOCAL_LENGTH], options.baBehav[ADDSET_PERSP_CAM_SHIFT], options.baBehav[ADDSET_PERSP_CAM_ANGLE], options.baBehav[ADDSET_PERSP_PROJ_ANGLE], options.baBehav[ADDSET_PERSP_PROJ_SHIFT], options.baBehav[ADDSET_PERSP_PROJ_ROTATE]); gradient->setAdjusterBehavior (options.baBehav[ADDSET_GRADIENT_DEGREE], options.baBehav[ADDSET_GRADIENT_FEATHER], options.baBehav[ADDSET_GRADIENT_STRENGTH], options.baBehav[ADDSET_GRADIENT_CENTER]); pcvignette->setAdjusterBehavior (options.baBehav[ADDSET_PCVIGNETTE_STRENGTH], options.baBehav[ADDSET_PCVIGNETTE_FEATHER], options.baBehav[ADDSET_PCVIGNETTE_ROUNDNESS]); cacorrection->setAdjusterBehavior (options.baBehav[ADDSET_CA]); @@ -225,7 +226,7 @@ void BatchToolPanelCoordinator::initSession () xtransprocess->setAdjusterBehavior(options.baBehav[ADDSET_BAYER_FALSE_COLOR_SUPPRESSION], options.baBehav[ADDSET_BAYER_DUALDEMOZCONTRAST]); bayerpreprocess->setAdjusterBehavior (options.baBehav[ADDSET_PREPROCESS_LINEDENOISE], options.baBehav[ADDSET_PREPROCESS_GREENEQUIL]); rawcacorrection->setAdjusterBehavior (options.baBehav[ADDSET_RAWCACORR]); - flatfield->setAdjusterBehavior(options.baBehav[ADDSET_RAWFFCLIPCONTROL]); + flatfield->setAdjusterBehavior (options.baBehav[ADDSET_RAWFFCLIPCONTROL]); rawexposure->setAdjusterBehavior (options.baBehav[ADDSET_RAWEXPOS_LINEAR]); bayerrawexposure->setAdjusterBehavior (options.baBehav[ADDSET_RAWEXPOS_BLACKS]); xtransrawexposure->setAdjusterBehavior (options.baBehav[ADDSET_RAWEXPOS_BLACKS]); @@ -308,6 +309,12 @@ void BatchToolPanelCoordinator::initSession () if (options.baBehav[ADDSET_RESIZE_SCALE]) { pparams.resize.scale = 0; } if (options.baBehav[ADDSET_DIST_AMOUNT]) { pparams.distortion.amount = 0; } if (options.baBehav[ADDSET_PERSPECTIVE]) { pparams.perspective.horizontal = pparams.perspective.vertical = 0; } + if (options.baBehav[ADDSET_PERSP_CAM_FOCAL_LENGTH]) { pparams.perspective.camera_focal_length = pparams.perspective.camera_crop_factor = 0; } + if (options.baBehav[ADDSET_PERSP_CAM_SHIFT]) { pparams.perspective.camera_shift_horiz = pparams.perspective.camera_shift_vert = 0; } + if (options.baBehav[ADDSET_PERSP_CAM_ANGLE]) { pparams.perspective.camera_yaw = pparams.perspective.camera_pitch = 0; } + if (options.baBehav[ADDSET_PERSP_PROJ_ANGLE]) { pparams.perspective.projection_yaw = pparams.perspective.projection_pitch = 0; } + if (options.baBehav[ADDSET_PERSP_PROJ_SHIFT]) { pparams.perspective.projection_shift_horiz = pparams.perspective.projection_shift_vert = 0; } + if (options.baBehav[ADDSET_PERSP_PROJ_ROTATE]) { pparams.perspective.projection_rotate = 0; } if (options.baBehav[ADDSET_GRADIENT_DEGREE]) { pparams.gradient.degree = 0; } if (options.baBehav[ADDSET_GRADIENT_FEATHER]) { pparams.gradient.feather = 0; } if (options.baBehav[ADDSET_GRADIENT_STRENGTH]) { pparams.gradient.strength = 0; } @@ -392,9 +399,12 @@ void BatchToolPanelCoordinator::initSession () for (size_t i = 0; i < paramcListeners.size(); i++) // send this initial state to the History { - paramcListeners[i]->procParamsChanged (&pparams, rtengine::EvPhotoLoaded, M("BATCH_PROCESSING"), &pparamsEdited); + paramcListeners[i]->procParamsChanged (&pparams, rtengine::EvPhotoLoaded, M ("BATCH_PROCESSING"), &pparamsEdited); } } + + // ParamsEdited are set to false for initialization and is updated each time panel is changed (mandatory for Locallab) + pparamsEdited.set(false); } void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const Glib::ustring& descr) @@ -405,7 +415,8 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c somethingChanged = true; - pparamsEdited.set (false); + // Should remain commented for Locallab to work + // pparamsEdited.set (false); // read new values from the gui for (size_t i = 0; i < toolPanels.size(); i++) { @@ -417,7 +428,7 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c if (selected.size() == 1) { // Compensate rotation on flip if (event == rtengine::EvCTHFlip || event == rtengine::EvCTVFlip) { - if (fabs(pparams.rotate.degree) > 0.001) { + if (fabs (pparams.rotate.degree) > 0.001) { pparams.rotate.degree *= -1; rotate->read (&pparams); } @@ -425,7 +436,7 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c int w, h; selected[0]->getFinalSize (selected[0]->getProcParams (), w, h); - crop->setDimensions(w, h); + crop->setDimensions (w, h); // Some transformations change the crop and resize parameter for convenience. if (event == rtengine::EvCTHFlip) { @@ -447,7 +458,7 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c // Compensate rotation on flip if (event == rtengine::EvCTHFlip || event == rtengine::EvCTVFlip) { for (size_t i = 0; i < selected.size(); i++) { - if (fabs(initialPP[i].rotate.degree) > 0.001) { + if (fabs (initialPP[i].rotate.degree) > 0.001) { initialPP[i].rotate.degree *= -1.0; pparamsEdited.rotate.degree = false; @@ -489,30 +500,30 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c int rotation = (360 + newDeg - oldDeg) % 360; ProcParams pptemp = selected[i]->getProcParams(); // Get actual procparams - if((pptemp.coarse.hflip != pptemp.coarse.vflip) && ((rotation % 180) == 90)) { + if ((pptemp.coarse.hflip != pptemp.coarse.vflip) && ((rotation % 180) == 90)) { rotation = (rotation + 180) % 360; } switch (rotation) { - case 90: - std::swap(crop.x, crop.y); - std::swap(crop.w, crop.h); + case 90: + std::swap (crop.x, crop.y); + std::swap (crop.w, crop.h); - crop.x = h - crop.x - crop.w; - break; + crop.x = h - crop.x - crop.w; + break; - case 270: - std::swap(crop.x, crop.y); - std::swap(crop.w, crop.h); + case 270: + std::swap (crop.x, crop.y); + std::swap (crop.w, crop.h); - crop.y = w - crop.y - crop.h; - break; + crop.y = w - crop.y - crop.h; + break; - case 180: - crop.x = w - crop.x - crop.w; - crop.y = h - crop.y - crop.h; - break; + case 180: + crop.x = w - crop.x - crop.w; + crop.y = h - crop.y - crop.h; + break; } initialPP[i].coarse.rotate = newDeg; @@ -599,13 +610,13 @@ void BatchToolPanelCoordinator::procParamsChanged (Thumbnail* thm, int whoChange } } -void BatchToolPanelCoordinator::beginBatchPParamsChange(int numberOfEntries) +void BatchToolPanelCoordinator::beginBatchPParamsChange (int numberOfEntries) { blockedUpdate = true; if (numberOfEntries > 50) { // Arbitrary amount - parent->set_sensitive(false); + parent->set_sensitive (false); } } @@ -616,7 +627,7 @@ void BatchToolPanelCoordinator::endBatchPParamsChange() closeSession (false); initSession (); blockedUpdate = false; - parent->set_sensitive(true); + parent->set_sensitive (true); } /* @@ -638,7 +649,7 @@ void BatchToolPanelCoordinator::profileChange( return; } - pparams = *(nparams->pparams); + pparams = * (nparams->pparams); if (paramsEdited) { pparamsEdited = *paramsEdited; diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index 9ba011c59..a1aa3f01e 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -29,7 +29,9 @@ using namespace rtengine; using namespace rtengine::procparams; -BayerProcess::BayerProcess () : FoldableToolPanel(this, "bayerprocess", M("TP_RAW_LABEL"), options.prevdemo != PD_Sidecar) +BayerProcess::BayerProcess () : + FoldableToolPanel(this, "bayerprocess", M("TP_RAW_LABEL"), options.prevdemo != PD_Sidecar), + oldMethod(-1) { auto m = ProcEventMapper::getInstance(); diff --git a/rtgui/cachemanager.cc b/rtgui/cachemanager.cc index 93dafc1ba..5e540b604 100644 --- a/rtgui/cachemanager.cc +++ b/rtgui/cachemanager.cc @@ -79,6 +79,7 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) // if it is open, return it const auto iterator = openEntries.find (fname); + if (iterator != openEntries.end ()) { auto cachedThumbnail = iterator->second; @@ -102,9 +103,11 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) CacheImageData imageData; const auto error = imageData.load (cacheName); + if (error == 0 && imageData.supported) { thumbnail.reset (new Thumbnail (this, fname, &imageData)); + if (!thumbnail->isSupported ()) { thumbnail.reset (); } @@ -115,6 +118,7 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) if (!thumbnail) { thumbnail.reset (new Thumbnail (this, fname, md5)); + if (!thumbnail->isSupported ()) { thumbnail.reset (); } @@ -126,6 +130,7 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) MyMutex::MyLock lock (mutex); const auto iterator = openEntries.find (fname); + if (iterator != openEntries.end ()) { auto cachedThumbnail = iterator->second; @@ -148,6 +153,7 @@ void CacheManager::deleteEntry (const Glib::ustring& fname) // check if it is opened auto iterator = openEntries.find (fname); + if (iterator == openEntries.end ()) { deleteFiles (fname, getMD5 (fname), true, true); return; @@ -195,6 +201,7 @@ void CacheManager::renameEntry (const std::string& oldfilename, const std::strin // check if it is opened // if it is open, update md5 const auto iterator = openEntries.find (oldfilename); + if (iterator == openEntries.end ()) { return; } @@ -246,8 +253,10 @@ void CacheManager::clearProfiles () const MyMutex::MyLock lock (mutex); deleteDir ("profiles"); + } + void CacheManager::deleteDir (const Glib::ustring& dirName) const { try { @@ -255,6 +264,7 @@ void CacheManager::deleteDir (const Glib::ustring& dirName) const Glib::Dir dir (Glib::build_filename (baseDir, dirName)); auto error = 0; + for (auto entry = dir.begin (); entry != dir.end (); ++entry) { error |= g_remove (Glib::build_filename (baseDir, dirName, *entry).c_str ()); } @@ -325,9 +335,9 @@ std::string CacheManager::getMD5 (const Glib::ustring& fname) } Glib::ustring CacheManager::getCacheFileName (const Glib::ustring& subDir, - const Glib::ustring& fname, - const Glib::ustring& fext, - const Glib::ustring& md5) const + const Glib::ustring& fname, + const Glib::ustring& fext, + const Glib::ustring& md5) const { const auto dirName = Glib::build_filename (baseDir, subDir); const auto baseName = Glib::path_get_basename (fname) + "." + md5; diff --git a/rtgui/cachemanager.h b/rtgui/cachemanager.h index 56370e966..61602aeba 100644 --- a/rtgui/cachemanager.h +++ b/rtgui/cachemanager.h @@ -59,7 +59,6 @@ public: void clearImages () const; void clearProfiles () const; void clearFromCache (const Glib::ustring& fname, bool purge) const; - static std::string getMD5 (const Glib::ustring& fname); Glib::ustring getCacheFileName (const Glib::ustring& subDir, diff --git a/rtgui/config.h.in b/rtgui/config.h.in index 558c25f76..d3fc58cf7 100644 --- a/rtgui/config.h.in +++ b/rtgui/config.h.in @@ -22,8 +22,9 @@ #cmakedefine BUILD_BUNDLE #cmakedefine HAVE_UNALIGNED_MALLOC +#cmakedefine OSX_DEV_BUILD -#ifdef __APPLE__ +#if defined(__APPLE__) && !defined(OSX_DEV_BUILD) #define DATA_SEARCH_PATH "/Applications/RawTherapee.app/Contents/Resources/share" #define DOC_SEARCH_PATH "/Applications/RawTherapee.app/Contents/Resources" #define CREDITS_SEARCH_PATH "/Applications/RawTherapee.app/Contents/Resources" diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc new file mode 100644 index 000000000..83d62f572 --- /dev/null +++ b/rtgui/controlspotpanel.cc @@ -0,0 +1,2516 @@ +/* + * 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 . + * 2018 Pierre Cabrera + */ + +#include "../rtengine/rt_math.h" +#include "controlspotpanel.h" +#include "editwidgets.h" +#include "options.h" +#include "../rtengine/procparams.h" +#include "rtimage.h" + +using namespace rtengine; +using namespace procparams; + +extern Options options; + +//----------------------------------------------------------------------------- +// ControlSpotPanel +//----------------------------------------------------------------------------- + +ControlSpotPanel::ControlSpotPanel(): + EditSubscriber(ET_OBJECTS), + FoldableToolPanel(this, "controlspotpanel", M("TP_LOCALLAB_SETTINGS")), + + scrolledwindow_(Gtk::manage(new Gtk::ScrolledWindow())), + treeview_(Gtk::manage(new Gtk::TreeView())), + + button_add_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_ADD")))), + button_delete_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_DEL")))), + button_duplicate_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_DUPL")))), + + button_rename_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_REN")))), + button_visibility_(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_BUTTON_VIS")))), + + shape_(Gtk::manage(new MyComboBoxText())), + spotMethod_(Gtk::manage(new MyComboBoxText())), + shapeMethod_(Gtk::manage(new MyComboBoxText())), + qualityMethod_(Gtk::manage(new MyComboBoxText())), + complexMethod_(Gtk::manage(new MyComboBoxText())), + wavMethod_(Gtk::manage(new MyComboBoxText())), + + sensiexclu_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIEXCLU"), 0, 100, 1, 12))), + structexclu_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUCCOL"), 0, 100, 1, 0))), + locX_(Gtk::manage(new Adjuster(M("TP_LOCAL_WIDTH"), 2, 3000, 1, 150))), + locXL_(Gtk::manage(new Adjuster(M("TP_LOCAL_WIDTH_L"), 2, 3000, 1, 150))), + locY_(Gtk::manage(new Adjuster(M("TP_LOCAL_HEIGHT"), 2, 3000, 1, 150))), + locYT_(Gtk::manage(new Adjuster(M("TP_LOCAL_HEIGHT_T"), 2, 3000, 1, 150))), + centerX_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CENTER_X"), -1000, 1000, 1, 0))), + centerY_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CENTER_Y"), -1000, 1000, 1, 0))), + circrad_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CIRCRADIUS"), 2, 150, 1, 18))), + transit_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_TRANSITVALUE"), 2., 100., 0.1, 60.))), + transitweak_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_TRANSITWEAK"), 0.5, 25.0, 0.1, 1.0))), + transitgrad_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_TRANSITGRAD"), -1.0, 1.0, 0.01, 0.0))), + feather_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FEATVALUE"), 10., 100., 0.1, 25.))), + struc_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_THRES"), 1.0, 12.0, 0.1, 4.0))), + thresh_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_THRESDELTAE"), 0.0, 10.0, 0.1, 2.0))), + iter_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_PROXI"), 0.2, 10.0, 0.1, 2.0))), + balan_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BALAN"), 0.2, 2.5, 0.1, 1.0, Gtk::manage(new RTImage("rawtherapee-logo-16.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + balanh_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BALANH"), 0.2, 2.5, 0.1, 1.0, Gtk::manage(new RTImage("rawtherapee-logo-16.png")), Gtk::manage(new RTImage("circle-red-green-small.png"))))), + colorde_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_COLORDE"), -15, 15, 2, 5, Gtk::manage(new RTImage("circle-blue-yellow-small.png")), Gtk::manage(new RTImage("circle-gray-green-small.png"))))), + colorscope_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_COLORSCOPE"), 0., 100.0, 1., 30.))), + scopemask_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SCOPEMASK"), 0, 100, 1, 60))), + lumask_(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LUMASK"), 0, 30, 1, 10))), + + avoid_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_AVOID")))), + blwh_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_BLWH")))), + recurs_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_RECURS")))), + laplac_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_LAPLACC")))), + deltae_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_DELTAEC")))), + shortc_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_SHORTC")))), + savrest_(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_SAVREST")))), + + expTransGrad_(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_TRANSIT")))), + expShapeDetect_(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_ARTIF")))), + expSpecCases_(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SPECCASE")))), + expMaskMerge_(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_MASFRAME")))), + + preview_(Gtk::manage(new Gtk::ToggleButton(M("TP_LOCALLAB_PREVIEW")))), + + controlPanelListener(nullptr), + lastObject_(-1), + nbSpotChanged_(false), + selSpotChanged_(false), + nameChanged_(false), + visibilityChanged_(false), + eventType(None), + excluFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_EXCLUF")))), + maskPrevActive(false) +{ + const bool showtooltip = options.showtooltip; + + Gtk::HBox* const hbox1_ = Gtk::manage(new Gtk::HBox(true, 4)); + buttonaddconn_ = button_add_->signal_clicked().connect( + sigc::mem_fun(*this, &ControlSpotPanel::on_button_add)); + buttondeleteconn_ = button_delete_->signal_clicked().connect( + sigc::mem_fun(*this, &ControlSpotPanel::on_button_delete)); + buttonduplicateconn_ = button_duplicate_->signal_clicked().connect( + sigc::mem_fun(*this, &ControlSpotPanel::on_button_duplicate)); + + hbox1_->pack_start(*button_add_); + hbox1_->pack_start(*button_delete_); + hbox1_->pack_start(*button_duplicate_); + pack_start(*hbox1_); + + Gtk::HBox* const hbox2_ = Gtk::manage(new Gtk::HBox(true, 4)); + buttonrenameconn_ = button_rename_->signal_clicked().connect( + sigc::mem_fun(*this, &ControlSpotPanel::on_button_rename)); + buttonvisibilityconn_ = button_visibility_->signal_button_release_event().connect( + sigc::mem_fun(*this, &ControlSpotPanel::on_button_visibility)); + + + if (showtooltip) { + button_visibility_->set_tooltip_markup(M("TP_LOCALLAB_VIS_TOOLTIP")); + } + + hbox2_->pack_start(*button_rename_); + hbox2_->pack_start(*button_visibility_); + pack_start(*hbox2_); + + treemodel_ = Gtk::ListStore::create(spots_); + treeview_->set_model(treemodel_); + treeviewconn_ = treeview_->get_selection()->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::controlspotChanged)); + treeview_->set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_VERTICAL); + + // Disable search to prevent hijacking keyboard shortcuts #5265 + treeview_->set_enable_search(false); + treeview_->signal_key_press_event().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::blockTreeviewSearch), false); + + // Avoid situation where no spot is selected (Ctrl+click on treeview) + treeview_->signal_button_press_event().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::onSpotSelectionEvent), false); + + auto cell = Gtk::manage(new Gtk::CellRendererText()); + int cols_count = treeview_->append_column(M("TP_LOCALLAB_COL_NAME"), *cell); + auto col = treeview_->get_column(cols_count - 1); + + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &ControlSpotPanel::render_name)); + } + + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_->append_column(M("TP_LOCALLAB_COL_VIS"), *cell); + col = treeview_->get_column(cols_count - 1); + + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &ControlSpotPanel::render_isvisible)); + } + + scrolledwindow_->add(*treeview_); + scrolledwindow_->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + scrolledwindow_->set_min_content_height(150); + pack_start(*scrolledwindow_); + + Gtk::HBox* const ctboxshape = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelshape = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_SHAPETYPE") + ":")); + ctboxshape->pack_start(*labelshape, Gtk::PACK_SHRINK, 4); + shape_->append(M("TP_LOCALLAB_ELI")); + shape_->append(M("TP_LOCALLAB_RECT")); + shape_->set_active(0); + shapeconn_ = shape_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::shapeChanged)); + ctboxshape->pack_start(*shape_); + pack_start(*ctboxshape); + if (showtooltip) { + shape_->set_tooltip_text(M("TP_LOCALLAB_SHAPE_TOOLTIP")); + } + + Gtk::HBox* const ctboxspotmethod = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelspotmethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_EXCLUTYPE") + ":")); + ctboxspotmethod->pack_start(*labelspotmethod, Gtk::PACK_SHRINK, 4); + + if (showtooltip) { + ctboxspotmethod->set_tooltip_markup(M("TP_LOCALLAB_EXCLUTYPE_TOOLTIP")); + } + + spotMethod_->append(M("TP_LOCALLAB_EXNORM")); + spotMethod_->append(M("TP_LOCALLAB_EXECLU")); + spotMethod_->set_active(0); + spotMethodconn_ = spotMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::spotMethodChanged)); + ctboxspotmethod->pack_start(*spotMethod_); + pack_start(*ctboxspotmethod); + + excluFrame->set_label_align(0.025, 0.5); + + if (showtooltip) { + excluFrame->set_tooltip_text(M("TP_LOCALLAB_EXCLUF_TOOLTIP")); + } + + ToolParamBlock* const excluBox = Gtk::manage(new ToolParamBlock()); + + if (showtooltip) { + sensiexclu_->set_tooltip_text(M("TP_LOCALLAB_SENSIEXCLU_TOOLTIP")); + } + + sensiexclu_->setAdjusterListener(this); + structexclu_->setAdjusterListener(this); + structexclu_->setLogScale(10, 0); + + excluBox->pack_start(*sensiexclu_); + excluBox->pack_start(*structexclu_); + excluFrame->add(*excluBox); + pack_start(*excluFrame); + + Gtk::HBox* const ctboxshapemethod = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelshapemethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_STYPE") + ":")); + ctboxshapemethod->pack_start(*labelshapemethod, Gtk::PACK_SHRINK, 4); + + if (showtooltip) { + ctboxshapemethod->set_tooltip_markup(M("TP_LOCALLAB_STYPE_TOOLTIP")); + } + + shapeMethod_->append(M("TP_LOCALLAB_IND")); + shapeMethod_->append(M("TP_LOCALLAB_SYM")); + shapeMethod_->append(M("TP_LOCALLAB_INDSL")); + shapeMethod_->append(M("TP_LOCALLAB_SYMSL")); + shapeMethod_->set_active(0); + shapeMethodconn_ = shapeMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::shapeMethodChanged)); + ctboxshapemethod->pack_start(*shapeMethod_); +// pack_start(*ctboxshapemethod); + + pack_start(*locX_); + locX_->setAdjusterListener(this); + + pack_start(*locXL_); + locXL_->setAdjusterListener(this); + + pack_start(*locY_); + locY_->setAdjusterListener(this); + + pack_start(*locYT_); + locYT_->setAdjusterListener(this); + + pack_start(*centerX_); + centerX_->setAdjusterListener(this); + + pack_start(*centerY_); + centerY_->setAdjusterListener(this); + + pack_start(*circrad_); + circrad_->setAdjusterListener(this); + + if (showtooltip) { + circrad_->set_tooltip_text(M("TP_LOCALLAB_CIRCRAD_TOOLTIP")); + } + + Gtk::HBox* const ctboxqualitymethod = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelqualitymethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_QUAL_METHOD") + ":")); + ctboxqualitymethod->pack_start(*labelqualitymethod, Gtk::PACK_SHRINK, 4); + + if (showtooltip) { + ctboxqualitymethod->set_tooltip_markup(M("TP_LOCALLAB_METHOD_TOOLTIP")); + } + + qualityMethod_->append(M("TP_LOCALLAB_ENH")); + qualityMethod_->append(M("TP_LOCALLAB_ENHDEN")); + qualityMethod_->set_active(1); + qualityMethodconn_ = qualityMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::qualityMethodChanged)); + ctboxqualitymethod->pack_start(*qualityMethod_); +// pack_start(*ctboxqualitymethod); + + if (showtooltip) { + expTransGrad_->set_tooltip_text(M("TP_LOCALLAB_TRANSIT_TOOLTIP")); + } + + ToolParamBlock* const transitBox = Gtk::manage(new ToolParamBlock()); + + if (showtooltip) { + transit_->set_tooltip_text(M("TP_LOCALLAB_TRANSIT_TOOLTIP")); + } + + if (showtooltip) { + transitweak_->set_tooltip_text(M("TP_LOCALLAB_TRANSITWEAK_TOOLTIP")); + } + + if (showtooltip) { + feather_->set_tooltip_text(M("TP_LOCALLAB_FEATH_TOOLTIP")); + } + + if (showtooltip) { + transitgrad_->set_tooltip_text(M("TP_LOCALLAB_TRANSITGRAD_TOOLTIP")); + } + + if (showtooltip) { + scopemask_->set_tooltip_text(M("TP_LOCALLAB_SCOPEMASK_TOOLTIP")); + } + + transit_->setAdjusterListener(this); + transitweak_->setAdjusterListener(this); + transitgrad_->setAdjusterListener(this); + feather_->setAdjusterListener(this); + scopemask_->setAdjusterListener(this); + transitBox->pack_start(*transit_); + transitBox->pack_start(*transitweak_); + transitBox->pack_start(*transitgrad_); + transitBox->pack_start(*feather_); + expTransGrad_->add(*transitBox, false); + pack_start(*expTransGrad_, false, false); + + if (showtooltip) { + expShapeDetect_->set_tooltip_text(M("TP_LOCALLAB_ARTIF_TOOLTIP")); + } + + ToolParamBlock* const artifBox = Gtk::manage(new ToolParamBlock()); + struc_->setAdjusterListener(this); + thresh_->setAdjusterListener(this); + iter_->setAdjusterListener(this); + balan_->setAdjusterListener(this); + balanh_->setAdjusterListener(this); + colorde_->setAdjusterListener(this); + colorscope_->setAdjusterListener(this); + + preview_->set_active(false); + previewConn_ = preview_->signal_clicked().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::previewChanged)); + + if (showtooltip) { + balan_->set_tooltip_text(M("TP_LOCALLAB_BALAN_TOOLTIP")); + balanh_->set_tooltip_text(M("TP_LOCALLAB_BALAN_TOOLTIP")); + colorde_->set_tooltip_text(M("TP_LOCALLAB_COLORDE_TOOLTIP")); + colorscope_->set_tooltip_text(M("TP_LOCALLAB_COLORSCOPE_TOOLTIP")); + preview_->set_tooltip_text(M("TP_LOCALLAB_COLORDEPREV_TOOLTIP")); + } + +// artifBox->pack_start(*struc_); + artifBox->pack_start(*thresh_); + artifBox->pack_start(*iter_); + artifBox->pack_start(*balan_); + artifBox->pack_start(*balanh_); + artifBox->pack_start(*colorde_); + artifBox->pack_start(*preview_); + artifBox->pack_start(*colorscope_); + expShapeDetect_->add(*artifBox, false); + pack_start(*expShapeDetect_, false, false); + + ToolParamBlock* const specCaseBox = Gtk::manage(new ToolParamBlock()); + + avoidConn_ = avoid_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::avoidChanged)); + specCaseBox->pack_start(*avoid_); + + blwhConn_ = blwh_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::blwhChanged)); + + if (showtooltip) { + blwh_->set_tooltip_text(M("TP_LOCALLAB_BLWH_TOOLTIP")); + } + + specCaseBox->pack_start(*blwh_); + + recursConn_ = recurs_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::recursChanged)); + + if (showtooltip) { + recurs_->set_tooltip_text(M("TP_LOCALLAB_RECURS_TOOLTIP")); + avoid_->set_tooltip_text(M("TP_LABCURVE_AVOIDCOLORSHIFT_TOOLTIP")); + } + + specCaseBox->pack_start(*recurs_); + expSpecCases_->add(*specCaseBox, false); + pack_start(*expSpecCases_, false, false); + + if (showtooltip) { + expMaskMerge_->set_tooltip_text(M("TP_LOCALLAB_MASFRAME_TOOLTIP")); + } + + ToolParamBlock* const maskBox = Gtk::manage(new ToolParamBlock()); + laplacConn_ = laplac_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::laplacChanged)); + deltaeConn_ = deltae_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::deltaeChanged)); + shortcConn_ = shortc_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::shortcChanged)); + + if (showtooltip) { + shortc_->set_tooltip_text(M("TP_LOCALLAB_SHORTCMASK_TOOLTIP")); + } + + lumask_->setAdjusterListener(this); + savrestConn_ = savrest_->signal_toggled().connect( + sigc::mem_fun(*this, &ControlSpotPanel::savrestChanged)); + + if (showtooltip) { + savrest_->set_tooltip_text(M("TP_LOCALLAB_SAVREST_TOOLTIP")); + lumask_->set_tooltip_text(M("TP_LOCALLAB_LUMASK_TOOLTIP")); + laplac_->set_tooltip_text(M("TP_LOCALLAB_LAP_MASK_TOOLTIP")); + } + + maskBox->pack_start(*laplac_); + maskBox->pack_start(*deltae_); + maskBox->pack_start(*scopemask_); + // maskBox->pack_start(*shortc_); + maskBox->pack_start(*lumask_); + // maskBox->pack_start(*savrest_); + expMaskMerge_->add(*maskBox, false); + pack_start(*expMaskMerge_, false, false); + + Gtk::HSeparator *separatormet = Gtk::manage(new Gtk::HSeparator()); + pack_start(*separatormet, Gtk::PACK_SHRINK, 2); + + Gtk::HBox* const ctboxcomplexmethod = Gtk::manage(new Gtk::HBox()); + + if (showtooltip) { + ctboxcomplexmethod->set_tooltip_markup(M("TP_LOCALLAB_COMPLEXMETHOD_TOOLTIP")); + } + + Gtk::Label* const labelcomplexmethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_COMPLEX_METHOD") + ":")); + ctboxcomplexmethod->pack_start(*labelcomplexmethod, Gtk::PACK_SHRINK, 4); + + if (showtooltip) { + complexMethod_->set_tooltip_markup(M("TP_LOCALLAB_COMPLEX_TOOLTIP")); + } + + complexMethod_->append(M("TP_LOCALLAB_SIM")); + complexMethod_->append(M("TP_LOCALLAB_MED")); + complexMethod_->append(M("TP_LOCALLAB_ALL")); + complexMethod_->set_active(1); + complexMethodconn_ = complexMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::complexMethodChanged)); + ctboxcomplexmethod->pack_start(*complexMethod_); + // pack_start(*ctboxcomplexmethod); + + Gtk::HBox* const ctboxwavmethod = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelwavmethod = Gtk::manage(new Gtk::Label(M("TP_WAVELET_DAUBLOCAL") + ":")); + ctboxwavmethod->pack_start(*labelwavmethod, Gtk::PACK_SHRINK, 4); + + if (showtooltip) { + ctboxwavmethod->set_tooltip_markup(M("TP_WAVELET_DAUB_TOOLTIP")); + } + + wavMethod_->append(M("TP_WAVELET_DAUB2")); + wavMethod_->append(M("TP_WAVELET_DAUB4")); + wavMethod_->append(M("TP_WAVELET_DAUB6")); + wavMethod_->append(M("TP_WAVELET_DAUB10")); + wavMethod_->append(M("TP_WAVELET_DAUB14")); + wavMethod_->set_active(1); + wavMethodconn_ = wavMethod_->signal_changed().connect( + sigc::mem_fun( + *this, &ControlSpotPanel::wavMethodChanged)); + ctboxwavmethod->pack_start(*wavMethod_); + pack_start(*ctboxwavmethod); + + show_all(); + + // Define row background color + // Mouseovered spot (opaque orange) + colorMouseover.set_red(1.); + colorMouseover.set_green(100. / 255.); + colorMouseover.set_blue(0.); + colorMouseover.set_alpha(1.); + + colorMouseovertext.set_red(0.6); + colorMouseovertext.set_green(100. / 255.); + colorMouseovertext.set_blue(0.); + colorMouseovertext.set_alpha(0.5); + + // Nominal spot (transparent black) + colorNominal.set_red(0.); + colorNominal.set_green(0.); + colorNominal.set_blue(0.); + colorNominal.set_alpha(0.); +} + +ControlSpotPanel::~ControlSpotPanel() +{ + // visibleGeometry + for (auto i = EditSubscriber::visibleGeometry.begin(); i != EditSubscriber::visibleGeometry.end(); ++i) { + delete *i; + } + + // mouseOverGeometry + for (auto i = EditSubscriber::mouseOverGeometry.begin(); i != EditSubscriber::mouseOverGeometry.end(); ++i) { + delete *i; + } +} + +void ControlSpotPanel::setEditProvider(EditDataProvider* provider) +{ + EditSubscriber::setEditProvider(provider); +} + +void ControlSpotPanel::render_name( + Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + auto row = *iter; + Gtk::CellRendererText *ct = static_cast(cell); + + // Render cell text + ct->property_text() = row[spots_.name]; + + // Render cell background color + if (row[spots_.mouseover]) { + ct->property_background_rgba() = colorMouseovertext; + } else { + ct->property_background_rgba() = colorNominal; + } +} + +void ControlSpotPanel::render_isvisible( + Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + auto row = *iter; + Gtk::CellRendererText *ct = static_cast(cell); + + // Render cell text + if (row[spots_.isvisible]) { + ct->property_text() = M("TP_LOCALLAB_ROW_VIS"); + } else { + ct->property_text() = M("TP_LOCALLAB_ROW_NVIS"); + } + + // Render cell background color + if (row[spots_.mouseover]) { + ct->property_background_rgba() = colorMouseovertext; + } else { + ct->property_background_rgba() = colorNominal; + } +} + +void ControlSpotPanel::on_button_add() +{ + // printf("on_button_add\n"); + + if (!listener) { + return; + } + + // Raise event + nbSpotChanged_ = true; + selSpotChanged_ = true; + eventType = SpotCreation; + listener->panelChanged(EvLocallabSpotCreated, "-"); +} + +void ControlSpotPanel::on_button_delete() +{ + // printf("on_button_delete\n"); + + if (!listener) { + return; + } + + // Raise event + const int selIndex = getSelectedSpot(); + + if (selIndex == -1) { // No selected spot to remove + return; + } + + nbSpotChanged_ = true; + selSpotChanged_ = true; + eventType = SpotDeletion; + SpotRow* const delSpotRow = getSpot(selIndex); + listener->panelChanged(EvLocallabSpotDeleted, delSpotRow->name); +} + +void ControlSpotPanel::on_button_duplicate() +{ + // printf("on_button_duplicate\n"); + + if (!listener) { + return; + } + + // Raise event + const int selIndex = getSelectedSpot(); + + if (selIndex == -1) { // No selected spot to duplicate + return; + } + + nbSpotChanged_ = true; + selSpotChanged_ = true; + eventType = SpotDuplication; + SpotRow* const duplSpotRow = getSpot(selIndex); + listener->panelChanged(EvLocallabSpotCreated, M("TP_LOCALLAB_EV_DUPL") + " " + + duplSpotRow->name); +} + +void ControlSpotPanel::on_button_rename() +{ + // printf("on_button_rename\n"); + + if (!listener) { + return; + } + + // Get actual control spot name + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + const Gtk::TreeModel::Row row = *iter; + const Glib::ustring actualname = row[spots_.name]; + + // Launch windows to update spot name + RenameDialog d(actualname, + static_cast(*get_toplevel())); + int status = d.run(); + + // Update actual name and raise event + if (status == RenameDialog::OkButton) { + const Glib::ustring newname = d.get_new_name(); + + if (newname != actualname) { // Event is only raised if name is updated + nameChanged_ = true; + row[spots_.name] = newname; + treeview_->columns_autosize(); + listener->panelChanged(EvLocallabSpotName, newname); + } + } +} + +bool ControlSpotPanel::on_button_visibility(GdkEventButton* event) +{ + // printf("on_button_visibility\n"); + + if (!listener) { + return true; + } + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return true; + } + + const auto iter = s->get_selected(); + const Gtk::TreeModel::Row row = *iter; + + const int ctrl = event->state & GDK_CONTROL_MASK; + + if (event->button == 1) { // Left click on button + if (ctrl) { // Ctrl+click case: all spots are shown/hidden + // Get visibility of selected spot + const bool selVisibility = row[spots_.isvisible]; + + // Update visibility of all spot + const Gtk::TreeModel::Children children = treemodel_->children(); + + for (auto i = children.begin(); i != children.end(); i++) { + Gtk::TreeModel::Row r = *i; + r[spots_.isvisible] = !selVisibility; + updateControlSpotCurve(r); + } + + // Raise event + visibilityChanged_ = true; + eventType = SpotAllVisibilityChanged; + + if (!selVisibility) { + listener->panelChanged(EvLocallabSpotVisibility, M("TP_LOCALLAB_EV_VIS_ALL")); + } else { + listener->panelChanged(EvLocallabSpotVisibility, M("TP_LOCALLAB_EV_NVIS_ALL")); + } + + return true; + } else { // Click case: only selected spot is shown/hidden + // Update visibility for selected spot only + row[spots_.isvisible] = !row[spots_.isvisible]; + updateControlSpotCurve(row); + + // Raise event + visibilityChanged_ = true; + SpotRow* const spotRow = getSpot(getSelectedSpot()); + + if (row[spots_.isvisible]) { + listener->panelChanged(EvLocallabSpotVisibility, M("TP_LOCALLAB_EV_VIS") + " (" + spotRow->name + ")"); + } else { + listener->panelChanged(EvLocallabSpotVisibility, M("TP_LOCALLAB_EV_NVIS") + " (" + spotRow->name + ")"); + } + + return true; + } + } + + return false; +} + +bool ControlSpotPanel::blockTreeviewSearch(GdkEventKey* event) +{ + // printf("blockTreeviewSearch\n"); + + if (event->state & Gdk::CONTROL_MASK) { // Ctrl + if (event->keyval == GDK_KEY_f || event->keyval == GDK_KEY_F) { + // No action is performed to avoid activating treeview search + return true; + } + } + + // Otherwise key action is transferred to treeview widget + return false; +} + +bool ControlSpotPanel::onSpotSelectionEvent(GdkEventButton* event) +{ + if (event->state & Gdk::CONTROL_MASK) { // Ctrl + // No action is performed to avoid a situation where no spot is selected + return true; + } + + // Otherwise selection action is transferred to treeview widget + return false; +} + +void ControlSpotPanel::load_ControlSpot_param() +{ + // printf("load_ControlSpot_param\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + const Gtk::TreeModel::Row row = *iter; + + // Load param in selected control spot + shape_->set_active(row[spots_.shape]); + spotMethod_->set_active(row[spots_.spotMethod]); + sensiexclu_->setValue((double)row[spots_.sensiexclu]); + structexclu_->setValue((double)row[spots_.structexclu]); + shapeMethod_->set_active(row[spots_.shapeMethod]); + locX_->setValue((double)row[spots_.locX]); + locXL_->setValue((double)row[spots_.locXL]); + locY_->setValue((double)row[spots_.locY]); + locYT_->setValue((double)row[spots_.locYT]); + centerX_->setValue((double)row[spots_.centerX]); + centerY_->setValue((double)row[spots_.centerY]); + circrad_->setValue((double)row[spots_.circrad]); + qualityMethod_->set_active(row[spots_.qualityMethod]); + transit_->setValue((double)row[spots_.transit]); + transitweak_->setValue((double)row[spots_.transitweak]); + transitgrad_->setValue((double)row[spots_.transitgrad]); + feather_->setValue((double)row[spots_.feather]); + struc_->setValue((double)row[spots_.struc]); + thresh_->setValue((double)row[spots_.thresh]); + iter_->setValue((double)row[spots_.iter]); + balan_->setValue((double)row[spots_.balan]); + balanh_->setValue((double)row[spots_.balanh]); + colorde_->setValue((double)row[spots_.colorde]); + colorscope_->setValue((double)row[spots_.colorscope]); + avoid_->set_active(row[spots_.avoid]); + blwh_->set_active(row[spots_.blwh]); + recurs_->set_active(row[spots_.recurs]); + laplac_->set_active(row[spots_.laplac]); + deltae_->set_active(row[spots_.deltae]); + scopemask_->setValue((double)row[spots_.scopemask]); + shortc_->set_active(row[spots_.shortc]); + lumask_->setValue((double)row[spots_.lumask]); + savrest_->set_active(row[spots_.savrest]); + complexMethod_->set_active(row[spots_.complexMethod]); + wavMethod_->set_active(row[spots_.wavMethod]); +} + +void ControlSpotPanel::controlspotChanged() +{ + // printf("controlspotChanged\n"); + + if (!listener) { + return; + } + + // Raise event + const int selIndex = getSelectedSpot(); + + if (selIndex == -1) { // No selected spot + return; + } + + selSpotChanged_ = true; + eventType = SpotSelection; + SpotRow* const spotRow = getSpot(selIndex); + + // Image area shall be regenerated if mask or deltaE preview was active when switching spot + if (maskPrevActive || preview_->get_active()) { + listener->panelChanged(EvLocallabSpotSelectedWithMask, spotRow->name); + } else { + listener->panelChanged(EvLocallabSpotSelected, spotRow->name); + } +} + +void ControlSpotPanel::shapeChanged() +{ + // printf("shapeChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + row[spots_.shape] = shape_->get_active_row_number(); + updateControlSpotCurve(row); + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotShape, shape_->get_active_text()); + } +} + +void ControlSpotPanel::spotMethodChanged() +{ + // printf("spotMethodChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + row[spots_.spotMethod] = spotMethod_->get_active_row_number(); + + // Update Control Spot GUI according to spotMethod_ combobox state (to be compliant with updateParamVisibility function) + if (multiImage && spotMethod_->get_active_text() == M("GENERAL_UNCHANGED")) { + excluFrame->show(); + } else if (spotMethod_->get_active_row_number() == 0) { // Normal case + excluFrame->hide(); + } else { // Excluding case + excluFrame->show(); + } + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotSpotMethod, spotMethod_->get_active_text()); + } +} + +void ControlSpotPanel::shapeMethodChanged() +{ + // printf("shapeMethodChanged\n"); + + const int method = shapeMethod_->get_active_row_number(); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + if (!batchMode && (method == 1 || method == 3)) { // Symmetrical cases + disableParamlistener(true); + locXL_->setValue(locX_->getValue()); + locYT_->setValue(locY_->getValue()); + disableParamlistener(false); + + row[spots_.shapeMethod] = shapeMethod_->get_active_row_number(); + row[spots_.locXL] = locX_->getIntValue(); + row[spots_.locYT] = locY_->getIntValue(); + + updateControlSpotCurve(row); + } else { // In batch mode, sliders are always independent + row[spots_.shapeMethod] = shapeMethod_->get_active_row_number(); + } + + // Update Control Spot GUI according to shapeMethod_ combobox state (to be compliant with updateParamVisibility function) + if (!batchMode) { + if (method == 1 || method == 3) { // Symmetrical cases + locXL_->hide(); + locYT_->hide(); + + if (method == 1) { // 1 = Symmetrical (mouse) + locX_->hide(); + locY_->hide(); + centerX_->hide(); + centerY_->hide(); + } else { // 3 = Symmetrical (mouse + sliders) + locX_->show(); + locY_->show(); + centerX_->show(); + centerY_->show(); + } + } else { // Independent cases + if (method == 0) { // 0 = Independent (mouse) + locX_->hide(); + locXL_->hide(); + locY_->hide(); + locYT_->hide(); + centerX_->hide(); + centerY_->hide(); + } else { // 2 = Independent (mouse + sliders) + locX_->show(); + locXL_->show(); + locY_->show(); + locYT_->show(); + centerX_->show(); + centerY_->show(); + } + } + } else { // In batch mode, sliders are necessary shown + locX_->show(); + locXL_->show(); + locY_->show(); + locYT_->show(); + centerX_->show(); + centerY_->show(); + } + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotShapeMethod, shapeMethod_->get_active_text()); + } +} + +void ControlSpotPanel::qualityMethodChanged() +{ + // printf("qualityMethodChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + row[spots_.qualityMethod] = qualityMethod_->get_active_row_number(); + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotQualityMethod, qualityMethod_->get_active_text()); + } +} + +void ControlSpotPanel::complexMethodChanged() +{ + // printf("qualityMethodChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + row[spots_.complexMethod] = complexMethod_->get_active_row_number(); + + if (multiImage && complexMethod_->get_active_text() == M("GENERAL_UNCHANGED")) { + // excluFrame->show(); + } else if (complexMethod_->get_active_row_number() == 0) { //sim + // excluFrame->hide(); + } else if (complexMethod_->get_active_row_number() == 1) { // mod + // excluFrame->show(); + } else if (complexMethod_->get_active_row_number() == 2) { // all + // excluFrame->show(); + } + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotcomplexMethod, complexMethod_->get_active_text()); + } +} + +void ControlSpotPanel::wavMethodChanged() +{ + // printf("qualityMethodChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + row[spots_.wavMethod] = wavMethod_->get_active_row_number(); + + // Raise event + if (listener) { + listener->panelChanged(EvLocallabSpotwavMethod, wavMethod_->get_active_text()); + } +} + +void ControlSpotPanel::updateParamVisibility() +{ + // printf("updateParamVisibility\n"); + + // Update Control Spot GUI according to shapeMethod_ combobox state (to be compliant with shapeMethodChanged function) + const int method = shapeMethod_->get_active_row_number(); + + if (!batchMode) { + if (method == 1 || method == 3) { // Symmetrical cases + locXL_->hide(); + locYT_->hide(); + + if (method == 1) { // 1 = Symmetrical (mouse) + locX_->hide(); + locY_->hide(); + centerX_->hide(); + centerY_->hide(); + } else { // 3 = Symmetrical (mouse + sliders) + locX_->show(); + locY_->show(); + centerX_->show(); + centerY_->show(); + } + } else { // Independent cases + if (method == 0) { // 0 = Independent (mouse) + locX_->hide(); + locXL_->hide(); + locY_->hide(); + locYT_->hide(); + centerX_->hide(); + centerY_->hide(); + } else { // 2 = Independent (mouse + sliders) + locX_->show(); + locXL_->show(); + locY_->show(); + locYT_->show(); + centerX_->show(); + centerY_->show(); + } + } + } else { // In batch mode, sliders are necessary shown + locX_->show(); + locXL_->show(); + locY_->show(); + locYT_->show(); + centerX_->show(); + centerY_->show(); + } + + // Update Control Spot GUI according to spotMethod_ combobox state (to be compliant with spotMethodChanged function) + if (multiImage && spotMethod_->get_active_text() == M("GENERAL_UNCHANGED")) { + excluFrame->show(); + } else if (spotMethod_->get_active_row_number() == 0) { // Normal case + excluFrame->hide(); + } else { // Excluding case + excluFrame->show(); + } +} + +void ControlSpotPanel::adjusterChanged(Adjuster* a, double newval) +{ + // printf("adjusterChanged\n"); + + const int method = shapeMethod_->get_active_row_number(); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + if (a == sensiexclu_) { + row[spots_.sensiexclu] = sensiexclu_->getIntValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotSensiexclu, sensiexclu_->getTextValue()); + } + } + + if (a == structexclu_) { + row[spots_.structexclu] = structexclu_->getIntValue(); + + if (listener) { + listener->panelChanged(Evlocallabstructexlu, structexclu_->getTextValue()); + } + } + + if (a == locX_) { + row[spots_.locX] = locX_->getIntValue(); + + if (!batchMode && (method == 1 || method == 3)) { // Symmetrical cases (in batch mode, sliders are always independent) + disableParamlistener(true); + locXL_->setValue(locX_->getValue()); + disableParamlistener(false); + row[spots_.locXL] = locXL_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocX, locX_->getTextValue()); + } + } + + if (a == locXL_) { + row[spots_.locXL] = locXL_->getIntValue(); + + if (!batchMode && (method == 1 || method == 3)) { // Symmetrical cases (in batch mode, sliders are always independent) + disableParamlistener(true); + locX_->setValue(locXL_->getValue()); + disableParamlistener(false); + row[spots_.locX] = locX_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocXL, locXL_->getTextValue()); + } + } + + if (a == locY_) { + row[spots_.locY] = locY_->getIntValue(); + + if (!batchMode && (method == 1 || method == 3)) { // Symmetrical cases (in batch mode, sliders are always independent) + disableParamlistener(true); + locYT_->setValue(locY_->getValue()); + disableParamlistener(false); + row[spots_.locYT] = locYT_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocY, locY_->getTextValue()); + } + } + + if (a == locYT_) { + row[spots_.locYT] = locYT_->getIntValue(); + + if (!batchMode && (method == 1 || method == 3)) { // Symmetrical cases (in batch mode, sliders are always independent) + disableParamlistener(true); + locY_->setValue(locYT_->getValue()); + disableParamlistener(false); + row[spots_.locY] = locY_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocYT, locYT_->getTextValue()); + } + } + + if (a == centerX_ || a == centerY_) { + row[spots_.centerX] = centerX_->getIntValue(); + row[spots_.centerY] = centerY_->getIntValue(); + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotCenter, "X=" + centerX_->getTextValue() + ", Y=" + centerY_->getTextValue()); + } + } + + if (a == circrad_) { + row[spots_.circrad] = circrad_->getIntValue(); + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotCircrad, circrad_->getTextValue()); + } + } + + if (a == transit_) { + row[spots_.transit] = transit_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotTransit, transit_->getTextValue()); + } + } + + if (a == transitweak_) { + row[spots_.transitweak] = transitweak_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotTransitweak, transitweak_->getTextValue()); + } + } + + if (a == transitgrad_) { + row[spots_.transitgrad] = transitgrad_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotTransitgrad, transitgrad_->getTextValue()); + } + } + + if (a == feather_) { + row[spots_.feather] = feather_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotfeather, feather_->getTextValue()); + } + } + + if (a == struc_) { + row[spots_.struc] = struc_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotStruc, struc_->getTextValue()); + } + } + + if (a == thresh_) { + row[spots_.thresh] = thresh_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotThresh, thresh_->getTextValue()); + } + } + + if (a == iter_) { + row[spots_.iter] = iter_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotIter, iter_->getTextValue()); + } + } + + if (a == balan_) { + row[spots_.balan] = balan_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotbalan, balan_->getTextValue()); + } + } + + if (a == balanh_) { + row[spots_.balanh] = balanh_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotbalanh, balanh_->getTextValue()); + } + } + + if (a == colorde_) { + row[spots_.colorde] = colorde_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotcolorde, colorde_->getTextValue()); + } + } + + if (a == colorscope_) { + row[spots_.colorscope] = colorscope_->getValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotcolorscope, colorscope_->getTextValue()); + } + } + + if (a == scopemask_) { + row[spots_.scopemask] = scopemask_->getIntValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotscopemask, scopemask_->getTextValue()); + } + } + + if (a == lumask_) { + row[spots_.lumask] = lumask_->getIntValue(); + + if (listener) { + listener->panelChanged(EvLocallabSpotlumask, lumask_->getTextValue()); + } + } +} + +void ControlSpotPanel::avoidChanged() +{ + // printf("avoidChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.avoid] = avoid_->get_active(); + + // Raise event + if (listener) { + if (avoid_->get_active()) { + listener->panelChanged(Evlocallabavoid, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabavoid, M("GENERAL_DISABLED")); + } + } +} + +void ControlSpotPanel::blwhChanged() +{ + // printf("blwhChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.blwh] = blwh_->get_active(); + + // Raise event + if (listener) { + if (blwh_->get_active()) { + listener->panelChanged(Evlocallabblwh, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabblwh, M("GENERAL_DISABLED")); + } + } +} + +void ControlSpotPanel::recursChanged() +{ + // printf("recursChanged\n"); + + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.recurs] = recurs_->get_active(); + + // Raise event + if (listener) { + if (recurs_->get_active()) { + listener->panelChanged(Evlocallabrecurs, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabrecurs, M("GENERAL_DISABLED")); + } + } +} + + +void ControlSpotPanel::laplacChanged() +{ + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.laplac] = laplac_->get_active(); + + // Raise event + if (listener) { + if (laplac_->get_active()) { + listener->panelChanged(Evlocallablaplac, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallablaplac, M("GENERAL_DISABLED")); + } + } +} + + +void ControlSpotPanel::deltaeChanged() +{ + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.deltae] = deltae_->get_active(); + + // Raise event + if (listener) { + if (deltae_->get_active()) { + listener->panelChanged(Evlocallabdeltae, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabdeltae, M("GENERAL_DISABLED")); + } + } +} + +void ControlSpotPanel::shortcChanged() +{ + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.shortc] = shortc_->get_active(); + + // Raise event + if (listener) { + if (shortc_->get_active()) { + listener->panelChanged(Evlocallabshortc, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabshortc, M("GENERAL_DISABLED")); + } + } +} + +void ControlSpotPanel::savrestChanged() +{ + // Get selected control spot + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + row[spots_.savrest] = savrest_->get_active(); + + // Raise event + if (listener) { + if (savrest_->get_active()) { + listener->panelChanged(Evlocallabsavrest, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(Evlocallabsavrest, M("GENERAL_DISABLED")); + } + } +} + +void ControlSpotPanel::previewChanged() +{ + // If deltaE preview is activated, deactivate all other tool mask preview + if (controlPanelListener) { + controlPanelListener->resetToolMaskView(); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void ControlSpotPanel::disableParamlistener(bool cond) +{ + // printf("disableParamlistener: %d\n", cond); + + treeviewconn_.block(cond); + buttonaddconn_.block(cond); + buttondeleteconn_.block(cond); + buttonduplicateconn_.block(cond); + buttonrenameconn_.block(cond); + buttonvisibilityconn_.block(cond); + shapeconn_.block(cond); + spotMethodconn_.block(cond); + sensiexclu_->block(cond); + structexclu_->block(cond); + shapeMethodconn_.block(cond); + locX_->block(cond); + locXL_->block(cond); + locY_->block(cond); + locYT_->block(cond); + centerX_->block(cond); + centerY_->block(cond); + circrad_->block(cond); + qualityMethodconn_.block(cond); + transit_->block(cond); + transitweak_->block(cond); + transitgrad_->block(cond); + feather_->block(cond); + struc_->block(cond); + thresh_->block(cond); + iter_->block(cond); + balan_->block(cond); + balanh_->block(cond); + colorde_->block(cond); + colorscope_->block(cond); + avoidConn_.block(cond); + blwhConn_.block(cond); + recursConn_.block(cond); + laplacConn_.block(cond); + deltaeConn_.block(cond); + scopemask_->block(cond); + shortcConn_.block(cond); + lumask_->block(cond); + savrestConn_.block(cond); + complexMethodconn_.block(cond); + wavMethodconn_.block(cond); +} + +void ControlSpotPanel::setParamEditable(bool cond) +{ + // printf("setParamEditable: %d\n", cond); + + shape_->set_sensitive(cond); + spotMethod_->set_sensitive(cond); + sensiexclu_->set_sensitive(cond); + structexclu_->set_sensitive(cond); + shapeMethod_->set_sensitive(cond); + locX_->set_sensitive(cond); + locXL_->set_sensitive(cond); + locY_->set_sensitive(cond); + locYT_->set_sensitive(cond); + centerX_->set_sensitive(cond); + centerY_->set_sensitive(cond); + circrad_->set_sensitive(cond); + qualityMethod_->set_sensitive(cond); + transit_->set_sensitive(cond); + transitweak_->set_sensitive(cond); + transitgrad_->set_sensitive(cond); + feather_->set_sensitive(cond); + struc_->set_sensitive(cond); + thresh_->set_sensitive(cond); + iter_->set_sensitive(cond); + balan_->set_sensitive(cond); + balanh_->set_sensitive(cond); + colorde_->set_sensitive(cond); + colorscope_->set_sensitive(cond); + avoid_->set_sensitive(cond); + blwh_->set_sensitive(cond); + recurs_->set_sensitive(cond); + laplac_->set_sensitive(cond); + deltae_->set_sensitive(cond); + scopemask_->set_sensitive(cond); + shortc_->set_sensitive(cond); + lumask_->set_sensitive(cond); + savrest_->set_sensitive(cond); + complexMethod_->set_sensitive(cond); + wavMethod_->set_sensitive(cond); +} + +void ControlSpotPanel::setDefaultExpanderVisibility() +{ + expTransGrad_->set_expanded(false); + expShapeDetect_->set_expanded(false); + expSpecCases_->set_expanded(false); + expMaskMerge_->set_expanded(false); +} + +void ControlSpotPanel::addControlSpotCurve(Gtk::TreeModel::Row& row) +{ + // printf("addControlSpotCurve\n"); + + if (row[spots_.curveid] > 0) { // Row has already an associated curve + return; + } + + // Creation of visibleGeometry + Circle* cirX; + cirX = new Circle(); + cirX->radius = 4.; + cirX->filled = true; + cirX->datum = Geometry::IMAGE; + Circle* cirXL; + cirXL = new Circle(); + cirXL->radius = 4.; + cirXL->filled = true; + cirXL->datum = Geometry::IMAGE; + Circle* cirY; + cirY = new Circle(); + cirY->radius = 4.; + cirY->filled = true; + cirY->datum = Geometry::IMAGE; + Circle* cirYT; + cirYT = new Circle(); + cirYT->radius = 4.; + cirYT->filled = true; + cirYT->datum = Geometry::IMAGE; + Circle* centerCircle; + centerCircle = new Circle(); + centerCircle->datum = Geometry::IMAGE; + centerCircle->radiusInImageSpace = true; + Ellipse* shape_ellipse; + shape_ellipse = new Ellipse(); + shape_ellipse->datum = Geometry::IMAGE; + shape_ellipse->radiusInImageSpace = true; + Rectangle* shape_rectangle; + shape_rectangle = new Rectangle(); + shape_rectangle->datum = Geometry::IMAGE; + EditSubscriber::visibleGeometry.push_back(centerCircle); // (curveid - 1) * 7 + EditSubscriber::visibleGeometry.push_back(shape_ellipse); // (curveid - 1) * 7 + 1 + EditSubscriber::visibleGeometry.push_back(shape_rectangle); // (curveid - 1) * 7 + 2 + EditSubscriber::visibleGeometry.push_back(cirX); // (curveid - 1) * 7 + 3 + EditSubscriber::visibleGeometry.push_back(cirXL); // (curveid - 1) * 7 + 4 + EditSubscriber::visibleGeometry.push_back(cirY); // (curveid - 1) * 7 + 5 + EditSubscriber::visibleGeometry.push_back(cirYT); // (curveid - 1) * 7 + 6 + + // Creation of mouseOverGeometry + cirX = new Circle(); + cirX->radius = 4.; + cirX->filled = true; + cirX->datum = Geometry::IMAGE; + cirXL = new Circle(); + cirXL->radius = 4.; + cirXL->filled = true; + cirXL->datum = Geometry::IMAGE; + cirY = new Circle(); + cirY->radius = 4.; + cirY->filled = true; + cirY->datum = Geometry::IMAGE; + cirYT = new Circle(); + cirYT->radius = 4.; + cirYT->filled = true; + cirYT->datum = Geometry::IMAGE; + centerCircle = new Circle(); + centerCircle->filled = true; + centerCircle->datum = Geometry::IMAGE; + centerCircle->radiusInImageSpace = true; + shape_ellipse = new Ellipse(); + shape_ellipse->datum = Geometry::IMAGE; + shape_ellipse->radiusInImageSpace = true; + shape_rectangle = new Rectangle(); + shape_rectangle->datum = Geometry::IMAGE; + EditSubscriber::mouseOverGeometry.push_back(centerCircle); // (curveid - 1) * 7 + EditSubscriber::mouseOverGeometry.push_back(shape_ellipse); // (curveid - 1) * 7 + 1 + EditSubscriber::mouseOverGeometry.push_back(shape_rectangle); // (curveid - 1) * 7 + 2 + EditSubscriber::mouseOverGeometry.push_back(cirX); // (curveid - 1) * 7 + 3 + EditSubscriber::mouseOverGeometry.push_back(cirXL); // (curveid - 1) * 7 + 4 + EditSubscriber::mouseOverGeometry.push_back(cirY); // (curveid - 1) * 7 + 5 + EditSubscriber::mouseOverGeometry.push_back(cirYT); // (curveid - 1) * 7 + 6 + + row[spots_.curveid] = EditSubscriber::visibleGeometry.size() / 7; +} + +void ControlSpotPanel::updateControlSpotCurve(const Gtk::TreeModel::Row& row) +{ + const int curveid_ = row[spots_.curveid]; + EditDataProvider* const dataProvider = getEditProvider(); + + // printf("updateControlSpotCurve: %d\n", curveid_); + + if (curveid_ == 0 || !dataProvider) { // Row has no associated curve or there is no EditProvider + return; + } + + int imW = 0; + int imH = 0; + dataProvider->getImageSize(imW, imH); + + if (!imW || !imH) { // No image loaded + return; + } + + const int centerX_ = row[spots_.centerX]; + const int centerY_ = row[spots_.centerY]; + const int circrad_ = row[spots_.circrad]; + const int locX_ = row[spots_.locX]; + const int locXL_ = row[spots_.locXL]; + const int locY_ = row[spots_.locY]; + const int locYT_ = row[spots_.locYT]; + const int shape_ = row[spots_.shape]; + const bool isvisible_ = row[spots_.isvisible]; + + const int decayX = (double)locX_ * (double)imW / 2000.; + const int decayXL = (double)locXL_ * (double)imW / 2000.; + const int decayY = (double)locY_ * (double)imH / 2000.; + const int decayYT = (double)locYT_ * (double)imH / 2000.; + const rtengine::Coord origin((double)imW / 2. + (double)centerX_ * (double)imW / 2000., (double)imH / 2. + (double)centerY_ * (double)imH / 2000.); + + const auto updateSelectionCircle = [&](Geometry * geometry, const int offsetX, const int offsetY) { + const auto cir = static_cast(geometry); + cir->center.x = origin.x + offsetX; + cir->center.y = origin.y + offsetY; + }; + + const auto updateCenterCircle = [&](Geometry * geometry) { + const auto circle = static_cast(geometry); + circle->center = origin; + circle->radius = circrad_; + }; + + const auto updateEllipse = [&](Geometry * geometry) { + const auto ellipse = static_cast(geometry); + ellipse->center = origin; + ellipse->radX = decayX; + ellipse->radXL = decayXL; + ellipse->radY = decayY; + ellipse->radYT = decayYT; + }; + + const auto updateRectangle = [&](Geometry * geometry) { + const auto rectangle = static_cast(geometry); + rectangle->bottomRight.x = origin.x + decayX; + rectangle->bottomRight.y = origin.y + decayY; + rectangle->topLeft.x = origin.x - decayXL; + rectangle->topLeft.y = origin.y - decayYT; + }; + + updateCenterCircle(visibleGeometry.at((curveid_ - 1) * 7)); + updateCenterCircle(mouseOverGeometry.at((curveid_ - 1) * 7)); + + updateEllipse(visibleGeometry.at((curveid_ - 1) * 7 + 1)); + updateEllipse(mouseOverGeometry.at((curveid_ - 1) * 7 + 1)); + + updateRectangle(visibleGeometry.at((curveid_ - 1) * 7 + 2)); + updateRectangle(mouseOverGeometry.at((curveid_ - 1) * 7 + 2)); + + updateSelectionCircle(visibleGeometry.at((curveid_ - 1) * 7 + 3), decayX, 0.); + updateSelectionCircle(mouseOverGeometry.at((curveid_ - 1) * 7 + 3), decayX, 0.); + + updateSelectionCircle(visibleGeometry.at((curveid_ - 1) * 7 + 4), -decayXL, 0.); + updateSelectionCircle(mouseOverGeometry.at((curveid_ - 1) * 7 + 4), -decayXL, 0.); + + updateSelectionCircle(visibleGeometry.at((curveid_ - 1) * 7 + 5), 0., decayY); + updateSelectionCircle(mouseOverGeometry.at((curveid_ - 1) * 7 + 5), 0., decayY); + + updateSelectionCircle(visibleGeometry.at((curveid_ - 1) * 7 + 6), 0., -decayYT); + updateSelectionCircle(mouseOverGeometry.at((curveid_ - 1) * 7 + 6), 0., -decayYT); + + // Update Arcellipse/Rectangle visibility according to shape and visibility + if (isvisible_) { + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7)->setActive(true); // centerCircle + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 3)->setActive(true); // cirX + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 4)->setActive(true); // cirXL + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 5)->setActive(true); // cirY + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 6)->setActive(true); // cirYT + + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7)->setActive(true); // centerCircle + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 3)->setActive(true); // cirX + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 4)->setActive(true); // cirXL + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 5)->setActive(true); // cirY + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 6)->setActive(true); // cirYT + + if (shape_ == 0) { // 0 = Ellipse + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 1)->setActive(true); // shape_ellipse + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 2)->setActive(false); // shape_rectangle + + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 1)->setActive(true); // shape_ellipse + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 2)->setActive(false); // shape_rectangle + } else { // 1 = Rectangle + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 1)->setActive(false); // shape_ellipse + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 2)->setActive(true); // shape_rectangle + + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 1)->setActive(false); // shape_ellipse + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 2)->setActive(true); // shape_rectangle + } + } else { + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7)->setActive(false); // centerCircle + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 1)->setActive(false); // shape_ellipse + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 2)->setActive(false); // shape_rectangle + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 3)->setActive(false); // cirX + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 4)->setActive(false); // cirXL + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 5)->setActive(false); // cirY + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 7 + 6)->setActive(false); // cirYT + + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7)->setActive(false); // centerCircle + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 1)->setActive(false); // shape_ellipse + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 2)->setActive(false); // shape_rectangle + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 3)->setActive(false); // cirX + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 4)->setActive(false); // cirXL + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 5)->setActive(false); // cirY + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 7 + 6)->setActive(false); // cirYT + } +} + +void ControlSpotPanel::deleteControlSpotCurve(Gtk::TreeModel::Row& row) +{ + const int curveid_ = row[spots_.curveid]; + + // printf("deleteControlSpotCurve: %d\n", curveid_); + + if (curveid_ == 0) { // Row has no associated curve + return; + } + + // visibleGeometry + for (int i = 6; i >= 0; i--) { + delete *(EditSubscriber::visibleGeometry.begin() + (curveid_ - 1) * 7 + i); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin() + (curveid_ - 1) * 7 + i); + } + + // mouseOverGeometry + for (int i = 6; i >= 0; i--) { + delete *(EditSubscriber::mouseOverGeometry.begin() + (curveid_ - 1) * 7 + i); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin() + (curveid_ - 1) * 7 + i); + } + + row[spots_.curveid] = 0; // Reset associated curve id + + // Reordering curve id + const Gtk::TreeModel::Children children = treemodel_->children(); + + for (auto iter = children.begin(); iter != children.end(); iter++) { + Gtk::TreeModel::Row r = *iter; + + if (r[spots_.curveid] > curveid_) { + r[spots_.curveid] = r[spots_.curveid] - 1; + } + } +} + +void ControlSpotPanel::updateCurveOpacity(const Gtk::TreeModel::Row& selectedRow) +{ + const int curveid_ = selectedRow[spots_.curveid]; + + // printf("updateCurveOpacity: %d\n", curveid_); + + if (curveid_ == 0) { // Row has no associated curve + return; + } + + for (int it_ = 0; it_ < (int) EditSubscriber::visibleGeometry.size(); it_++) { + if ((it_ < ((curveid_ - 1) * 7)) || (it_ > ((curveid_ - 1) * 7) + 6)) { // it_ does not belong to selected curve + EditSubscriber::visibleGeometry.at(it_)->opacity = 25.; + } else { + EditSubscriber::visibleGeometry.at(it_)->opacity = 75.; + } + } +} + +CursorShape ControlSpotPanel::getCursor(int objectID) const +{ + // printf("Object ID: %d\n", objectID); + + // When there is no control spot (i.e. no selected row), objectID can unexpectedly be different from -1 and produced not desired behavior + const auto s = treeview_->get_selection(); + + if (!s->count_selected_rows()) { + return CSHandOpen; + } + + const int rem_ = objectID % 7; + + switch (rem_) { + case (0): // centerCircle: (curveid_ - 1) * 7 + return CSMove2D; + + case (1): // shape_ellipse: (curveid_ - 1) * 7 + 1 + return CSMove2D; + + case (2): // shape_rectangle: (curveid_ - 1) * 7 + 2 + return CSMove2D; + + case (3): // cirX: (curveid_ - 1) * 7 + 3 + return CSMove1DH; + + case (4): // cirXL: (curveid_ - 1) * 7 + 4 + return CSMove1DH; + + case (5): // cirY: (curveid_ - 1) * 7 + 5 + return CSMove1DV; + + case (6): // cirYT: (curveid_ - 1) * 7 + 6 + return CSMove1DV; + + default: + return CSHandOpen; + } +} + +bool ControlSpotPanel::mouseOver(int modifierKey) +{ + EditDataProvider* editProvider_ = getEditProvider(); + const auto s = treeview_->get_selection(); + + if (!editProvider_ || !s->count_selected_rows()) { // When there is no control spot (i.e. no selected row), objectID can unexpectedly be different from -1 and produced not desired behavior + return false; + } + + // Get selected row + const auto selIter = s->get_selected(); + const Gtk::TreeModel::Row selRow = *selIter; + + const int object_ = editProvider_->object; + + if (object_ != lastObject_) { + if (object_ == -1) { + // Reset mouseOver preview for visibleGeometry + for (size_t it_ = 0; it_ < EditSubscriber::visibleGeometry.size(); it_++) { + EditSubscriber::visibleGeometry.at(it_)->state = Geometry::NORMAL; + } + + // Reset mouseOver preview for TreeView + const Gtk::TreeModel::Children children = treemodel_->children(); + + for (auto iter = children.begin(); iter != children.end(); iter++) { + Gtk::TreeModel::Row row = *iter; + row[spots_.mouseover] = false; + } + + // Actualize lastObject_ + lastObject_ = object_; + return false; + } + + const int curveId_ = object_ / 7 + 1; + const int rem = object_ % 7; + + // Manage mouseOver preview for TreeView + const Gtk::TreeModel::Children children = treemodel_->children(); + + for (auto iter = children.begin(); iter != children.end(); iter++) { + Gtk::TreeModel::Row row = *iter; + + if (row[spots_.curveid] == curveId_ && *row != *selRow) { + row[spots_.mouseover] = true; + } else { + row[spots_.mouseover] = false; + } + } + + for (int it_ = 0; it_ < (int) EditSubscriber::visibleGeometry.size(); it_++) { + if ((it_ < ((curveId_ - 1) * 7)) || (it_ > ((curveId_ - 1) * 7) + 6)) { // it_ does not belong to cursor pointed curve + EditSubscriber::visibleGeometry.at(it_)->state = Geometry::NORMAL; + } + } + + const int method = shapeMethod_->get_active_row_number(); + + // Circle, Arcellipses and Rectangle + if (rem >= 0 && rem < 3) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 1)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 2)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 3)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 4)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 5)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 6)->state = Geometry::PRELIGHT; + } else { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 2)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 3)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 4)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 4)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 5)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 6)->state = Geometry::NORMAL; + } + + // cirX + if (rem == 3) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 3)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 4)->state = Geometry::PRELIGHT; + } + } + + // cirXL + if (rem == 4) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 4)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 3)->state = Geometry::PRELIGHT; + } + } + + // cirY + if (rem == 5) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 5)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 6)->state = Geometry::PRELIGHT; + } + } + + // cirYT + if (rem == 6) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 6)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 7 + 5)->state = Geometry::PRELIGHT; + } + } + + lastObject_ = object_; + return true; + } + + return false; +} + +bool ControlSpotPanel::button1Pressed(int modifierKey) +{ + // printf("button1Pressed\n"); + + EditDataProvider *provider = getEditProvider(); + const auto s = treeview_->get_selection(); + + if (!provider || lastObject_ == -1 || !s->count_selected_rows()) { // When there is no control spot (i.e. no selected row), objectID can unexpectedly be different from -1 and produced not desired behavior + return false; + } + + // Select associated control spot + const int curveId_ = lastObject_ / 7 + 1; + Gtk::TreeModel::Children children = treemodel_->children(); + + for (auto iter = children.begin(); iter != children.end(); iter++) { + const Gtk::TreeModel::Row r = *iter; + + if (r[spots_.curveid] == curveId_) { + treeview_->set_cursor(treemodel_->get_path(r)); + break; + } + } + + lastCoord_.set(provider->posImage.x + provider->deltaImage.x, provider->posImage.y + provider->deltaImage.y); + EditSubscriber::action = EditSubscriber::Action::DRAGGING; + return true; +} + +bool ControlSpotPanel::button1Released() +{ + // printf("button1Released\n"); + EditSubscriber::action = EditSubscriber::Action::NONE; + return true; +} + +bool ControlSpotPanel::drag1(int modifierKey) +{ + // printf("drag1\n"); + + EditDataProvider *provider = getEditProvider(); + const auto s = treeview_->get_selection(); + + if (!provider || lastObject_ == -1 || !s->count_selected_rows()) { // When there is no control spot (i.e. no selected row), objectID can unexpectedly be different from -1 and produced not desired behavior + return false; + } + + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + int imW, imH; + provider->getImageSize(imW, imH); + const int rem = lastObject_ % 7; + const int method = shapeMethod_->get_active_row_number(); + Coord newCoord = Coord(provider->posImage.x + provider->deltaImage.x, provider->posImage.y + provider->deltaImage.y); + + // Circle, Ellipses and Rectangle + if (rem >= 0 && rem < 3) { + double deltaX = (double (newCoord.x) - double (lastCoord_.x)) * 2000. / double (imW); + double deltaY = (double (newCoord.y) - double (lastCoord_.y)) * 2000. / double (imH); + centerX_->setValue(centerX_->getValue() + deltaX); + centerY_->setValue(centerY_->getValue() + deltaY); + row[spots_.centerX] = centerX_->getIntValue(); + row[spots_.centerY] = centerY_->getIntValue(); + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotCenter, "X=" + centerX_->getTextValue() + ", Y=" + centerY_->getTextValue()); + } + } + + // cirX + if (rem == 3) { + double deltaX = (double (newCoord.x) - double (lastCoord_.x)) * 2000. / double (imW); + locX_->setValue(locX_->getValue() + deltaX); + row[spots_.locX] = locX_->getIntValue(); + + if (method == 1 || method == 3) { // Symmetrical cases + disableParamlistener(true); + locXL_->setValue(locX_->getValue()); + disableParamlistener(false); + row[spots_.locXL] = locXL_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocX, locX_->getTextValue()); + } + } + + // cirXL + if (rem == 4) { + double deltaXL = (double (lastCoord_.x) - double (newCoord.x)) * 2000. / double (imW); + locXL_->setValue(locXL_->getValue() + deltaXL); + row[spots_.locXL] = locXL_->getIntValue(); + + if (method == 1 || method == 3) { // Symmetrical cases + disableParamlistener(true); + locX_->setValue(locXL_->getValue()); + disableParamlistener(false); + row[spots_.locX] = locX_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocXL, locXL_->getTextValue()); + } + } + + // cirY + if (rem == 5) { + double deltaY = (double (newCoord.y) - double (lastCoord_.y)) * 2000. / double (imH); + locY_->setValue(locY_->getValue() + deltaY); + row[spots_.locY] = locY_->getIntValue(); + + if (method == 1 || method == 3) { // Symmetrical cases + disableParamlistener(true); + locYT_->setValue(locY_->getValue()); + disableParamlistener(false); + row[spots_.locYT] = locYT_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocY, locY_->getTextValue()); + } + } + + // cirYT + if (rem == 6) { + double deltaYT = (double (lastCoord_.y) - double (newCoord.y)) * 2000. / double (imH); + locYT_->setValue(locYT_->getValue() + deltaYT); + row[spots_.locYT] = locYT_->getIntValue(); + + if (method == 1 || method == 3) { // Symmetrical cases + disableParamlistener(true); + locY_->setValue(locYT_->getValue()); + disableParamlistener(false); + row[spots_.locY] = locY_->getIntValue(); + } + + updateControlSpotCurve(row); + + if (listener) { + listener->panelChanged(EvLocallabSpotLocYT, locYT_->getTextValue()); + } + } + + lastCoord_.set(newCoord.x, newCoord.y); + return true; +} + +int ControlSpotPanel::getEventType() +{ + const int tmp = eventType; + eventType = None; // Re-initialization at "None" if event type gotten + return tmp; +} + +ControlSpotPanel::SpotRow* ControlSpotPanel::getSpot(const int index) +{ + // printf("getSpot: %d\n", index); + + MyMutex::MyLock lock(mTreeview); + + SpotRow* r = new SpotRow(); + + int i = -1; + + for (auto &row : treemodel_->children()) { + i++; + + if (i == index) { + r->name = row[spots_.name]; + r->isvisible = row[spots_.isvisible]; + r->shape = row[spots_.shape]; + r->spotMethod = row[spots_.spotMethod]; +// r->mergeMethod = row[spots_.mergeMethod]; + r->sensiexclu = row[spots_.sensiexclu]; + r->structexclu = row[spots_.structexclu]; + r->struc = row[spots_.struc]; + r->shapeMethod = row[spots_.shapeMethod]; + r->locX = row[spots_.locX]; + r->locXL = row[spots_.locXL]; + r->locY = row[spots_.locY]; + r->locYT = row[spots_.locYT]; + r->centerX = row[spots_.centerX]; + r->centerY = row[spots_.centerY]; + r->circrad = row[spots_.circrad]; + r->qualityMethod = row[spots_.qualityMethod]; + r->complexMethod = row[spots_.complexMethod]; + r->transit = row[spots_.transit]; + r->feather = row[spots_.feather]; + r->thresh = row[spots_.thresh]; + r->iter = row[spots_.iter]; + r->balan = row[spots_.balan]; + r->balanh = row[spots_.balanh]; + r->colorde = row[spots_.colorde]; + r->colorscope = row[spots_.colorscope]; + r->transitweak = row[spots_.transitweak]; + r->transitgrad = row[spots_.transitgrad]; + r->scopemask = row[spots_.scopemask]; + r->lumask = row[spots_.lumask]; + r->avoid = row[spots_.avoid]; + r->blwh = row[spots_.blwh]; + r->recurs = row[spots_.recurs]; + r->laplac = row[spots_.laplac]; + r->deltae = row[spots_.deltae]; + r->shortc = row[spots_.shortc]; + r->savrest = row[spots_.savrest]; + r->wavMethod = row[spots_.wavMethod]; + + return r; + } + } + + return nullptr; +} + +int ControlSpotPanel::getSpotNumber() +{ + // printf("getSpotNumber\n"); + + return (int)treemodel_->children().size(); +} + +int ControlSpotPanel::getSelectedSpot() +{ + // printf("getSelectedSpot\n"); + + MyMutex::MyLock lock(mTreeview); + + const auto s = treeview_->get_selection(); + + // Check if treeview has row, otherwise return 0 + if (!s->count_selected_rows()) { + return -1; + } + + const auto selRow = s->get_selected(); + + // Get selected spot index + int index = -1; + + for (auto i : treemodel_->children()) { + index++; + + if (selRow == i) { + return index; + } + } + + return -1; +} + +bool ControlSpotPanel::setSelectedSpot(const int index) +{ + // printf("setSelectedSpot: %d\n", index); + + MyMutex::MyLock lock(mTreeview); + + int i = -1; + + for (auto &row : treemodel_->children()) { + i++; + + if (i == index) { + disableParamlistener(true); + + treeview_->set_cursor(treemodel_->get_path(row)); + load_ControlSpot_param(); + updateParamVisibility(); + updateCurveOpacity(row); + + disableParamlistener(false); + + return true; + } + } + + return false; +} + +bool ControlSpotPanel::isDeltaEPrevActive() +{ + return (preview_->get_active()); +} + +void ControlSpotPanel::resetDeltaEPreview() +{ + previewConn_.block(true); + preview_->set_active(false); + previewConn_.block(false); +} + +void ControlSpotPanel::addControlSpot(SpotRow* newSpot) +{ + // printf("addControlSpot: %d\n", newSpot->name); + + MyMutex::MyLock lock(mTreeview); + + disableParamlistener(true); + Gtk::TreeModel::Row row = *(treemodel_->append()); + row[spots_.mouseover] = false; + row[spots_.name] = newSpot->name; + row[spots_.isvisible] = newSpot->isvisible; + row[spots_.curveid] = 0; // No associated curve + row[spots_.shape] = newSpot->shape; + row[spots_.spotMethod] = newSpot->spotMethod; + row[spots_.sensiexclu] = newSpot->sensiexclu; + row[spots_.structexclu] = newSpot->structexclu; + row[spots_.shapeMethod] = newSpot->shapeMethod; + row[spots_.locX] = newSpot->locX; + row[spots_.locXL] = newSpot->locXL; + row[spots_.locY] = newSpot->locY; + row[spots_.locYT] = newSpot->locYT; + row[spots_.centerX] = newSpot->centerX; + row[spots_.centerY] = newSpot->centerY; + row[spots_.circrad] = newSpot->circrad; + row[spots_.qualityMethod] = newSpot->qualityMethod; + row[spots_.transit] = newSpot->transit; + row[spots_.transitweak] = newSpot->transitweak; + row[spots_.transitgrad] = newSpot->transitgrad; + row[spots_.feather] = newSpot->feather; + row[spots_.struc] = newSpot->struc; + row[spots_.thresh] = newSpot->thresh; + row[spots_.iter] = newSpot->iter; + row[spots_.balan] = newSpot->balan; + row[spots_.balanh] = newSpot->balanh; + row[spots_.colorde] = newSpot->colorde; + row[spots_.colorscope] = newSpot->colorscope; + row[spots_.avoid] = newSpot->avoid; + row[spots_.blwh] = newSpot->blwh; + row[spots_.recurs] = newSpot->recurs; + row[spots_.laplac] = newSpot->laplac; + row[spots_.deltae] = newSpot->deltae; + row[spots_.scopemask] = newSpot->scopemask; + row[spots_.shortc] = newSpot->shortc; + row[spots_.lumask] = newSpot->lumask; + row[spots_.savrest] = newSpot->savrest; + row[spots_.complexMethod] = newSpot->complexMethod; + row[spots_.wavMethod] = newSpot->wavMethod; + updateParamVisibility(); + disableParamlistener(false); + + // Add associated control spot curve + addControlSpotCurve(row); + updateControlSpotCurve(row); +} + +void ControlSpotPanel::deleteControlSpot(const int index) +{ + // printf("deleteControlSpot: %d\n", index); + + MyMutex::MyLock lock(mTreeview); + + disableParamlistener(true); + + int i = -1; + + for (auto iter : treemodel_->children()) { + i++; + + if (i == index) { + Gtk::TreeModel::Row row = *iter; + deleteControlSpotCurve(row); + treemodel_->erase(*row); + break; + } + } + + disableParamlistener(false); +} + +void ControlSpotPanel::setDefaults(const rtengine::procparams::ProcParams * defParams, const ParamsEdited * pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + sensiexclu_->setDefault((double)defSpot.sensiexclu); + structexclu_->setDefault((double)defSpot.structexclu); + locX_->setDefault((double)defSpot.loc.at(0)); + locXL_->setDefault((double)defSpot.loc.at(1)); + locY_->setDefault((double)defSpot.loc.at(2)); + locYT_->setDefault((double)defSpot.loc.at(3)); + centerX_->setDefault((double)defSpot.centerX); + centerY_->setDefault((double)defSpot.centerY); + circrad_->setDefault((double)defSpot.circrad); + transit_->setDefault(defSpot.transit); + transitweak_->setDefault(defSpot.transitweak); + transitgrad_->setDefault(defSpot.transitgrad); + feather_->setDefault(defSpot.feather); + struc_->setDefault(defSpot.struc); + thresh_->setDefault(defSpot.thresh); + iter_->setDefault(defSpot.iter); + balan_->setDefault(defSpot.balan); + balanh_->setDefault(defSpot.balanh); + colorde_->setDefault(defSpot.colorde); + colorscope_->setDefault(defSpot.colorscope); + scopemask_->setDefault((double)defSpot.scopemask); + lumask_->setDefault((double)defSpot.lumask); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +//----------------------------------------------------------------------------- +// ControlSpots +//----------------------------------------------------------------------------- + +ControlSpotPanel::ControlSpots::ControlSpots() +{ + add(mouseover); + add(name); + add(isvisible); + add(curveid); + add(shape); + add(spotMethod); + add(sensiexclu); + add(structexclu); + add(shapeMethod); + add(locX); + add(locXL); + add(locYT); + add(locY); + add(centerX); + add(centerY); + add(circrad); + add(qualityMethod); + add(transit); + add(transitweak); + add(transitgrad); + add(feather); + add(struc); + add(thresh); + add(iter); + add(balan); + add(balanh); + add(colorde); + add(colorscope); + add(avoid); + add(blwh); + add(recurs); + add(laplac); + add(deltae); + add(scopemask); + add(shortc); + add(lumask); + add(savrest); + add(complexMethod); + add(wavMethod); +} + +//----------------------------------------------------------------------------- +// RenameDialog +//----------------------------------------------------------------------------- + +ControlSpotPanel::RenameDialog::RenameDialog(const Glib::ustring &actualname, Gtk::Window &parent): + Gtk::Dialog(M("TP_LOCALLAB_REN_DIALOG_NAME"), parent), + + newname_(Gtk::manage(new Gtk::Entry())) +{ + // Entry widget + Gtk::HBox* const hb = Gtk::manage(new Gtk::HBox()); + hb->pack_start(*Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_REN_DIALOG_LAB"))), false, false, 4); + newname_->set_text(actualname); + hb->pack_start(*newname_); + get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); + + // OK/CANCEL buttons + add_button(M("GENERAL_OK"), OkButton); + add_button(M("GENERAL_CANCEL"), CancelButton); + + // Set OK button as default one when pressing enter + newname_->set_activates_default(); + set_default_response(OkButton); + + show_all_children(); +} + +Glib::ustring ControlSpotPanel::RenameDialog::get_new_name() +{ + return newname_->get_text(); +} diff --git a/rtgui/controlspotpanel.h b/rtgui/controlspotpanel.h new file mode 100644 index 000000000..f2bfa1fd0 --- /dev/null +++ b/rtgui/controlspotpanel.h @@ -0,0 +1,430 @@ +/* + * 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 . + * 2018 Pierre Cabrera + */ + +#ifndef _CONTROLSPOTPANEL_H_ +#define _CONTROLSPOTPANEL_H_ + +#include "../rtengine/coord.h" +#include "editcallbacks.h" +#include "threadutils.h" +#include "toolpanel.h" +#include "adjuster.h" + +class ControlPanelListener +{ +public: + ControlPanelListener() {}; + virtual ~ControlPanelListener() {}; + + virtual void resetToolMaskView() = 0; +}; + + +class ControlSpotPanel: + public ToolParamBlock, + public AdjusterListener, + public EditSubscriber, + public FoldableToolPanel +{ +public: + /** + * A SpotRow structure allows exchanges from and to ControlSpotClass + */ + struct SpotRow { + Glib::ustring name; + bool isvisible; + int shape; // 0 = Ellipse, 1 = Rectangle + int spotMethod; // 0 = Normal, 1 = Excluding + int sensiexclu; + int structexclu; + int shapeMethod; // 0 = Independent (mouse), 1 = Symmetrical (mouse), 2 = Independent (mouse + sliders), 3 = Symmetrical (mouse + sliders) + int locX; + int locXL; + int locY; + int locYT; + int centerX; + int centerY; + int circrad; + int qualityMethod; // 0 = Standard, 1 = Enhanced, 2 = Enhanced + chroma denoise + double transit; + double transitweak; + double transitgrad; + double feather; + double struc; + double thresh; + double iter; + double balan; + double balanh; + double colorde; + double colorscope; + bool avoid; + bool blwh; + bool recurs; + bool laplac; + bool deltae; + int scopemask; + bool shortc; + int lumask; + bool savrest; + int complexMethod; // 0 = Simple, 1 = Moderate, 2 = all + int wavMethod; // 0 = D2, 1 = D4, 2 = D6, 3 = D10, 4 = D14 + }; + + /** + * An event type enumeration allows exchanges of spot panel event type from and to ControlSpotClass + */ + enum eventType { + None = 0, + SpotCreation = 1, + SpotDeletion = 2, + SpotSelection = 3, + SpotDuplication = 4, + SpotAllVisibilityChanged = 5 + }; + + // Constructor and management functions + /** + * Default constructor of ControlSpotPanel class + */ + ControlSpotPanel(); + /** + * Destructor of ControlSpotPanel class + */ + ~ControlSpotPanel(); + /** + * Implementation of setEditProvider function of toolpanel.h + * + * @param provider The EditDataProvider to be linked to the panel to manage curves + */ + void setEditProvider(EditDataProvider* provider) override; + /** + * Setter for controlPanelListener + * + * @param cpl The ControlPanelListener to be linked to the panel + */ + void setControlPanelListener(ControlPanelListener* cpl) + { + controlPanelListener = cpl; + } + /** + * Getter of the event type raised by this panel + * + * @return The raised event type (refer to eventType enumeration) + */ + int getEventType(); + /** + * Getter of params of associated spot + * + * @param index The spot index to get params + * @return A SpotRow structure containing params of associated spot + */ + SpotRow* getSpot(const int index); + /** + * Getter of spots number + * + * @return The number of spots in panel + */ + int getSpotNumber(); + /** + * Getter of selected spot index + * + * @return The index of selected spot in treeview (return -1 if no selected spot) + */ + int getSelectedSpot(); + /** + * Setter of selected spot + * + * @param index The index of spot to be selected + * @return True if a spot corresponding to the index has been selected + */ + bool setSelectedSpot(const int index); + /** + * Setter for mask preview active indicator + * + * @param ind True is mask preview is active + */ + void setMaskPrevActive(bool ind) + { + maskPrevActive = ind; + } + /** + * Getter for deltaE preview active + * + * @return True if preview deltaE is active + */ + bool isDeltaEPrevActive(); + /** + * Reset deltaE preview active state + */ + void resetDeltaEPreview(); + + // Control spot creation functions + /** + * Add a new spot (and its associated curve) + * + * @param newSpot A SpotRow structure containing new spot params + */ + void addControlSpot(SpotRow* newSpot); + + // Control spot delete function + /** + * Delete a spot (and its associated curve) + * + * @param id The id of the spot to be deleted + */ + void deleteControlSpot(const int index); + + // Panel widgets management functions + /** + * Implementation of setDefaults function of toolpanel.h + * + * @param defParams ProcParams containing default values to set to the adjusters + * @param pedited ParamsEdited containing default state values to set to the adjusters (not used because batch mode is deactivated for Locallab) + */ + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + /** + * Enable or disable the interactions with panel widgets + * + * @param cond Condition to enable interactions + */ + void setParamEditable(bool cond); + /** + * Reset expander collapse state to default one + */ + void setDefaultExpanderVisibility(); + + // Batch mode management + // Note: Batch mode is deactivated for Locallab + +private: + // Cell renderer + void render_name(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_isvisible(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + + void on_button_add(); + void on_button_delete(); + void on_button_duplicate(); + void on_button_rename(); + bool on_button_visibility(GdkEventButton* event); + + bool blockTreeviewSearch(GdkEventKey* event); + bool onSpotSelectionEvent(GdkEventButton* event); + + void load_ControlSpot_param(); + + void controlspotChanged(); + + void shapeChanged(); + void spotMethodChanged(); + void shapeMethodChanged(); + void qualityMethodChanged(); + void complexMethodChanged(); + void wavMethodChanged(); + + void updateParamVisibility(); + + void adjusterChanged(Adjuster* a, double newval) override; + + void avoidChanged(); + void blwhChanged(); + void recursChanged(); + void laplacChanged(); + void deltaeChanged(); + void shortcChanged(); + void savrestChanged(); + + void previewChanged(); + + void disableParamlistener(bool cond); + + void addControlSpotCurve(Gtk::TreeModel::Row& row); + void updateControlSpotCurve(const Gtk::TreeModel::Row& row); + void deleteControlSpotCurve(Gtk::TreeModel::Row& row); + void updateCurveOpacity(const Gtk::TreeModel::Row& selectedRow); + CursorShape getCursor(int objectID) const override; + bool mouseOver(int modifierKey) override; + bool button1Pressed(int modifierKey) override; + bool button1Released() override; + bool drag1(int modifierKey) override; + + using ToolPanel::setDefaults; + + class ControlSpots: + public Gtk::TreeModel::ColumnRecord + { + public: + ControlSpots(); + + Gtk::TreeModelColumn mouseover; // Used to manage spot enlightening when mouse over + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn isvisible; + Gtk::TreeModelColumn curveid; // Associated curve id + Gtk::TreeModelColumn shape; // 0 = Ellipse, 1 = Rectangle + Gtk::TreeModelColumn spotMethod; // 0 = Normal, 1 = Excluding + Gtk::TreeModelColumn sensiexclu; + Gtk::TreeModelColumn structexclu; + Gtk::TreeModelColumn shapeMethod; // 0 = Independent (mouse), 1 = Symmetrical (mouse), 2 = Independent (mouse + sliders), 3 = Symmetrical (mouse + sliders) + Gtk::TreeModelColumn locX; + Gtk::TreeModelColumn locXL; + Gtk::TreeModelColumn locY; + Gtk::TreeModelColumn locYT; + Gtk::TreeModelColumn centerX; + Gtk::TreeModelColumn centerY; + Gtk::TreeModelColumn circrad; + Gtk::TreeModelColumn qualityMethod; // 0 = Standard, 1 = Enhanced, 2 = Enhanced + chroma denoise + Gtk::TreeModelColumn transit; + Gtk::TreeModelColumn transitweak; + Gtk::TreeModelColumn transitgrad; + Gtk::TreeModelColumn feather; + Gtk::TreeModelColumn struc; + Gtk::TreeModelColumn thresh; + Gtk::TreeModelColumn iter; + Gtk::TreeModelColumn balan; + Gtk::TreeModelColumn balanh; + Gtk::TreeModelColumn colorde; + Gtk::TreeModelColumn colorscope; + Gtk::TreeModelColumn avoid; + Gtk::TreeModelColumn blwh; + Gtk::TreeModelColumn recurs; + Gtk::TreeModelColumn laplac; + Gtk::TreeModelColumn deltae; + Gtk::TreeModelColumn scopemask; + Gtk::TreeModelColumn shortc; + Gtk::TreeModelColumn lumask; + Gtk::TreeModelColumn savrest; + Gtk::TreeModelColumn complexMethod; // 0 = Simple, 1 = mod, 2 = all + Gtk::TreeModelColumn wavMethod; // 0 = D2, 1 = D4, 2 = D6, 3 = D10, 4 = D14 + }; + + class RenameDialog: + public Gtk::Dialog + { + public: + enum DialogButton { + OkButton = 1, + CancelButton = 2 + }; + + RenameDialog(const Glib::ustring &actualname, Gtk::Window &parent); + Glib::ustring get_new_name(); + + private: + Gtk::Entry* const newname_; + }; + + ControlSpots spots_; + + // Child widgets + Gtk::ScrolledWindow* const scrolledwindow_; + Gtk::TreeView* const treeview_; + sigc::connection treeviewconn_; + Glib::RefPtr treemodel_; + + Gtk::Button* const button_add_; + sigc::connection buttonaddconn_; + Gtk::Button* const button_delete_; + sigc::connection buttondeleteconn_; + Gtk::Button* const button_duplicate_; + sigc::connection buttonduplicateconn_; + + Gtk::Button* const button_rename_; + sigc::connection buttonrenameconn_; + Gtk::Button* const button_visibility_; + sigc::connection buttonvisibilityconn_; + + MyComboBoxText* const shape_; + sigc::connection shapeconn_; + MyComboBoxText* const spotMethod_; + sigc::connection spotMethodconn_; + MyComboBoxText* const shapeMethod_; + sigc::connection shapeMethodconn_; + MyComboBoxText* const qualityMethod_; + sigc::connection qualityMethodconn_; + MyComboBoxText* const complexMethod_; + sigc::connection complexMethodconn_; + MyComboBoxText* const wavMethod_; + sigc::connection wavMethodconn_; + + Adjuster* const sensiexclu_; + Adjuster* const structexclu_; + Adjuster* const locX_; + Adjuster* const locXL_; + Adjuster* const locY_; + Adjuster* const locYT_; + Adjuster* const centerX_; + Adjuster* const centerY_; + Adjuster* const circrad_; + Adjuster* const transit_; + Adjuster* const transitweak_; + Adjuster* const transitgrad_; + Adjuster* const feather_; + Adjuster* const struc_; + Adjuster* const thresh_; + Adjuster* const iter_; + Adjuster* const balan_; + Adjuster* const balanh_; + Adjuster* const colorde_; + Adjuster* const colorscope_; + Adjuster* const scopemask_; + Adjuster* const lumask_; + + Gtk::CheckButton* const avoid_; + sigc::connection avoidConn_; + Gtk::CheckButton* const blwh_; + sigc::connection blwhConn_; + Gtk::CheckButton* const recurs_; + sigc::connection recursConn_; + Gtk::CheckButton* const laplac_; + sigc::connection laplacConn_; + Gtk::CheckButton* const deltae_; + sigc::connection deltaeConn_; + Gtk::CheckButton* const shortc_; + sigc::connection shortcConn_; + Gtk::CheckButton* const savrest_; + sigc::connection savrestConn_; + + MyExpander* const expTransGrad_; + MyExpander* const expShapeDetect_; + MyExpander* const expSpecCases_; + MyExpander* const expMaskMerge_; + + Gtk::ToggleButton* const preview_; + sigc::connection previewConn_; + + // Internal variables + ControlPanelListener* controlPanelListener; + int lastObject_; + rtengine::Coord lastCoord_; + bool nbSpotChanged_; + bool selSpotChanged_; + bool nameChanged_; + bool visibilityChanged_; + int eventType; // 0 = No event, 1 = Spot creation event, 2 = Spot deletion event, 3 = Spot selection event, 4 = Spot duplication event + Gtk::Frame* const excluFrame; + bool maskPrevActive; + + // Row background color + Gdk::RGBA colorMouseover, colorNominal, colorMouseovertext; + + // Treeview mutex + MyMutex mTreeview; +}; + +#endif // _CONTROLSPOTPANEL_H_ diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 8dc88473e..e38f3a947 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -248,6 +248,7 @@ CurveEditor::CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEd { bgHistValid = false; + locallabRef = 0.0; remoteDrag = false; selected = DCT_Linear; bottomBarCP = nullptr; @@ -322,6 +323,18 @@ void CurveEditor::updateBackgroundHistogram(const LUTu& hist) subGroup->updateBackgroundHistogram(this); } +/* + * Update Locallab reference value displayed in the background + */ +void CurveEditor::updateLocallabBackground(double ref) +{ + // Copy Locallab reference value in the curve editor cache + locallabRef = ref; + + // Then call the curve editor group to eventually update the histogram + subGroup->updateLocallabBackground(this); +} + // Open up the curve if it has modifications and it's not already opened // Returns: true if curve was non linear and opened bool CurveEditor::openIfNonlinear() diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index abc0cc10a..d5ec49559 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -59,7 +59,7 @@ protected: PopUpToggleButton* curveType; LUTu histogram; // histogram values bool bgHistValid; - + double locallabRef; // Locallab reference value bool remoteDrag; int selected; @@ -95,7 +95,7 @@ public: bool isUnChanged (); void setUnChanged (bool uc); void updateBackgroundHistogram(const LUTu& hist); - + void updateLocallabBackground(double ref); void setLeftBarColorProvider(ColorProvider* cp, int callerId); void setBottomBarColorProvider(ColorProvider* cp, int callerId); void setCurveColorProvider(ColorProvider* cp, int callerId); diff --git a/rtgui/curveeditorgroup.cc b/rtgui/curveeditorgroup.cc index cad49d331..5c120f7e4 100644 --- a/rtgui/curveeditorgroup.cc +++ b/rtgui/curveeditorgroup.cc @@ -274,6 +274,7 @@ void CurveEditorGroup::curveTypeToggled(CurveEditor* ce) if (ct < ce->subGroup->valUnchanged) { ce->subGroup->restoreDisplayedHistogram(); + ce->subGroup->restoreLocallabBackground(); } } else { // The button is now released, so we have to hide this CurveEditor diff --git a/rtgui/curveeditorgroup.h b/rtgui/curveeditorgroup.h index 5ef13656b..0f7c01a32 100644 --- a/rtgui/curveeditorgroup.h +++ b/rtgui/curveeditorgroup.h @@ -137,6 +137,7 @@ public: } void updateEditButton(CurveEditor* curve, Gtk::ToggleButton *button, sigc::connection &connection); virtual void updateBackgroundHistogram (CurveEditor* ce) {} + virtual void updateLocallabBackground(CurveEditor* ce) {}; virtual void switchGUI() = 0; virtual void refresh(CurveEditor *curveToRefresh) = 0; virtual void editModeSwitchedOff() = 0; @@ -166,6 +167,7 @@ protected: virtual void storeCurveValues (CurveEditor* ce, const std::vector& p) = 0; virtual void storeDisplayedCurve () = 0; virtual void restoreDisplayedHistogram() {}; + virtual void restoreLocallabBackground() {}; virtual void removeEditor () = 0; virtual const std::vector getCurveFromGUI (int type) = 0; diff --git a/rtgui/editcallbacks.cc b/rtgui/editcallbacks.cc index 1538ef7ba..5404b0bc9 100644 --- a/rtgui/editcallbacks.cc +++ b/rtgui/editcallbacks.cc @@ -99,10 +99,11 @@ bool EditSubscriber::isPicking() const EditDataProvider::EditDataProvider() : currSubscriber(nullptr), - object(0), +// object(0), pipetteVal1(0.f), pipetteVal2(0.f), pipetteVal3(0.f), + object(0), posScreen(-1, -1), posImage(-1, -1), deltaScreen(0, 0), diff --git a/rtgui/editcallbacks.h b/rtgui/editcallbacks.h index ee357c2f7..524ec1622 100644 --- a/rtgui/editcallbacks.h +++ b/rtgui/editcallbacks.h @@ -160,12 +160,13 @@ class EditDataProvider private: EditSubscriber *currSubscriber; - int object; /// ET_OBJECTS mode: Object detected under the cursor, 0 otherwise; ET_PIPETTE mode: 1 if above the image, 0 otherwise +// int object; /// ET_OBJECTS mode: Object detected under the cursor, 0 otherwise; ET_PIPETTE mode: 1 if above the image, 0 otherwise float pipetteVal1; /// Current pipette values float pipetteVal2; /// Current pipette values; if bufferType==BT_SINGLEPLANE_FLOAT, will be set to 0 float pipetteVal3; /// Current pipette values; if bufferType==BT_SINGLEPLANE_FLOAT, will be set to 0 public: + int object; /// ET_OBJECTS mode: Object detected under the cursor, 0 otherwise; ET_PIPETTE mode: 1 if above the image, 0 otherwise rtengine::Coord posScreen; /// Location of the mouse button press, in preview image space rtengine::Coord posImage; /// Location of the mouse button press, in the full image space @@ -192,4 +193,6 @@ public: int getPipetteRectSize () const; EditSubscriber* getCurrSubscriber() const; virtual void getImageSize (int &w, int&h) = 0; + virtual void getPreviewCenterPos(int &x, int &y) = 0; + virtual void getPreviewSize(int &w, int &h) = 0; }; diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 1a82c4a94..669dd491a 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -957,6 +957,13 @@ void EditorPanel::writeToolExpandedStatus (std::vector &tpOpen) } } +void EditorPanel::updateShowtooltipVisibility (bool showtooltip) +{ + if (tpc) { + tpc->updateShowtooltipVisibility (showtooltip); + } +} + void EditorPanel::showTopPanel (bool show) { if (tbTopPanel_1->get_active() != show) { @@ -1674,6 +1681,11 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) case GDK_KEY_F5: openThm->openDefaultViewer (3); return true; + + case GDK_KEY_f: + case GDK_KEY_F: + // No action is performed to avoid Gtk-CRITICAL due to Locallab treeview when treeview isn't focused + return true; } } //if (!ctrl) } //if (!alt) @@ -1735,6 +1747,7 @@ void EditorPanel::procParamsChanged (Thumbnail* thm, int whoChangedIt) PartialProfile pp (true); pp.set (true); * (pp.pparams) = openThm->getProcParams(); + pp.pedited->locallab.spots.resize(pp.pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); tpc->profileChange (&pp, rtengine::EvProfileChangeNotification, M ("PROGRESSDLG_PROFILECHANGEDINBROWSER")); pp.deleteInstance(); } diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h index 8993fea07..826793507 100644 --- a/rtgui/editorpanel.h +++ b/rtgui/editorpanel.h @@ -79,6 +79,7 @@ public: void writeOptions(); void writeToolExpandedStatus (std::vector &tpOpen); + void updateShowtooltipVisibility (bool showtooltip); void showTopPanel (bool show); bool isRealized() diff --git a/rtgui/editwidgets.cc b/rtgui/editwidgets.cc index e67b664db..fccdb874a 100644 --- a/rtgui/editwidgets.cc +++ b/rtgui/editwidgets.cc @@ -76,7 +76,7 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); cr->set_line_width( getOuterLineWidth() ); rtengine::Coord center_ = center; @@ -107,7 +107,7 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer color = innerLineColor; } - cr->set_source_rgb(color.getR(), color.getG(), color.getB()); + cr->set_source_rgba(color.getR(), color.getG(), color.getB(), opacity / 100.); } cr->set_line_width( innerLineWidth ); @@ -199,7 +199,7 @@ void Line::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer * color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.0); cr->set_line_width( getOuterLineWidth() ); rtengine::Coord begin_ = begin; @@ -234,7 +234,7 @@ void Line::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer * color = innerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); } cr->set_line_width(innerLineWidth); @@ -313,7 +313,7 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuff color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); cr->set_line_width( getOuterLineWidth() ); rtengine::Coord currPos; @@ -357,7 +357,7 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuff color = innerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); } cr->set_line_width( innerLineWidth ); @@ -506,7 +506,7 @@ void Rectangle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuf color = outerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); cr->set_line_width( getOuterLineWidth() ); rtengine::Coord tl, br; @@ -550,7 +550,7 @@ void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuf color = innerLineColor; } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); } cr->set_line_width( innerLineWidth ); @@ -646,6 +646,292 @@ void Rectangle::drawToMOChannel(Cairo::RefPtr &cr, unsigned shor } } +void Ellipse::drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getOuterLineColor(); + } else { + color = outerLineColor; + } + + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); + cr->set_line_width ( getOuterLineWidth() ); + + rtengine::Coord center_ = center; + double radYT_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radYT)) : double (radYT); + double radY_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radY)) : double (radY); + double radXL_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radXL)) : double (radXL); + double radX_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radX)) : double (radX); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); + } else if (datum == CLICKED_POINT) { + center_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (radYT_ > 0 && radY_ > 0 && radXL_ > 0 && radX_ > 0) { + // To have an ellipse with radius of (radX, radX), a circle of radius 1. shall be twisted with a scale + // of radX for x-axis, radY for y-axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (radX * x, radY * y) in new one + // To go back to previous location, center shall be translated to tx = -X * (1 - 1 / radX) in x-axis (x = tx + X) + // and ty = -Y * (1 - 1 / radY) in y-axis (y = ty + Y) + cr->save(); + + // Drawing bottom-right part + cr->scale (radX_, radY_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, 0.0, rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing bottom-left part + cr->scale (radXL_, radY_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI_2, rtengine::RT_PI); + cr->scale (radXL_, radY_); + + cr->restore (); + cr->save(); + + // Drawing top-left part + cr->scale (radXL_, radYT_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI, 3. * rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing top-right part + cr->scale (radX_, radYT_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, 3. * rtengine::RT_PI_2, 2. * rtengine::RT_PI); + + cr->restore (); + cr->stroke (); + } + } +} + +void Ellipse::drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_VISIBLE) { + if (state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgba (color.getR(), color.getG(), color.getB(), opacity / 100.); + } + + cr->set_line_width ( innerLineWidth ); + + rtengine::Coord center_ = center; + double radYT_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radYT)) : double (radYT); + double radY_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radY)) : double (radY); + double radXL_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radXL)) : double (radXL); + double radX_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radX)) : double (radX); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); + } else if (datum == CLICKED_POINT) { + center_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (filled && state != INSENSITIVE) { + if (radYT_ > 0 && radY_ > 0 && radXL_ > 0 && radX_ > 0) { + // To have an ellipse with radius of (radX, radX), a circle of radius 1. shall be twisted with a scale + // of radX for x-axis, radY for y-axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (radX * x, radY * y) in new one + // To go back to previous location, center shall be translated to tx = -X * (1 - 1 / radX) in x-axis (x = tx + X) + // and ty = -Y * (1 - 1 / radY) in y-axis (y = ty + Y) + cr->save(); + + // Drawing bottom-right part + cr->scale (radX_, radY_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, 0.0, rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing bottom-left part + cr->scale (radXL_, radY_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI_2, rtengine::RT_PI); + cr->scale (radXL_, radY_); + + cr->restore (); + cr->save(); + + // Drawing top-left part + cr->scale (radXL_, radYT_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI, 3. * rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing top-right part + cr->scale (radX_, radYT_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, 3. * rtengine::RT_PI_2, 2. * rtengine::RT_PI); + + cr->restore (); + cr->stroke (); + } + + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else if (innerLineWidth > 0.) { + if (radYT_ > 0 && radY_ > 0 && radXL_ > 0 && radX_ > 0) { + // To have an ellipse with radius of (radX, radX), a circle of radius 1. shall be twisted with a scale + // of radX for x-axis, radY for y-axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (radX * x, radY * y) in new one + // To go back to previous location, center shall be translated to tx = -X * (1 - 1 / radX) in x-axis (x = tx + X) + // and ty = -Y * (1 - 1 / radY) in y-axis (y = ty + Y) + cr->save(); + + // Drawing bottom-right part + cr->scale (radX_, radY_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, 0.0, rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing bottom-left part + cr->scale (radXL_, radY_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI_2, rtengine::RT_PI); + cr->scale (radXL_, radY_); + + cr->restore (); + cr->save(); + + // Drawing top-left part + cr->scale (radXL_, radYT_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI, 3. * rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing top-right part + cr->scale (radX_, radYT_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, 3. * rtengine::RT_PI_2, 2. * rtengine::RT_PI); + + cr->restore (); + cr->stroke (); + } + + if (state == INSENSITIVE) { + std::valarray ds (1); + ds[0] = 4; + cr->set_source_rgba (1.0, 1.0, 1.0, 0.618); + cr->stroke_preserve(); + cr->set_source_rgba (0.0, 0.0, 0.0, 0.618); + cr->set_dash (ds, 0); + cr->stroke(); + ds.resize (0); + cr->set_dash (ds, 0); + } else { + cr->stroke(); + } + } + } +} + +void Ellipse::drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_HOVERABLE) { + cr->set_line_width ( getMouseOverLineWidth() ); + + rtengine::Coord center_ = center; + double radYT_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radYT)) : double (radYT); + double radY_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radY)) : double (radY); + double radXL_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radXL)) : double (radXL); + double radX_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radX)) : double (radX); + + if (datum == IMAGE) { + coordSystem.imageCoordToCropCanvas (center.x, center.y, center_.x, center_.y); + } else if (datum == CLICKED_POINT) { + center_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (radYT_ > 0 && radY_ > 0 && radXL_ > 0 && radX_ > 0) { + // To have an ellipse with radius of (radX, radX), a circle of radius 1. shall be twisted with a scale + // of radX for x-axis, radY for y-axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (radX * x, radY * y) in new one + // To go back to previous location, center shall be translated to tx = -X * (1 - 1 / radX) in x-axis (x = tx + X) + // and ty = -Y * (1 - 1 / radY) in y-axis (y = ty + Y) + cr->save(); + + // Drawing bottom-right part + cr->scale (radX_, radY_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, 0.0, rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing bottom-left part + cr->scale (radXL_, radY_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radY_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI_2, rtengine::RT_PI); + cr->scale (radXL_, radY_); + + cr->restore (); + cr->save(); + + // Drawing top-left part + cr->scale (radXL_, radYT_); + cr->translate(- center_.x * (1 - 1 / radXL_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, rtengine::RT_PI, 3. * rtengine::RT_PI_2); + + cr->restore (); + cr->save(); + + // Drawing top-right part + cr->scale (radX_, radYT_); + cr->translate(- center_.x * (1 - 1 / radX_), - center_.y * (1 - 1 / radYT_)); + cr->arc (center_.x, center_.y, 1.0, 3. * rtengine::RT_PI_2, 2. * rtengine::RT_PI); + + cr->restore (); + cr->stroke (); + } + + if (filled) { + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else { + cr->stroke(); + } + } +} + void OPIcon::drivenPointToRectangle(const rtengine::Coord &pos, rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H) { diff --git a/rtgui/editwidgets.h b/rtgui/editwidgets.h index 0fa7a91bf..c86949cb4 100644 --- a/rtgui/editwidgets.h +++ b/rtgui/editwidgets.h @@ -234,6 +234,7 @@ public: float innerLineWidth; // ...outerLineWidth = innerLineWidth+2 Datum datum; State state; // set by the Subscriber + float opacity; // Percentage of opacity Geometry (); virtual ~Geometry() {} @@ -324,6 +325,25 @@ public: void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; }; +class Ellipse : public Geometry +{ +public: + rtengine::Coord center; + int radYT; // Ellipse half-radius for top y-axis + int radY; // Ellipse half-radius for bottom y-axis + int radXL; // Ellipse half-radius for left x-axis + int radX; // Ellipse half-radius for right x-axis + bool filled; + bool radiusInImageSpace; /// If true, the radius depend on the image scale; if false, it is a fixed 'screen' size + + Ellipse (); + Ellipse (const rtengine::Coord& center, int radYT, int radY, int radXL, int radX, bool filled = false, bool radiusInImageSpace = false); + + void drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; +}; + class OPIcon : public Geometry // OP stands for "On Preview" { @@ -476,7 +496,7 @@ inline Geometry::Geometry () : innerLineColor (char (255), char (255), char (255)), outerLineColor ( char (0), char (0), char (0)), flags ( F_VISIBLE | F_HOVERABLE | F_AUTO_COLOR), innerLineWidth (1.5f), datum ( - IMAGE), state (NORMAL) { + IMAGE), state (NORMAL), opacity(100.) { } inline RGBAColor::RGBAColor () : @@ -520,6 +540,10 @@ inline Line::Line () : begin (10, 10), end (100, 100) { } +inline Ellipse::Ellipse () : + center (100, 100), radYT (5), radY (5), radXL (10), radX (10), filled (false), radiusInImageSpace (false) { +} + inline Circle::Circle (rtengine::Coord& center, int radius, bool filled, bool radiusInImageSpace) : center (center), radius (radius), filled (filled), radiusInImageSpace ( @@ -540,5 +564,10 @@ inline Line::Line (int beginX, int beginY, int endX, int endY) : begin (beginX, beginY), end (endX, endY) { } -#endif +inline Ellipse::Ellipse (const rtengine::Coord& center, int radYT, int radY, int radXL, int radX, + bool filled, bool radiusInImageSpace) : + center (center), radYT (radYT), radY (radY), radXL (radXL), radX (radX), filled (filled), + radiusInImageSpace (radiusInImageSpace) { +} +#endif diff --git a/rtgui/filebrowser.cc b/rtgui/filebrowser.cc index caa60ebbc..42aafd2e0 100644 --- a/rtgui/filebrowser.cc +++ b/rtgui/filebrowser.cc @@ -29,6 +29,7 @@ #include "clipboard.h" #include "multilangmgr.h" #include "options.h" +#include "paramsedited.h" #include "profilestorecombobox.h" #include "procparamchangers.h" #include "rtimage.h" @@ -1066,6 +1067,8 @@ void FileBrowser::partPasteProfile () auto toplevel = static_cast (get_toplevel ()); PartialPasteDlg partialPasteDlg (M("PARTIALPASTE_DIALOGLABEL"), toplevel); + partialPasteDlg.updateSpotWidget(clipboard.getPartialProfile().pparams); + int i = partialPasteDlg.run (); if (i == Gtk::RESPONSE_OK) { @@ -1392,6 +1395,8 @@ void FileBrowser::applyPartialMenuItemActivated (ProfileStoreLabel *label) auto toplevel = static_cast (get_toplevel ()); PartialPasteDlg partialPasteDlg (M("PARTIALPASTE_DIALOGLABEL"), toplevel); + partialPasteDlg.updateSpotWidget(srcProfiles->pparams); + if (partialPasteDlg.run() == Gtk::RESPONSE_OK) { MYREADERLOCK(l, entryRW); @@ -1406,6 +1411,7 @@ void FileBrowser::applyPartialMenuItemActivated (ProfileStoreLabel *label) rtengine::procparams::PartialProfile dstProfile(true); *dstProfile.pparams = (static_cast(selected[i]))->thumbnail->getProcParams (); dstProfile.set(true); + dstProfile.pedited->locallab.spots.resize(dstProfile.pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); partialPasteDlg.applyPaste (dstProfile.pparams, dstProfile.pedited, srcProfiles->pparams, srcProfiles->pedited); (static_cast(selected[i]))->thumbnail->setProcParams (*dstProfile.pparams, dstProfile.pedited, FILEBROWSER); dstProfile.deleteInstance(); diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc index 102c17daf..42a2263e2 100644 --- a/rtgui/filecatalog.cc +++ b/rtgui/filecatalog.cc @@ -2446,6 +2446,8 @@ bool FileCatalog::handleShortcutKey (GdkEventKey* event) case GDK_KEY_underscore: zoomOut(); return true; + default: // do nothing, avoids a cppcheck false positive + break; } } diff --git a/rtgui/flatcurveeditorsubgroup.cc b/rtgui/flatcurveeditorsubgroup.cc index 4e0591ac3..1cc3f5c14 100644 --- a/rtgui/flatcurveeditorsubgroup.cc +++ b/rtgui/flatcurveeditorsubgroup.cc @@ -556,6 +556,13 @@ void FlatCurveEditorSubGroup::restoreDisplayedHistogram() } +void FlatCurveEditorSubGroup::restoreLocallabBackground() +{ + if (parent->displayedCurve) { + CPointsCurve->updateLocallabBackground(parent->displayedCurve->locallabRef); + } +} + void FlatCurveEditorSubGroup::storeCurveValues (CurveEditor* ce, const std::vector& p) { if (!p.empty()) { @@ -629,6 +636,13 @@ bool FlatCurveEditorSubGroup::curveReset(CurveEditor *ce) return true; } +void FlatCurveEditorSubGroup::updateLocallabBackground(CurveEditor* ce) +{ + if (ce == parent->displayedCurve) { + CPointsCurve->updateLocallabBackground(ce->locallabRef); + } +} + /*void FlatCurveEditorSubGroup::updateBackgroundHistogram (CurveEditor* ce) { CurveEditor* fce = (CurveEditor*)ce; if (fce==displayedCurve) { diff --git a/rtgui/flatcurveeditorsubgroup.h b/rtgui/flatcurveeditorsubgroup.h index 865a0ef83..79dd5e7d0 100644 --- a/rtgui/flatcurveeditorsubgroup.h +++ b/rtgui/flatcurveeditorsubgroup.h @@ -55,6 +55,7 @@ public: FlatCurveEditor* addCurve(Glib::ustring curveLabel = "", bool periodic = true); //virtual void updateBackgroundHistogram (CurveEditor* ce); + void updateLocallabBackground(CurveEditor* ce) override; void switchGUI() override; void refresh(CurveEditor *curveToRefresh) override; void editModeSwitchedOff() override; @@ -71,6 +72,7 @@ protected: void storeCurveValues (CurveEditor* ce, const std::vector& p) override; void storeDisplayedCurve () override; void restoreDisplayedHistogram () override; + void restoreLocallabBackground() override; void savePressed (); void loadPressed (); void copyPressed (); diff --git a/rtgui/gradient.cc b/rtgui/gradient.cc index b46f8e1ca..8229e883a 100644 --- a/rtgui/gradient.cc +++ b/rtgui/gradient.cc @@ -151,6 +151,8 @@ void Gradient::updateGeometry(const int centerX, const int centerY, const double int imW=0; int imH=0; + + if (fullWidth != -1 && fullHeight != -1) { imW = fullWidth; imH = fullHeight; diff --git a/rtgui/history.cc b/rtgui/history.cc index 5305c258b..1a9cc1258 100644 --- a/rtgui/history.cc +++ b/rtgui/history.cc @@ -29,16 +29,22 @@ using namespace rtengine; using namespace rtengine::procparams; -History::History (bool bookmarkSupport) : historyVPaned(nullptr), blistener(nullptr), tpc (nullptr), bmnum (1) +History::History (bool bookmarkSupport) : historyVPaned (nullptr), blistener (nullptr), tpc (nullptr), bmnum (1) { blistenerLock = false; // sets default that the Before preview will not be locked + /* + // fill history event message array + for (int i = 0; i < NUMOFEVENTS; i++) { + eventDescrArray[i] = M (Glib::ustring::compose ("HISTORY_MSG_%1", i + 1)); + } + */ // History List // ~~~~~~~~~~~~ Gtk::ScrolledWindow* hscrollw = Gtk::manage (new Gtk::ScrolledWindow ()); hscrollw->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); - Gtk::Frame* histFrame = Gtk::manage (new Gtk::Frame (M("HISTORY_LABEL"))); + Gtk::Frame* histFrame = Gtk::manage (new Gtk::Frame (M ("HISTORY_LABEL"))); histFrame->set_name ("HistoryPanel"); histFrame->add (*hscrollw); @@ -48,9 +54,9 @@ History::History (bool bookmarkSupport) : historyVPaned(nullptr), blistener(null historyModel = Gtk::ListStore::create (historyColumns); hTreeView->set_model (historyModel); hTreeView->set_headers_visible (false); - hTreeView->set_hscroll_policy(Gtk::SCROLL_MINIMUM); - hTreeView->set_vscroll_policy(Gtk::SCROLL_NATURAL); - hTreeView->set_size_request(80, -1); + hTreeView->set_hscroll_policy (Gtk::SCROLL_MINIMUM); + hTreeView->set_vscroll_policy (Gtk::SCROLL_NATURAL); + hTreeView->set_size_request (80, -1); Gtk::CellRendererText *changecrt = Gtk::manage (new Gtk::CellRendererText()); changecrt->property_ellipsize() = Pango::ELLIPSIZE_END; @@ -59,46 +65,46 @@ History::History (bool bookmarkSupport) : historyVPaned(nullptr), blistener(null Gtk::TreeView::Column *hviewcol = Gtk::manage (new Gtk::TreeView::Column ("")); hviewcol->pack_start (*changecrt, true); hviewcol->add_attribute (changecrt->property_markup (), historyColumns.text); - hviewcol->set_expand(true); + hviewcol->set_expand (true); hviewcol->set_resizable (true); - hviewcol->set_fixed_width(35); - hviewcol->set_min_width(35); - hviewcol->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE); + hviewcol->set_fixed_width (35); + hviewcol->set_min_width (35); + hviewcol->set_sizing (Gtk::TREE_VIEW_COLUMN_AUTOSIZE); Gtk::TreeView::Column *hviewcol2 = Gtk::manage (new Gtk::TreeView::Column ("")); hviewcol2->pack_start (*valuecrt, true); hviewcol2->add_attribute (valuecrt->property_markup (), historyColumns.value); - hviewcol2->set_expand(true); - hviewcol2->set_resizable(true); - hviewcol2->set_fixed_width(35); - hviewcol2->set_min_width(35); - hviewcol2->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE); - valuecrt->set_alignment(1.f, 0.f); + hviewcol2->set_expand (true); + hviewcol2->set_resizable (true); + hviewcol2->set_fixed_width (35); + hviewcol2->set_min_width (35); + hviewcol2->set_sizing (Gtk::TREE_VIEW_COLUMN_AUTOSIZE); + valuecrt->set_alignment (1.f, 0.f); - hTreeView->set_has_tooltip(true); - hTreeView->signal_query_tooltip().connect( sigc::mem_fun(*this, &History::on_query_tooltip) ); + hTreeView->set_has_tooltip (true); + hTreeView->signal_query_tooltip().connect ( sigc::mem_fun (*this, &History::on_query_tooltip) ); hTreeView->append_column (*hviewcol); hTreeView->append_column (*hviewcol2); - selchangehist = hTreeView->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &History::historySelectionChanged)); + selchangehist = hTreeView->get_selection()->signal_changed().connect (sigc::mem_fun (*this, &History::historySelectionChanged)); // Bookmark List // ~~~~~~~~~~~~~ Gtk::HBox* ahbox = Gtk::manage (new Gtk::HBox ()); addBookmark = Gtk::manage (new Gtk::Button ()); // M("HISTORY_NEWSNAPSHOT") - setExpandAlignProperties(addBookmark, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + setExpandAlignProperties (addBookmark, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); //addBookmark->get_style_context()->set_junction_sides(Gtk::JUNCTION_RIGHT); - addBookmark->get_style_context()->add_class("Left"); - addBookmark->set_tooltip_markup (M("HISTORY_NEWSNAPSHOT_TOOLTIP")); + addBookmark->get_style_context()->add_class ("Left"); + addBookmark->set_tooltip_markup (M ("HISTORY_NEWSNAPSHOT_TOOLTIP")); Gtk::Image* addimg = Gtk::manage (new RTImage ("add-small.png")); addBookmark->set_image (*addimg); ahbox->pack_start (*addBookmark); delBookmark = Gtk::manage (new Gtk::Button ()); // M("HISTORY_DELSNAPSHOT") - setExpandAlignProperties(delBookmark, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + setExpandAlignProperties (delBookmark, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); //delBookmark->get_style_context()->set_junction_sides(Gtk::JUNCTION_LEFT); - delBookmark->get_style_context()->add_class("Right"); + delBookmark->get_style_context()->add_class ("Right"); Gtk::Image* delimg = Gtk::manage (new RTImage ("remove-small.png")); delBookmark->set_image (*delimg); ahbox->pack_start (*delBookmark); @@ -108,19 +114,19 @@ History::History (bool bookmarkSupport) : historyVPaned(nullptr), blistener(null bscrollw->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); bscrollw->set_size_request (-1, 45); - Gtk::Frame* bmFrame = Gtk::manage (new Gtk::Frame (M("HISTORY_SNAPSHOTS"))); - bmFrame->set_name("Snapshots"); + Gtk::Frame* bmFrame = Gtk::manage (new Gtk::Frame (M ("HISTORY_SNAPSHOTS"))); + bmFrame->set_name ("Snapshots"); Gtk::VBox* bmBox = Gtk::manage (new Gtk::VBox ()); bmFrame->add (*bmBox); bmBox->pack_start (*bscrollw, Gtk::PACK_EXPAND_WIDGET, 4); bmBox->pack_end (*ahbox, Gtk::PACK_SHRINK, 4); - bmBox->set_size_request(-1,60); + bmBox->set_size_request (-1, 60); if (bookmarkSupport) { historyVPaned = Gtk::manage ( new Gtk::VPaned () ); historyVPaned->pack1 (*histFrame, true, true); historyVPaned->pack2 (*bmFrame, false, false); - pack_start(*historyVPaned); + pack_start (*historyVPaned); } else { pack_start (*histFrame); } @@ -132,19 +138,19 @@ History::History (bool bookmarkSupport) : historyVPaned(nullptr), blistener(null bookmarkModel = Gtk::ListStore::create (bookmarkColumns); bTreeView->set_model (bookmarkModel); bTreeView->set_headers_visible (false); - bTreeView->append_column_editable (M("HISTORY_SNAPSHOTS"), bookmarkColumns.text); + bTreeView->append_column_editable (M ("HISTORY_SNAPSHOTS"), bookmarkColumns.text); - selchangebm = bTreeView->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &History::bookmarkSelectionChanged)); + selchangebm = bTreeView->get_selection()->signal_changed().connect (sigc::mem_fun (*this, &History::bookmarkSelectionChanged)); - addBookmark->signal_clicked().connect( sigc::mem_fun(*this, &History::addBookmarkPressed) ); - delBookmark->signal_clicked().connect( sigc::mem_fun(*this, &History::delBookmarkPressed) ); + addBookmark->signal_clicked().connect ( sigc::mem_fun (*this, &History::addBookmarkPressed) ); + delBookmark->signal_clicked().connect ( sigc::mem_fun (*this, &History::delBookmarkPressed) ); //hTreeView->set_grid_lines (Gtk::TREE_VIEW_GRID_LINES_HORIZONTAL); hTreeView->set_grid_lines (Gtk::TREE_VIEW_GRID_LINES_BOTH); //hTreeView->signal_size_allocate().connect( sigc::mem_fun(*this, &History::resized) ); - hTreeView->set_enable_search(false); - bTreeView->set_enable_search(false); + hTreeView->set_enable_search (false); + bTreeView->set_enable_search (false); show_all_children (); } @@ -152,7 +158,7 @@ History::History (bool bookmarkSupport) : historyVPaned(nullptr), blistener(null void History::initHistory () { - ConnectionBlocker selBlocker(selchangehist); + ConnectionBlocker selBlocker (selchangehist); historyModel->clear (); bookmarkModel->clear (); } @@ -172,9 +178,11 @@ void History::historySelectionChanged () if (row && tpc) { ProcParams pparams = row[historyColumns.params]; - ParamsEdited pe(true); - PartialProfile pp(&pparams, &pe); + ParamsEdited pe (true); + pe.locallab.spots.resize(pparams.locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); + PartialProfile pp (&pparams, &pe); ParamsEdited paramsEdited = row[historyColumns.paramsEdited]; + tpc->profileChange (&pp, EvHistoryBrowsed, row[historyColumns.text], ¶msEdited); } @@ -205,8 +213,9 @@ void History::bookmarkSelectionChanged () if (row && tpc) { ProcParams pparams = row[bookmarkColumns.params]; - ParamsEdited pe(true); - PartialProfile pp(&pparams, &pe); + ParamsEdited pe (true); + pe.locallab.spots.resize(pparams.locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); + PartialProfile pp (&pparams, &pe); ParamsEdited paramsEdited = row[bookmarkColumns.paramsEdited]; tpc->profileChange (&pp, EvBookmarkSelected, row[bookmarkColumns.text], ¶msEdited); } @@ -221,7 +230,7 @@ void History::procParamsChanged( ) { // to prevent recursion, we filter out the events triggered by the history and events that should not be registered - if (ev == EvHistoryBrowsed || ev == EvMonitorTransform) { + if (ev == EvHistoryBrowsed || ev == EvMonitorTransform || descr == "") { return; } @@ -255,10 +264,11 @@ void History::procParamsChanged( } // if there is no last item or its chev!=ev, create a new one - if (size == 0 || !row || row[historyColumns.chev] != ev || ev == EvProfileChanged) { - Gtk::TreeModel::Row newrow = *(historyModel->append()); + if (size == 0 || !row || row[historyColumns.chev] != ev || ev == EvProfileChanged + || ev == EvLocallabSpotCreated || ev == EvLocallabSpotDeleted) { // Special cases: If Locallab spot is created , deleted or duplicated several times in a row, a new history row is used + Gtk::TreeModel::Row newrow = * (historyModel->append()); newrow[historyColumns.text] = ProcEventMapper::getInstance()->getHistoryMsg(ev); - newrow[historyColumns.value] = g_markup_escape_text(descr.c_str(), -1); + newrow[historyColumns.value] = g_markup_escape_text (descr.c_str(), -1); newrow[historyColumns.chev] = ev; newrow[historyColumns.params] = *params; newrow[historyColumns.paramsEdited] = paramsEdited ? *paramsEdited : defParamsEdited; @@ -314,7 +324,7 @@ void History::addBookmarkWithText (Glib::ustring text) } // append new row to bookmarks - Gtk::TreeModel::Row newrow = *(bookmarkModel->append()); + Gtk::TreeModel::Row newrow = * (bookmarkModel->append()); newrow[bookmarkColumns.text] = text; ProcParams params = row[historyColumns.params]; newrow[bookmarkColumns.params] = params; @@ -326,7 +336,7 @@ void History::addBookmarkPressed () { if (hTreeView->get_selection()->get_selected()) { - addBookmarkWithText (Glib::ustring::compose ("%1 %2", M("HISTORY_SNAPSHOT"), bmnum++)); + addBookmarkWithText (Glib::ustring::compose ("%1 %2", M ("HISTORY_SNAPSHOT"), bmnum++)); } } @@ -361,7 +371,7 @@ void History::undo () int size = historyModel->children().size (); if (size > 1) { - selection->select (historyModel->children().operator [](size - 2)); + selection->select (historyModel->children().operator [] (size - 2)); } } } @@ -382,7 +392,7 @@ void History::redo () int size = historyModel->children().size (); if (size > 1) { - selection->select (historyModel->children().operator [](size - 2)); + selection->select (historyModel->children().operator [] (size - 2)); } } } @@ -408,22 +418,27 @@ bool History::getBeforeLineParams (rtengine::procparams::ProcParams& params) return true; } -bool History::on_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr& tooltip) { +bool History::on_query_tooltip (int x, int y, bool keyboard_tooltip, const Glib::RefPtr& tooltip) +{ bool displayTooltip = false; Gtk::TreeModel::Path path; int x2 = -1; int y2 = -1; - hTreeView->convert_widget_to_bin_window_coords(x, y, x2, y2); - bool hasPath = hTreeView->get_path_at_pos(x2, y2, path); + hTreeView->convert_widget_to_bin_window_coords (x, y, x2, y2); + bool hasPath = hTreeView->get_path_at_pos (x2, y2, path); if (hasPath) { if (path && !path.empty()) { - Gtk::TreeModel::iterator iter = historyModel->get_iter(path); + Gtk::TreeModel::iterator iter = historyModel->get_iter (path); + if (iter) { +// Glib::ustring param, val; +// iter->get_value (1, param); +// iter->get_value (2, val); Glib::ustring text, val; - iter->get_value(0, text); - iter->get_value(1, val); + iter->get_value (0, text); + iter->get_value (1, val); /* * @@ -442,10 +457,12 @@ bool History::on_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib:: tooltip->set_custom(*hbox); */ - tooltip->set_text(text + " : " + val); +// tooltip->set_text (param + " : " + val); + tooltip->set_text (text + " : " + val); displayTooltip = true; } } } + return displayTooltip; } diff --git a/rtgui/imagearea.cc b/rtgui/imagearea.cc index 0be6982f9..22e140e7d 100644 --- a/rtgui/imagearea.cc +++ b/rtgui/imagearea.cc @@ -409,6 +409,34 @@ void ImageArea::getImageSize (int &w, int&h) } } +void ImageArea::getPreviewCenterPos(int &x, int &y) +{ + if (mainCropWindow) { + // Getting crop window size + int cW, cH; + mainCropWindow->getSize(cW, cH); + + // Converting center coord of crop window to image coord + const int cX = cW / 2; + const int cY = cH / 2; + mainCropWindow->screenCoordToImage(cX, cY, x, y); + } else { + x = y = 0; + } +} + +void ImageArea::getPreviewSize(int &w, int &h) +{ + if (mainCropWindow) { + int tmpW, tmpH; + mainCropWindow->getSize(tmpW, tmpH); + w = mainCropWindow->scaleValueToImage(tmpW); + h = mainCropWindow->scaleValueToImage(tmpH); + } else { + w = h = 0; + } +} + void ImageArea::grabFocus (CropWindow* cw) { diff --git a/rtgui/imagearea.h b/rtgui/imagearea.h index 586bba7a7..ad6fd305f 100644 --- a/rtgui/imagearea.h +++ b/rtgui/imagearea.h @@ -148,6 +148,8 @@ public: void subscribe(EditSubscriber *subscriber) override; void unsubscribe() override; void getImageSize (int &w, int&h) override; + void getPreviewCenterPos(int &x, int &y) override; + void getPreviewSize(int &w, int &h) override; // CropWindowListener interface void cropPositionChanged (CropWindow* cw) override; diff --git a/rtgui/lensgeomlistener.h b/rtgui/lensgeomlistener.h index 7bfa0fb45..810b7ed98 100644 --- a/rtgui/lensgeomlistener.h +++ b/rtgui/lensgeomlistener.h @@ -25,4 +25,5 @@ public: virtual void straightenRequested () = 0; virtual void autoCropRequested () = 0; virtual double autoDistorRequested () = 0; + virtual void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw) = 0; }; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc new file mode 100644 index 000000000..a4351c710 --- /dev/null +++ b/rtgui/locallab.cc @@ -0,0 +1,1189 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as publishfed 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 . + * 2017 Jacques Desmis + * 2019 Pierre Cabrera + */ +#include "locallab.h" + +#include "options.h" +#include "../rtengine/procparams.h" + +using namespace rtengine; +using namespace procparams; + +extern Options options; + +/* ==== LocallabToolList ==== */ +LocallabToolList::LocallabToolList(): + // Tool list GUI elements + list(Gtk::manage(new MyComboBox())), + listTreeModel(Gtk::ListStore::create(toolRow)), + + // Tool list listener + listListener(nullptr) +{ + list->set_model(listTreeModel); + list->pack_start(toolRow.name); + listConn = list->signal_changed().connect(sigc::mem_fun(*this, &LocallabToolList::toolRowSelected)); + list->set_tooltip_text(M("TP_LOCALLAB_LIST_TOOLTIP")); + // Append title row to list + // Important: Title row shall always be the first one + const auto titleRow = *(listTreeModel->append()); + titleRow[toolRow.id] = 0; + titleRow[toolRow.name] = M("TP_LOCALLAB_LIST_NAME"); + listConn.block(true); + list->set_active(titleRow); + listConn.block(false); + + // Add ComboBox to LocallabToolList widget + add(*list); +} + +void LocallabToolList::addToolRow(const Glib::ustring &toolname, const int id) +{ + // Disable event management + listConn.block(true); + + // Add tool name according to id + Gtk::TreeIter insertAfter; + + for (auto &r : listTreeModel->children()) { + if (r[toolRow.id] < id) { + insertAfter = *r; // Tool name shall be added at least after this row + } else { + break; // Tool name shall be added before this row + } + } + + // Note: There is always at list one row (i.e. title one) + + const auto newRow = *(listTreeModel->insert_after(insertAfter)); + newRow[toolRow.id] = id; + newRow[toolRow.name] = toolname; + + // Select title row (i.e. always first row) + list->set_active(0); + + // Enable event management + listConn.block(false); +} + +void LocallabToolList::removeToolRow(const Glib::ustring &toolname) +{ + // Disable event management + listConn.block(true); + + // Remove tool name row + for (auto &r : listTreeModel->children()) { + if (r[toolRow.name] == toolname) { + listTreeModel->erase(*r); + break; + } + } + + // Select title row (i.e. always first row) + list->set_active(0); + + // Enable event management + listConn.block(false); +} + +void LocallabToolList::removeAllTool() +{ + // Disable event management + listConn.block(true); + + // Remove all tools + listTreeModel->clear(); + + // Add title row again + const auto titleRow = *(listTreeModel->append()); + titleRow[toolRow.id] = 0; + titleRow[toolRow.name] = M("TP_LOCALLAB_LIST_NAME"); + + // Select title row (i.e. always first row) + list->set_active(0); + + // Enable event management + listConn.block(false); +} + +void LocallabToolList::toolRowSelected() +{ + // Get selected tool name + const auto selRow = *(list->get_active()); + const Glib::ustring toolname = selRow[toolRow.name]; + + // Remove selected tool name for ComboBox + removeToolRow(toolname); + + // Warm tool list listener + if (listListener) { + listListener->locallabToolToAdd(toolname); + } +} + +/* ==== Locallab ==== */ +Locallab::Locallab(): + FoldableToolPanel(this, "locallab", M("TP_LOCALLAB_LABEL"), false, true), + + // Spot control panel widget + expsettings(Gtk::manage(new ControlSpotPanel())), + + // Tool list widget + toollist(Gtk::manage(new LocallabToolList())), + + // Create Locallab tools + expcolor(Gtk::manage(new LocallabColor())), + expexpose(Gtk::manage(new LocallabExposure())), + expshadhigh(Gtk::manage(new LocallabShadow())), + expvibrance(Gtk::manage(new LocallabVibrance())), + expsoft(Gtk::manage(new LocallabSoft())), + expblur(Gtk::manage(new LocallabBlur())), + exptonemap(Gtk::manage(new LocallabTone())), + expreti(Gtk::manage(new LocallabRetinex())), + expsharp(Gtk::manage(new LocallabSharp())), + expcontrast(Gtk::manage(new LocallabContrast())), + expcbdl(Gtk::manage(new LocallabCBDL())), + explog(Gtk::manage(new LocallabLog())), + + // Other widgets + resetshowButton(Gtk::manage(new Gtk::Button(M("TP_LOCALLAB_RESETSHOW")))) +{ + // Create panel widget to receive Locallab GUI elements + ToolVBox* const panel = Gtk::manage(new ToolVBox()); + panel->set_spacing(2); + + // Add spot control panel to panel widget + expsettings->setControlPanelListener(this); + expsettings->setLevel(2); + panel->pack_start(*expsettings->getExpander(), false, false); + + // Add separator + Gtk::HSeparator* const separator = Gtk::manage(new Gtk::HSeparator()); + panel->pack_start(*separator, false, false); + + // Add tool list widget + toollist->setLocallabToolListListener(this); + panel->pack_start(*toollist, false, false); + + // Add Locallab tools to panel widget + ToolVBox* const toolpanel = Gtk::manage(new ToolVBox()); + toolpanel->set_name("LocallabToolPanel"); + addTool(toolpanel, expcolor); + addTool(toolpanel, expexpose); + addTool(toolpanel, expshadhigh); + addTool(toolpanel, expvibrance); + addTool(toolpanel, expsoft); + addTool(toolpanel, expblur); + addTool(toolpanel, exptonemap); + addTool(toolpanel, expreti); + addTool(toolpanel, expsharp); + addTool(toolpanel, expcontrast); + addTool(toolpanel, expcbdl); + addTool(toolpanel, explog); + panel->pack_start(*toolpanel, false, false); + + // Add separator + // Gtk::HSeparator* const separator2 = Gtk::manage(new Gtk::HSeparator()); + // panel->pack_start(*separator2, false, false); + + // Add mask reset button to panel widget + resetshowButton->signal_pressed().connect(sigc::mem_fun(*this, &Locallab::resetshowPressed)); + // panel->pack_start(*resetshowButton); + + // Add panel widget to Locallab GUI + pack_start(*panel); + + // Show all widgets + show_all(); + + // Update Locallab tools advice tooltips visibility based on saved option + for (auto tool : locallabTools) { + tool->updateAdviceTooltips(options.showtooltip); + } + + // By default, if no photo is loaded, all Locallab tools are removed and it's not possible to add them + // (to be necessary called after "show_all" function) + setParamEditable(false); +} + +void Locallab::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update Locallab activation state + setEnabled(pp->locallab.enabled); + + // Transmit Locallab activation state to Locallab tools + for (auto tool : locallabTools) { + tool->isLocallabActivated(exp->getEnabled()); + } + + // TODO Manage it with read function in controlspotpanel.cc + // Delete all existent spots + const int spotNb = expsettings->getSpotNumber(); + + for (int i = spotNb - 1; i >= 0; i--) { + expsettings->deleteControlSpot(i); + } + + // TODO Manage it with read function in controlspotpanel.cc + // Add existent spots based on pp + ControlSpotPanel::SpotRow* const r = new ControlSpotPanel::SpotRow(); + + for (int i = 0; i < (int)pp->locallab.spots.size(); i++) { + r->name = pp->locallab.spots.at(i).name; + r->isvisible = pp->locallab.spots.at(i).isvisible; + + if (pp->locallab.spots.at(i).shape == "ELI") { + r->shape = 0; + } else { + r->shape = 1; + } + + if (pp->locallab.spots.at(i).spotMethod == "norm") { + r->spotMethod = 0; + } else { + r->spotMethod = 1; + } + + r->sensiexclu = pp->locallab.spots.at(i).sensiexclu; + r->structexclu = pp->locallab.spots.at(i).structexclu; + + if (pp->locallab.spots.at(i).shapeMethod == "IND") { + r->shapeMethod = 0; + } else if (pp->locallab.spots.at(i).shapeMethod == "SYM") { + r->shapeMethod = 1; + } else if (pp->locallab.spots.at(i).shapeMethod == "INDSL") { + r->shapeMethod = 2; + } else { + r->shapeMethod = 3; + } + + r->locX = pp->locallab.spots.at(i).loc.at(0); + r->locXL = pp->locallab.spots.at(i).loc.at(1); + r->locY = pp->locallab.spots.at(i).loc.at(2); + r->locYT = pp->locallab.spots.at(i).loc.at(3); + r->centerX = pp->locallab.spots.at(i).centerX; + r->centerY = pp->locallab.spots.at(i).centerY; + r->circrad = pp->locallab.spots.at(i).circrad; + + if (pp->locallab.spots.at(i).qualityMethod == "enh") { + r->qualityMethod = 0; + } else { + r->qualityMethod = 1; + } + + r->transit = pp->locallab.spots.at(i).transit; + r->transitweak = pp->locallab.spots.at(i).transitweak; + r->transitgrad = pp->locallab.spots.at(i).transitgrad; + r->feather = pp->locallab.spots.at(i).feather; + r->struc = pp->locallab.spots.at(i).struc; + r->thresh = pp->locallab.spots.at(i).thresh; + r->iter = pp->locallab.spots.at(i).iter; + r->balan = pp->locallab.spots.at(i).balan; + r->balanh = pp->locallab.spots.at(i).balanh; + r->colorde = pp->locallab.spots.at(i).colorde; + r->colorscope = pp->locallab.spots.at(i).colorscope; + r->avoid = pp->locallab.spots.at(i).avoid; + r->blwh = pp->locallab.spots.at(i).blwh; + r->recurs = pp->locallab.spots.at(i).recurs; + r->laplac = pp->locallab.spots.at(i).laplac; + r->deltae = pp->locallab.spots.at(i).deltae; + r->scopemask = pp->locallab.spots.at(i).scopemask; + r->shortc = pp->locallab.spots.at(i).shortc; + r->lumask = pp->locallab.spots.at(i).lumask; + r->savrest = pp->locallab.spots.at(i).savrest; + + if (pp->locallab.spots.at(i).complexMethod == "sim") { + r->complexMethod = 0; + } else if (pp->locallab.spots.at(i).complexMethod == "mod") { + r->complexMethod = 1; + } else if (pp->locallab.spots.at(i).complexMethod == "all") { + r->complexMethod = 2; + } + + if (pp->locallab.spots.at(i).wavMethod == "D2") { + r->wavMethod = 0; + } else if (pp->locallab.spots.at(i).wavMethod == "D4") { + r->wavMethod = 1; + } else if (pp->locallab.spots.at(i).wavMethod == "D6") { + r->wavMethod = 2; + } else if (pp->locallab.spots.at(i).wavMethod == "D10") { + r->wavMethod = 3; + } else if (pp->locallab.spots.at(i).wavMethod == "D14") { + r->wavMethod = 4; + } + + expsettings->addControlSpot(r); + } + + // Select active spot + if (pp->locallab.spots.size() > 0) { + expsettings->setSelectedSpot(pp->locallab.selspot); + } + + // Update each Locallab tools GUI + for (auto tool : locallabTools) { + tool->read(pp, pedited); + } + + // Update tool list widget + int toolNb = 0; + toollist->removeAllTool(); // Reset Locallab list firstly + + for (auto tool : locallabTools) { + toolNb++; + + if (!tool->isLocallabToolAdded()) { + toollist->addToolRow(tool->getToolName(), toolNb); + } + } + + // Specific case: if there is no spot, GUI isn't anymore editable (i.e. Locallab tool cannot be managed) + if (pp->locallab.spots.size() > 0) { + setParamEditable(true); + } else { + setParamEditable(false); + } + + // Enable all listeners + enableListener(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void Locallab::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + // Update Locallab activation state + pp->locallab.enabled = getEnabled(); + + // Transmit Locallab activation state to Locallab tools (in case of updated) + for (auto tool : locallabTools) { + tool->isLocallabActivated(exp->getEnabled()); + } + + const int spotPanelEvent = expsettings->getEventType(); + int spotIndex; + ControlSpotPanel::SpotRow* r; + rtengine::procparams::LocallabParams::LocallabSpot* newSpot; + + int imW, imH; // Size of image + int prW, prH; // Size of preview area + int prX, prY; // Coord of preview area center + EditDataProvider* const provider = expsettings->getEditProvider(); + + int toolNb; + + switch (spotPanelEvent) { + case (ControlSpotPanel::SpotCreation): // Spot creation event + // Spot creation (default initialization) + newSpot = new LocallabParams::LocallabSpot(); + r = new ControlSpotPanel::SpotRow(); + r->name = newSpot->name = M("TP_LOCALLAB_SPOTNAME"); + r->isvisible = newSpot->isvisible; + + if (newSpot->shape == "ELI") { + r->shape = 0; + } else { + r->shape = 1; + } + + if (newSpot->spotMethod == "norm") { + r->spotMethod = 0; + } else { + r->spotMethod = 1; + } + + r->sensiexclu = newSpot->sensiexclu; + r->structexclu = newSpot->structexclu; + + if (newSpot->shapeMethod == "IND") { + r->shapeMethod = 0; + } else if (newSpot->shapeMethod == "SYM") { + r->shapeMethod = 1; + } else if (newSpot->shapeMethod == "INDSL") { + r->shapeMethod = 2; + } else { + r->shapeMethod = 3; + } + + // Calculate spot size and center position according to preview area + if (provider && !batchMode) { + provider->getImageSize(imW, imH); + provider->getPreviewCenterPos(prX, prY); + provider->getPreviewSize(prW, prH); + + if (imW && imH) { // Image loaded + // Spot center position computation + newSpot->centerX = rtengine::LIM(int(int((double)prX - (double)imW / 2.) * 2000. / (double)imW), -1000, 1000); + newSpot->centerY = rtengine::LIM(int(int((double)prY - (double)imH / 2.) * 2000. / (double)imH), -1000, 1000); + // Ellipse/rectangle size computation + newSpot->loc.at(0) = rtengine::LIM(int(((double)prW / 2. - 5.) * 2000. / (double)imW), 2, newSpot->loc.at(0)); + newSpot->loc.at(1) = rtengine::LIM(int(((double)prW / 2. - 5.) * 2000. / (double)imW), 2, newSpot->loc.at(1)); + newSpot->loc.at(2) = rtengine::LIM(int(((double)prH / 2. - 5.) * 2000. / (double)imH), 2, newSpot->loc.at(2)); + newSpot->loc.at(3) = rtengine::LIM(int(((double)prH / 2. - 5.) * 2000. / (double)imH), 2, newSpot->loc.at(3)); + } + } + + r->locX = newSpot->loc.at(0); + r->locXL = newSpot->loc.at(1); + r->locY = newSpot->loc.at(2); + r->locYT = newSpot->loc.at(3); + r->centerX = newSpot->centerX; + r->centerY = newSpot->centerY; + + r->circrad = newSpot->circrad; + + if (newSpot->qualityMethod == "enh") { + r->qualityMethod = 0; + } else { + r->qualityMethod = 1; + } + + r->transit = newSpot->transit; + r->transitweak = newSpot->transitweak; + r->transitgrad = newSpot->transitgrad; + r->feather = newSpot->feather; + r->struc = newSpot->struc; + r->thresh = newSpot->thresh; + r->iter = newSpot->iter; + r->balan = newSpot->balan; + r->balanh = newSpot->balanh; + r->colorde = newSpot->colorde; + r->colorscope = newSpot->colorscope; + r->avoid = newSpot->avoid; + r->blwh = newSpot->blwh; + r->recurs = newSpot->recurs; + r->laplac = newSpot->laplac; + r->deltae = newSpot->deltae; + r->scopemask = newSpot->scopemask; + r->shortc = newSpot->shortc; + r->lumask = newSpot->lumask; + r->savrest = newSpot->savrest; + + if (newSpot->complexMethod == "sim") { + r->complexMethod = 0; + } else if (newSpot->complexMethod == "mod") { + r->complexMethod = 1; + } else if (newSpot->complexMethod == "all") { + r->complexMethod = 2; + } + + if (newSpot->wavMethod == "D2") { + r->wavMethod = 0; + } else if (newSpot->wavMethod == "D4") { + r->wavMethod = 1; + } else if (newSpot->wavMethod == "D6") { + r->wavMethod = 2; + } else if (newSpot->wavMethod == "D10") { + r->wavMethod = 3; + } else if (newSpot->wavMethod == "D14") { + r->wavMethod = 4; + } + + expsettings->addControlSpot(r); + + // ProcParams update + pp->locallab.spots.push_back(*newSpot); + pp->locallab.selspot = pp->locallab.spots.size() - 1; + + // New created spot selection + expsettings->setSelectedSpot(pp->locallab.selspot); + + // Update Locallab tools GUI with new created spot + disableListener(); + + for (auto tool : locallabTools) { + tool->read(pp, pedited); + } + + enableListener(); + + // Update tool list widget + toolNb = 0; + toollist->removeAllTool(); // Reset Locallab list firstly + + for (auto tool : locallabTools) { + toolNb++; + + if (!tool->isLocallabToolAdded()) { + toollist->addToolRow(tool->getToolName(), toolNb); + } + } + + if (pp->locallab.spots.size() == 1) { + setParamEditable(true); + } + + // Update default values according to selected spot + setDefaults(pp, pedited); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab + + break; + + case (ControlSpotPanel::SpotDeletion): // Spot deletion event + // Get deleted spot index in ProcParams and update it + spotIndex = expsettings->getSelectedSpot(); + + for (int i = 0; i < (int)pp->locallab.spots.size(); i++) { + if (i == spotIndex) { + // ProcParams update + pp->locallab.spots.erase(pp->locallab.spots.begin() + i); + expsettings->deleteControlSpot(spotIndex); + + // Select the first remaining spot before deleted one + if (pp->locallab.spots.size() > 0) { + for (int j = i - 1; j >= 0; j--) { + if (expsettings->setSelectedSpot(j)) { // True if an existing spot has been selected on controlspotpanel + pp->locallab.selspot = j; + + break; + } + } + } else { + // Reset selspot + pp->locallab.selspot = 0; + } + + // Update Locallab tools GUI with selected spot + disableListener(); + + for (auto tool : locallabTools) { + tool->read(pp, pedited); + } + + enableListener(); + + // Update tool list widget + toolNb = 0; + toollist->removeAllTool(); // Reset Locallab list firstly + + for (auto tool : locallabTools) { + toolNb++; + + if (!tool->isLocallabToolAdded()) { + toollist->addToolRow(tool->getToolName(), toolNb); + } + } + + if (pp->locallab.spots.size() == 0) { + setParamEditable(false); + } + + // Update default values according to selected spot + setDefaults(pp, pedited); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab + + break; + } + } + + break; + + case (ControlSpotPanel::SpotSelection): // Spot selection event + pp->locallab.selspot = expsettings->getSelectedSpot(); + + // Update control spots and Locallab tools GUI with selected spot + expsettings->setSelectedSpot(pp->locallab.selspot); + disableListener(); + + for (auto tool : locallabTools) { + tool->read(pp, pedited); + } + + enableListener(); + + // Update tool list widget + toolNb = 0; + toollist->removeAllTool(); // Reset Locallab list firstly + + for (auto tool : locallabTools) { + toolNb++; + + if (!tool->isLocallabToolAdded()) { + toollist->addToolRow(tool->getToolName(), toolNb); + } + } + + // Update locallab tools mask background + if (pp->locallab.selspot < (int)maskBackRef.size()) { + const double huer = maskBackRef.at(pp->locallab.selspot).huer; + const double lumar = maskBackRef.at(pp->locallab.selspot).lumar; + const double chromar = maskBackRef.at(pp->locallab.selspot).chromar; + + for (auto tool : locallabTools) { + tool->refChanged(huer, lumar, chromar); + } + } + + // Update Locallab Retinex tool min/max + if (pp->locallab.selspot < (int)retiMinMax.size()) { + const double cdma = retiMinMax.at(pp->locallab.selspot).cdma; + const double cdmin = retiMinMax.at(pp->locallab.selspot).cdmin; + const double mini = retiMinMax.at(pp->locallab.selspot).mini; + const double maxi = retiMinMax.at(pp->locallab.selspot).maxi; + const double Tmean = retiMinMax.at(pp->locallab.selspot).Tmean; + const double Tsigma = retiMinMax.at(pp->locallab.selspot).Tsigma; + const double Tmin = retiMinMax.at(pp->locallab.selspot).Tmin; + const double Tmax = retiMinMax.at(pp->locallab.selspot).Tmax; + + expreti->updateMinMax(cdma, cdmin, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + } + + // Update default values according to selected spot + setDefaults(pp, pedited); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab + + break; + + case (ControlSpotPanel::SpotDuplication): // Spot duplication event + newSpot = nullptr; + spotIndex = expsettings->getSelectedSpot(); + + for (int i = 0; i < (int)pp->locallab.spots.size(); i++) { + if (i == spotIndex) { + newSpot = new LocallabParams::LocallabSpot(pp->locallab.spots.at(i)); + break; + } + } + + if (!newSpot) { + break; + } + + // Spot creation (initialization at currently selected spot) + r = new ControlSpotPanel::SpotRow(); + r->name = newSpot->name = newSpot->name + " - " + M("TP_LOCALLAB_DUPLSPOTNAME"); + r->isvisible = newSpot->isvisible; + + if (newSpot->shape == "ELI") { + r->shape = 0; + } else { + r->shape = 1; + } + + if (newSpot->spotMethod == "norm") { + r->spotMethod = 0; + } else { + r->spotMethod = 1; + } + + r->sensiexclu = newSpot->sensiexclu; + r->structexclu = newSpot->structexclu; + + if (newSpot->shapeMethod == "IND") { + r->shapeMethod = 0; + } else if (newSpot->shapeMethod == "SYM") { + r->shapeMethod = 1; + } else if (newSpot->shapeMethod == "INDSL") { + r->shapeMethod = 2; + } else { + r->shapeMethod = 3; + } + + // Calculate spot size and center position according to preview area + if (provider && !batchMode) { + provider->getImageSize(imW, imH); + provider->getPreviewCenterPos(prX, prY); + provider->getPreviewSize(prW, prH); + + if (imW && imH) { // Image loaded + // Spot center position computation + newSpot->centerX = rtengine::LIM(int(int((double)prX - (double)imW / 2.) * 2000. / (double)imW), -1000, 1000); + newSpot->centerY = rtengine::LIM(int(int((double)prY - (double)imH / 2.) * 2000. / (double)imH), -1000, 1000); + // Ellipse/rectangle size computation + newSpot->loc.at(0) = rtengine::LIM(int(((double)prW / 2. - 5.) * 2000. / (double)imW), 2, newSpot->loc.at(0)); + newSpot->loc.at(1) = rtengine::LIM(int(((double)prW / 2. - 5.) * 2000. / (double)imW), 2, newSpot->loc.at(1)); + newSpot->loc.at(2) = rtengine::LIM(int(((double)prH / 2. - 5.) * 2000. / (double)imH), 2, newSpot->loc.at(2)); + newSpot->loc.at(3) = rtengine::LIM(int(((double)prH / 2. - 5.) * 2000. / (double)imH), 2, newSpot->loc.at(3)); + } + } + + r->locX = newSpot->loc.at(0); + r->locXL = newSpot->loc.at(1); + r->locY = newSpot->loc.at(2); + r->locYT = newSpot->loc.at(3); + r->centerX = newSpot->centerX; + r->centerY = newSpot->centerY; + + r->circrad = newSpot->circrad; + + if (newSpot->qualityMethod == "enh") { + r->qualityMethod = 0; + } else { + r->qualityMethod = 1; + } + + r->transit = newSpot->transit; + r->transitweak = newSpot->transitweak; + r->transitgrad = newSpot->transitgrad; + r->feather = newSpot->feather; + r->struc = newSpot->struc; + r->thresh = newSpot->thresh; + r->iter = newSpot->iter; + r->balan = newSpot->balan; + r->balanh = newSpot->balanh; + r->colorde = newSpot->colorde; + r->colorscope = newSpot->colorscope; + r->avoid = newSpot->avoid; + r->blwh = newSpot->blwh; + r->recurs = newSpot->recurs; + r->laplac = newSpot->laplac; + r->deltae = newSpot->deltae; + r->scopemask = newSpot->scopemask; + r->shortc = newSpot->shortc; + r->lumask = newSpot->lumask; + r->savrest = newSpot->savrest; + + if (newSpot->complexMethod == "sim") { + r->complexMethod = 0; + } else if (newSpot->complexMethod == "mod") { + r->complexMethod = 1; + } else if (newSpot->complexMethod == "all") { + r->complexMethod = 2; + } + + if (newSpot->wavMethod == "D2") { + r->wavMethod = 0; + } else if (newSpot->wavMethod == "D4") { + r->wavMethod = 1; + } else if (newSpot->wavMethod == "D6") { + r->wavMethod = 2; + } else if (newSpot->wavMethod == "D10") { + r->wavMethod = 3; + } else if (newSpot->wavMethod == "D14") { + r->wavMethod = 4; + } + + expsettings->addControlSpot(r); + + // ProcParams update + pp->locallab.spots.push_back(*newSpot); + pp->locallab.selspot = pp->locallab.spots.size() - 1; + + + // New created spot selection + expsettings->setSelectedSpot(pp->locallab.selspot); + + // Update Locallab tools GUI with new created spot + disableListener(); + + for (auto tool : locallabTools) { + tool->read(pp, pedited); + } + + enableListener(); + + // Update tool list widget + toolNb = 0; + toollist->removeAllTool(); // Reset Locallab list firstly + + for (auto tool : locallabTools) { + toolNb++; + + if (!tool->isLocallabToolAdded()) { + toollist->addToolRow(tool->getToolName(), toolNb); + } + } + + // Update default values according to selected spot + setDefaults(pp, pedited); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab + + break; + + case (ControlSpotPanel::SpotAllVisibilityChanged): // Event when updating visibility of all spots + r = expsettings->getSpot(expsettings->getSelectedSpot()); + + // ProcParams update + for (size_t i = 0; i < pp->locallab.spots.size(); i++) { + pp->locallab.spots.at(i).isvisible = r->isvisible; + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab + + break; + + default: // Spot or locallab GUI updated + if (pp->locallab.spots.size() > 0) { + r = expsettings->getSpot(expsettings->getSelectedSpot()); + + // ProcParams update + if (pp->locallab.selspot < (int)pp->locallab.spots.size()) { + // Control spot settings + pp->locallab.spots.at(pp->locallab.selspot).name = r->name; + pp->locallab.spots.at(pp->locallab.selspot).isvisible = r->isvisible; + + if (r->shape == 0) { + pp->locallab.spots.at(pp->locallab.selspot).shape = "ELI"; + } else { + pp->locallab.spots.at(pp->locallab.selspot).shape = "RECT"; + } + + if (r->spotMethod == 0) { + pp->locallab.spots.at(pp->locallab.selspot).spotMethod = "norm"; + } else { + pp->locallab.spots.at(pp->locallab.selspot).spotMethod = "exc"; + } + + pp->locallab.spots.at(pp->locallab.selspot).sensiexclu = r->sensiexclu; + pp->locallab.spots.at(pp->locallab.selspot).structexclu = r->structexclu; + + if (r->shapeMethod == 0) { + pp->locallab.spots.at(pp->locallab.selspot).shapeMethod = "IND"; + } else if (r->shapeMethod == 1) { + pp->locallab.spots.at(pp->locallab.selspot).shapeMethod = "SYM"; + } else if (r->shapeMethod == 2) { + pp->locallab.spots.at(pp->locallab.selspot).shapeMethod = "INDSL"; + } else { + pp->locallab.spots.at(pp->locallab.selspot).shapeMethod = "SYMSL"; + } + + pp->locallab.spots.at(pp->locallab.selspot).loc.at(0) = r->locX; + pp->locallab.spots.at(pp->locallab.selspot).loc.at(1) = r->locXL; + pp->locallab.spots.at(pp->locallab.selspot).loc.at(2) = r->locY; + pp->locallab.spots.at(pp->locallab.selspot).loc.at(3) = r->locYT; + pp->locallab.spots.at(pp->locallab.selspot).centerX = r->centerX; + pp->locallab.spots.at(pp->locallab.selspot).centerY = r->centerY; + pp->locallab.spots.at(pp->locallab.selspot).circrad = r->circrad; + + if (r->qualityMethod == 0) { + pp->locallab.spots.at(pp->locallab.selspot).qualityMethod = "enh"; + } else { + pp->locallab.spots.at(pp->locallab.selspot).qualityMethod = "enhden"; + } + + pp->locallab.spots.at(pp->locallab.selspot).transit = r->transit; + pp->locallab.spots.at(pp->locallab.selspot).transitweak = r->transitweak; + pp->locallab.spots.at(pp->locallab.selspot).transitgrad = r->transitgrad; + pp->locallab.spots.at(pp->locallab.selspot).feather = r->feather; + pp->locallab.spots.at(pp->locallab.selspot).struc = r->struc; + pp->locallab.spots.at(pp->locallab.selspot).thresh = r->thresh; + pp->locallab.spots.at(pp->locallab.selspot).iter = r->iter; + pp->locallab.spots.at(pp->locallab.selspot).balan = r->balan; + pp->locallab.spots.at(pp->locallab.selspot).balanh = r->balanh; + pp->locallab.spots.at(pp->locallab.selspot).colorde = r->colorde; + pp->locallab.spots.at(pp->locallab.selspot).colorscope = r->colorscope; + pp->locallab.spots.at(pp->locallab.selspot).avoid = r->avoid; + pp->locallab.spots.at(pp->locallab.selspot).blwh = r->blwh; + pp->locallab.spots.at(pp->locallab.selspot).recurs = r->recurs; + pp->locallab.spots.at(pp->locallab.selspot).laplac = r->laplac; + pp->locallab.spots.at(pp->locallab.selspot).deltae = r->deltae; + pp->locallab.spots.at(pp->locallab.selspot).scopemask = r->scopemask; + pp->locallab.spots.at(pp->locallab.selspot).shortc = r->shortc; + pp->locallab.spots.at(pp->locallab.selspot).lumask = r->lumask; + pp->locallab.spots.at(pp->locallab.selspot).savrest = r->savrest; + + if (r->complexMethod == 0) { + pp->locallab.spots.at(pp->locallab.selspot).complexMethod = "sim"; + } else if (r->complexMethod == 1) { + pp->locallab.spots.at(pp->locallab.selspot).complexMethod = "mod"; + } else if (r->complexMethod == 2) { + pp->locallab.spots.at(pp->locallab.selspot).complexMethod = "all"; + } + + if (r->wavMethod == 0) { + pp->locallab.spots.at(pp->locallab.selspot).wavMethod = "D2"; + } else if (r->wavMethod == 1) { + pp->locallab.spots.at(pp->locallab.selspot).wavMethod = "D4"; + } else if (r->wavMethod == 2) { + pp->locallab.spots.at(pp->locallab.selspot).wavMethod = "D6"; + } else if (r->wavMethod == 3) { + pp->locallab.spots.at(pp->locallab.selspot).wavMethod = "D10"; + } else if (r->wavMethod == 4) { + pp->locallab.spots.at(pp->locallab.selspot).wavMethod = "D14"; + } + } + + for (auto tool : locallabTools) { + tool->write(pp, pedited); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab + } + } +} + +/* + * Note: + * By default, this function is called when a new image/profile is loaded (after read function). In this case, + * if there is at least one spot, default values are set to selected spot ones. + * To keep having default values according to selected spot, this function shall also be called in the following + * situations (after having called write function for controlspotpanel): + * - After spot creation + * - After spot deletion + * - After spot selection + * - After spot duplication + */ +void Locallab::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + // Set default values in spot panel control + expsettings->setDefaults(defParams, pedited); + + // Set default values in Locallab tools + for (auto tool : locallabTools) { + tool->setDefaults(defParams, pedited); + } +} + +void Locallab::setListener(ToolPanelListener* tpl) +{ + this->listener = tpl; + + // Set listener for spot control panel + expsettings->setListener(tpl); + + // Set listener for locallab tools + for (auto tool : locallabTools) { + tool->setListener(tpl); + } +} + +void Locallab::minmaxChanged(const std::vector &minmax, int selspot) +{ + // Saving transmitted min/max data + retiMinMax = minmax; + + // Update Locallab Retinex tool min/max + if (selspot < (int)retiMinMax.size()) { + const double cdma = retiMinMax.at(selspot).cdma; + const double cdmin = retiMinMax.at(selspot).cdmin; + const double mini = retiMinMax.at(selspot).mini; + const double maxi = retiMinMax.at(selspot).maxi; + const double Tmean = retiMinMax.at(selspot).Tmean; + const double Tsigma = retiMinMax.at(selspot).Tsigma; + const double Tmin = retiMinMax.at(selspot).Tmin; + const double Tmax = retiMinMax.at(selspot).Tmax; + + expreti->updateMinMax(cdma, cdmin, mini, maxi, Tmean, Tsigma, Tmin, Tmax); + } +} + +void Locallab::logencodChanged(const float blackev, const float whiteev, const float sourceg, const float targetg) +{ + // Update Locallab Log Encoding accordingly + explog->updateAutocompute(blackev, whiteev, sourceg, targetg); +} + +void Locallab::refChanged(const std::vector &ref, int selspot) +{ + // Saving transmitted mask background data + maskBackRef = ref; + + // Update locallab tools mask background + if (selspot < (int)maskBackRef.size()) { + const double huer = maskBackRef.at(selspot).huer; + const double lumar = maskBackRef.at(selspot).lumar; + const double chromar = maskBackRef.at(selspot).chromar; + + for (auto tool : locallabTools) { + tool->refChanged(huer, lumar, chromar); + } + } +} + +void Locallab::resetMaskVisibility() +{ + // Indicate to spot control panel that no more mask preview is active + expsettings->setMaskPrevActive(false); + + // Reset deltaE preview + expsettings->resetDeltaEPreview(); + + // Reset mask preview for all Locallab tools + for (auto tool : locallabTools) { + tool->resetMaskView(); + } +} + +Locallab::llMaskVisibility Locallab::getMaskVisibility() const +{ + // Get deltaE preview state + const bool prevDeltaE = expsettings->isDeltaEPrevActive(); + + // Get mask preview from Locallab tools + int colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask; + + for (auto tool : locallabTools) { + tool->getMaskView(colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask); + } + + // Indicate to spot control panel if one mask preview is active + const bool isMaskActive = (colorMask == 0) || (colorMaskinv == 0) || (expMask == 0) || (expMaskinv == 0) || + (shMask == 0) || (shMaskinv == 0) || (vibMask == 0) || (softMask == 0) || + (blMask == 0) || (tmMask == 0) || (retiMask == 0) || (sharMask == 0) || + (lcMask == 0) || (cbMask == 0); + expsettings->setMaskPrevActive(isMaskActive); + + return {prevDeltaE, colorMask, colorMaskinv, expMask, expMaskinv, shMask, shMaskinv, vibMask, softMask, blMask, tmMask, retiMask, sharMask, lcMask, cbMask}; +} + +void Locallab::resetshowPressed() +{ + // Raise event to reset mask + if (listener) { + listener->panelChanged(Evlocallabshowreset, ""); + } +} + +void Locallab::setEditProvider(EditDataProvider * provider) +{ + expsettings->setEditProvider(provider); +} + +void Locallab::subscribe() +{ + expsettings->subscribe(); +} + +void Locallab::unsubscribe() +{ + expsettings->unsubscribe(); +} + +void Locallab::enabledChanged() +{ + if (listener) { + if (getEnabled()) { + listener->panelChanged(EvlocallabEnabled, M("GENERAL_ENABLED")); + } else { + listener->panelChanged(EvlocallabEnabled, M("GENERAL_DISABLED")); + } + } +} + +void Locallab::autoOpenCurve() +{ + // TODO Actually autoOpenCurve only considers linearity state of selected spot curve +} + +void Locallab::foldAllButOne(LocallabTool* except) +{ + for (auto tool : locallabTools) { + if (tool != except) { + // All other tool expanders are fold + tool->setExpanded(false); + } else { + // If fold, selected tool expander is unfold + if (!tool->getExpanded()) { + tool->setExpanded(true); + } + } + } +} + +void Locallab::openAllTools() +{ + // Set default visibility for settings panel sub-expanders + expsettings->setDefaultExpanderVisibility(); + + for (auto tool : locallabTools) { + tool->setExpanded(true); + + // Set default visibility for tool sub-expanders + tool->setDefaultExpanderVisibility(); + } +} + +void Locallab::updateShowtooltipVisibility(bool showtooltip) +{ + for (auto tool : locallabTools) { + tool->updateAdviceTooltips(showtooltip); + } +} + +void Locallab::addTool(Gtk::Box* where, LocallabTool* tool) +{ + tool->getExpander()->setLevel(3); + where->pack_start(*tool->getExpander(), false, false); + locallabTools.push_back(tool); + tool->setLocallabToolListener(this); +} + +void Locallab::setParamEditable(bool cond) +{ + // Update params editable state for controlspotpanel + expsettings->setParamEditable(cond); // TODO Move this code to controlspotpanel.cc when there is zero spot + + // Enable/disable possibility to add Locallab tool + toollist->set_sensitive(cond); + + // Remove all Locallab tool (without raising event) only if cond is false + if (!cond) { + for (auto tool : locallabTools) { + tool->removeLocallabTool(false); + } + } +} + +void Locallab::resetToolMaskView() +{ + // Reset mask view GUI for all other Locallab tools + for (auto tool : locallabTools) { + tool->resetMaskView(); + } +} + +void Locallab::resetOtherMaskView(LocallabTool* current) +{ + // Reset deltaE preview + expsettings->resetDeltaEPreview(); + + // Reset mask view GUI for all other Locallab tools except current + for (auto tool : locallabTools) { + if (tool != current) { + tool->resetMaskView(); + } + } +} + +void Locallab::toolRemoved(LocallabTool* current) +{ + // Update tool list widget according to removed tool + int toolNb = 0; + + for (auto tool : locallabTools) { + toolNb++; + + if (tool == current) { + toollist->addToolRow(tool->getToolName(), toolNb); + } + } +} + +void Locallab::locallabToolToAdd(const Glib::ustring &toolname) +{ + for (auto tool : locallabTools) { + if (tool->getToolName() == toolname) { + // Set expanders visibility default state when adding tool + tool->setExpanded(true); + tool->setDefaultExpanderVisibility(); + + // Add tool + tool->addLocallabTool(true); + } + } +} diff --git a/rtgui/locallab.h b/rtgui/locallab.h new file mode 100644 index 000000000..8d68576f4 --- /dev/null +++ b/rtgui/locallab.h @@ -0,0 +1,209 @@ +/* + * 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 . + * 2017 Jacques Desmis + * 2019 Pierre Cabrera + */ +#ifndef _LOCALLAB_H_ +#define _LOCALLAB_H_ + +#pragma once + +#include "controlspotpanel.h" +#include "locallabtools.h" + +/* ==== LocallabToolListListener ==== */ +class LocallabToolList; +class LocallabToolListListener +{ +public: + LocallabToolListListener() {}; + virtual ~LocallabToolListListener() {}; + + virtual void locallabToolToAdd(const Glib::ustring &toolname) = 0; +}; + +/* ==== LocallabToolList ==== */ +class LocallabToolList: + public Gtk::VBox +{ +private: + // Tree model to manage ComboBox rows + class ToolRow: + public Gtk::TreeModel::ColumnRecord + { + public: + Gtk::TreeModelColumn id; + Gtk::TreeModelColumn name; + + ToolRow() + { + add(id); + add(name); + } + }; + + // Tool list GUI widgets + MyComboBox* const list; + sigc::connection listConn; + ToolRow toolRow; + Glib::RefPtr listTreeModel; + + // Tool list listener + LocallabToolListListener* listListener; + +public: + LocallabToolList(); + + // Setter for tool list listener + void setLocallabToolListListener(LocallabToolListListener* ltll) + { + listListener = ltll; + } + + // Tool list management function + void addToolRow(const Glib::ustring &toolname, const int id); + void removeToolRow(const Glib::ustring &toolname); + void removeAllTool(); + +private: + // Tool list event management function + void toolRowSelected(); +}; + +/* ==== Locallab ==== */ +class Locallab : + public ToolParamBlock, + public FoldableToolPanel, + public rtengine::LocallabListener, + public ControlPanelListener, + public LocallabToolListener, + public LocallabToolListListener +{ +private: + // Spot control panel widget + ControlSpotPanel* const expsettings; + + // Tool list widget + LocallabToolList* const toollist; + + // Locallab tool widgets + LocallabColor* const expcolor; + LocallabExposure* const expexpose; + LocallabShadow* const expshadhigh; + LocallabVibrance* const expvibrance; + LocallabSoft* const expsoft; + LocallabBlur* const expblur; + LocallabTone* const exptonemap; + LocallabRetinex* const expreti; + LocallabSharp* const expsharp; + LocallabContrast* const expcontrast; + LocallabCBDL* const expcbdl; + LocallabLog* const explog; + + std::vector locallabTools; + + // Locallab tools mask background management data + std::vector retiMinMax; + + // Locallab tools mask background management data + std::vector maskBackRef; + + // Other widgets + Gtk::Button* const resetshowButton; + +public: + Locallab(); + + // FoldableToolPanel management functions + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void setListener(ToolPanelListener* tpl) override; + + // Locallab Retinex tool min/man management function + void minmaxChanged(const std::vector &minmax, int selspot) override; + + // Locallab Log Encoding autocompute function + void logencodChanged(const float blackev, const float whiteev, const float sourceg, const float targetg) override; + + // Locallab tools mask background management function + void refChanged(const std::vector &ref, int selspot) override; + + // Mask visibility management functions + struct llMaskVisibility { + bool previewDeltaE; + int colorMask; + int colorMaskinv; + int expMask; + int expMaskinv; + int SHMask; + int SHMaskinv; + int vibMask; + int softMask; + int blMask; + int tmMask; + int retiMask; + int sharMask; + int lcMask; + int cbMask; + }; + + void resetMaskVisibility(); + llMaskVisibility getMaskVisibility() const; + + // Other widgets event functions + void resetshowPressed(); + + // EditProvider management function + void setEditProvider(EditDataProvider* provider) override; + void subscribe(); + void unsubscribe(); + + // FoldableToolPanel event function + void enabledChanged() override; + + // Curve management function + void autoOpenCurve() override; + + // Locallab tools expanders management functions + void foldAllButOne(LocallabTool* except); + void openAllTools(); + + // Locallab tools advice tooltips management function + void updateShowtooltipVisibility(bool showtooltip); + +private: + // Locallab tools management functions + void addTool(Gtk::Box* where, LocallabTool* tool); + + // Locallab GUI management function + void setParamEditable(bool cond); + + // ControlSpotListener function + void resetToolMaskView() override; + + // LocallabToolListener function + void resetOtherMaskView(LocallabTool* current) override; + void toolRemoved(LocallabTool* current) override; + + // LocallabToolListListener function + void locallabToolToAdd(const Glib::ustring &toolname) override; +}; + +#endif diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc new file mode 100644 index 000000000..78dfacbbe --- /dev/null +++ b/rtgui/locallabtools.cc @@ -0,0 +1,6393 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath frame + * + * + * 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 . + * 2019 Pierre Cabrera + */ +#include "locallabtools.h" + +#include "options.h" +#include "../rtengine/procparams.h" +#include "locallab.h" +#include "thresholdadjuster.h" +#include "rtimage.h" +#include "../rtengine/color.h" + +#define MINRAD 1.5 +#define MAXRAD 10000 +#define CENTERRAD 100 +#define MINCHRO 0. +#define MAXCHRO 150. +#define MAXCHROCC 100. +#define MINEXP -1.5 +#define MAXEXP 1.5 + +using namespace rtengine; +using namespace procparams; + +extern Options options; +static double blurSlider2radius(double sval) +{ + // Slider range: 0 - 1000 + double radius; + + if (sval <= 100) { + // Linear below center-temp + radius = MINRAD + (sval / 100.0) * (CENTERRAD - MINRAD); + } else { + const double slope = (double)(CENTERRAD - MINRAD) / (MAXRAD - CENTERRAD); + double x = (sval - 100) / 100; // x range: 0 - 1 + double y = x * slope + (1.0 - slope) * pow(x, 4.0); + radius = CENTERRAD + y * (MAXRAD - CENTERRAD); + } + + if (radius < MINRAD) { + radius = MINRAD; + } + + if (radius > MAXRAD) { + radius = MAXRAD; + } + + return radius; +} + +static double blurRadius2Slider(double radius) +{ + double sval; + + if (radius <= CENTERRAD) { + sval = ((radius - MINRAD) / (CENTERRAD - MINRAD)) * 100.0; + } else { + const double slope = (double)(CENTERRAD - MINRAD) / (MAXRAD - CENTERRAD); + const double y = (radius - CENTERRAD) / (MAXRAD - CENTERRAD); + double x = pow(y, 0.25); // Rough guess of x, will be a little lower + double k = 0.1; + bool add = true; + + // The y=f(x) function is a mess to invert, therefore we have this trial-refinement loop instead. + // From tests, worst case is about 20 iterations, i.e. no problem + for (;;) { + double y1 = x * slope + (1.0 - slope) * pow(x, 4.0); + + if (100. * fabs(y1 - y) < 0.1) { + break; + } + + if (y1 < y) { + if (!add) { + k /= 2; + } + + x += k; + add = true; + } else { + if (add) { + k /= 2; + } + + x -= k; + add = false; + } + } + + sval = 100.0 + x * 100.0; + } + + if (sval < 0.) { + sval = 0.; + } + + if (sval > 10000.) { + sval = 10000.; + } + + return sval; +} + +/* ==== LocallabTool ==== */ +LocallabTool::LocallabTool(Gtk::Box* content, Glib::ustring toolName, Glib::ustring UILabel, bool need11, bool needMode): + ToolPanel(toolName, need11), + + // LocallabTool parameters + needMode(needMode), + isLocActivated(false), + locToolListener(nullptr), + + // LocallabTool generic widgets + complexity(Gtk::manage(new MyComboBoxText())) +{ + // Create expander title bar + Gtk::HBox* const titleBox = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const titleLabel = Gtk::manage(new Gtk::Label()); + titleLabel->set_markup(Glib::ustring("") + escapeHtmlChars(UILabel) + Glib::ustring("")); + titleLabel->set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + titleBox->pack_start(*titleLabel, Gtk::PACK_EXPAND_WIDGET, 0); + + Gtk::EventBox* const removeEvBox = Gtk::manage(new Gtk::EventBox()); // Glue to manage mouse clicking event on remove image + removeEvBox->set_can_focus(false); + removeEvBox->set_above_child(false); // To have priority over expander title bar when mouse clicking on remove image + removeEvBox->signal_button_release_event().connect(sigc::mem_fun(this, &LocallabTool::on_remove_change)); + RTImage* const removeImage = Gtk::manage(new RTImage("cancel-small.png")); + removeEvBox->add(*removeImage); + titleBox->pack_end(*removeEvBox, Gtk::PACK_SHRINK, 4); + + if (needMode) { + complexity->append(M("TP_LOCALLAB_MODE_EXPERT")); + complexity->append(M("TP_LOCALLAB_MODE_NORMAL")); + complexity->set_active(0); + complexity->setPreferredWidth(100, -1); + complexityConn = complexity->signal_changed().connect(sigc::mem_fun(*this, &LocallabTool::complexityModeChanged)); + titleBox->pack_end(*complexity, Gtk::PACK_SHRINK, 2); + } + + Gtk::VSeparator* const separator = Gtk::manage(new Gtk::VSeparator()); + titleBox->pack_end(*separator, Gtk::PACK_SHRINK, 0); + + if (need100Percent) { + RTImage* const titleImage = Gtk::manage(new RTImage("one-to-one-small.png")); + titleImage->set_tooltip_text(M("TP_GENERAL_11SCALE_TOOLTIP")); + titleBox->pack_end(*titleImage, Gtk::PACK_SHRINK, 0); + } + + exp = Gtk::manage(new MyExpander(true, titleBox)); + exp->signal_button_release_event().connect_notify(sigc::mem_fun(this, &LocallabTool::foldThemAll)); + enaExpConn = exp->signal_enabled_toggled().connect(sigc::mem_fun(*this, &LocallabTool::enabledChanged)); + + ToolParamBlock* const totalBox = Gtk::manage(new ToolParamBlock()); + + // Create panel for specific widget tools + totalBox->pack_start(*content, Gtk::PACK_SHRINK, 0); + exp->add(*totalBox, false); +} + +LocallabTool::~LocallabTool() +{ + idle_register.destroy(); +} + +void LocallabTool::addLocallabTool(bool raiseEvent) +{ + exp->set_visible(true); + + // Raise event if required + if (raiseEvent) { // Note: Event is only raised when a tool is added by user + // By default, activate newly added tool + enaExpConn.block(true); + exp->setEnabled(true); + enaExpConn.block(false); + + if (needMode) { + // Set complexity mode according to chosen default one + complexityConn.block(true); + complexity->set_active(options.complexity); + complexityConn.block(false); + + // Update GUI accordingly + if (complexity->get_active_row_number() == Normal) { + convertParamToNormal(); + updateGUIToMode(Normal); + } else { + updateGUIToMode(Expert); + } + } + + if (listener) { + listener->panelChanged(EvlocallabToolAdded, + toolName + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabTool::removeLocallabTool(bool raiseEvent) +{ + exp->set_visible(false); + + // Inform LocallabToolListener to update Locallab tools list + if (locToolListener) { + locToolListener->toolRemoved(this); + } + + if (exp->getEnabled() || isMaskViewActive()) { + // Disable tool while removing it + disableListener(); + exp->setEnabled(false); + enableListener(); + // Note: Mask views are all reset when removing tool (in toolpanelcoord.cc) + + // Raise event if required refreshing image + if (raiseEvent && listener) { + listener->panelChanged(EvlocallabToolRemovedWithRefresh, + toolName + " (" + escapeHtmlChars(spotName) + ")"); + } + } else { + // Raise event if required without refreshing image + if (raiseEvent && listener) { + listener->panelChanged(EvlocallabToolRemovedWithoutRefresh, + toolName + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +bool LocallabTool::isLocallabToolAdded() +{ + return exp->get_visible(); +} + +void LocallabTool::refChanged(const double huer, const double lumar, const double chromar) +{ + // Hue reference normalization (between 0 and 1) + double normHuer = huer; + float h = Color::huelab_to_huehsv2(normHuer); + h += 1.f / 6.f; + + if (h > 1.f) { + h -= 1.f; + } + + normHuer = h; + + // Luma reference normalization (between 0 and 1) + const double normLumar = lumar / 100.f; + + // Chroma reference normalization (between 0 and 1) + const double normChromar = chromar / 137.4f; + + // Update mask curve backgrounds + updateMaskBackground(normChromar, normLumar, normHuer); +} + +void LocallabTool::colorForValue(double valX, double valY, enum ColorCaller::ElemType elemType, int callerId, ColorCaller* caller) +{ + float R = 0.f; + float G = 0.f; + float B = 0.f; + float x; + + if (elemType == ColorCaller::CCET_VERTICAL_BAR) { + valY = 0.5; + } + + switch (callerId) { + case 1: // Mask CC shape (bottom bar) + Color CC/LC shape (left bar) + Color::hsv2rgb01(float(valY), float(valX), 0.5f, R, G, B); + + break; + + case 2: // Mask HH shape (main curve and bottom bar) + x = valX - 1.f / 6.f; + + if (x < 0.f) { + x += 1.f; + } + + x = log2lin(x, 3.f); + Color::hsv2rgb01(x, 0.5f, 0.5f, R, G, B); + + break; + + case 3: // Color LH/HH shape (main curve) + Color::hsv2rgb01(float (valX), float (valY), 0.5f, R, G, B); + + break; + + case 4: // Color CC/LC shape (bottom bar) + const float value = (1.f - 0.7f) * float (valX) + 0.7f; + // Whole hue range + // Y axis / from 0.15 up to 0.75 (arbitrary values; was 0.45 before) + Color::hsv2rgb01(float (valY), float (valX), value, R, G, B); + } + + caller->ccRed = double (R); + caller->ccGreen = double (G); + caller->ccBlue = double (B); +} + +void LocallabTool::disableListener() +{ + ToolPanel::disableListener(); + + enaExpConn.block(true); + + if (needMode) { + complexityConn.block(true); + } +} +void LocallabTool::enableListener() +{ + ToolPanel::enableListener(); + + enaExpConn.block(false); + + if (needMode) { + complexityConn.block(false); + } +} + +bool LocallabTool::on_remove_change(GdkEventButton* event) +{ + if (event->button == GDK_BUTTON_PRIMARY) { + // Remove Locallab tool raising an event + removeLocallabTool(true); + } + + return true; // No event propagation further (to avoid closing expander when mouse clicking on remove image) +} + +void LocallabTool::foldThemAll(GdkEventButton* event) +{ + if (event->button == GDK_BUTTON_SECONDARY) { + if (locToolListener) { + (static_cast(locToolListener))->foldAllButOne(this); + } + } +} + +void LocallabTool::complexityModeChanged() +{ + if (complexity->get_active_row_number() == Normal) { // New selected mode is Normal one + // Convert tool widget parameters + convertParamToNormal(); + + // Update GUI based on new mode + updateGUIToMode(Normal); + + // Raise event with refreshing + if (listener) { + listener->panelChanged(EvlocallabcomplexityWithRefresh, + M("TP_LOCALLAB_MODE_NORMAL") + " (" + escapeHtmlChars(spotName) + ")"); + } + } else { // New selected mode is Expert one + // Update GUI based on new mode + updateGUIToMode(Expert); + + // Raise event without refreshing + if (listener) { + listener->panelChanged(EvlocallabcomplexityWithoutRefresh, + M("TP_LOCALLAB_MODE_EXPERT") + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +/* ==== LocallabColor ==== */ +LocallabColor::LocallabColor(): + LocallabTool(this, M("TP_LOCALLAB_COLOR_TOOLNAME"), M("TP_LOCALLAB_COFR"), false), + + // Color & Light specific widgets + curvactiv(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_CURV")))), + lightness(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LIGHTNESS"), -100, 500, 1, 0))), + contrast(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTRAST"), -100, 100, 1, 0))), + chroma(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMA"), -100, 150, 1, 0))), + gridFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABGRID")))), + labgrid(Gtk::manage(new LabGrid(EvLocallabLabGridValue, M("TP_LOCALLAB_LABGRID_VALUES")))), + gridMethod(Gtk::manage(new MyComboBoxText())), + strengthgrid(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRGRID"), 0, 100, 1, 30))), + sensi(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 15))), + structcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUCCOL1"), 0, 100, 1, 0))), + blurcolde(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURDE"), 2, 100, 1, 5))), + softradiuscol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + invers(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))), + expgradcol(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPGRAD")))), + strcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTRLUM"), -4., 4., 0.05, 0.))), + strcolab(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTRCHRO"), -6., 6., 0.05, 0.))), + strcolh(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTRHUE"), -6., 6., 0.05, 0.))), + angcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))), + expcurvcol(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPCURV")))), + labqualcurv(Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_QUALCURV_METHOD") + ":"))), + qualitycurveMethod(Gtk::manage(new MyComboBoxText())), + llCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_LUM"))), + llshape(static_cast(llCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + ccshape(static_cast(llCurveEditorG->addCurve(CT_Diagonal, "C(C)"))), + clCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_CH"))), + clshape(static_cast(clCurveEditorG->addCurve(CT_Diagonal, "C(L)"))), + lcshape(static_cast(clCurveEditorG->addCurve(CT_Diagonal, "L(C)"))), + HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), + LHshape(static_cast(HCurveEditorG->addCurve(CT_Flat, "L(H)", nullptr, false, true))), + H2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), + HHshape(static_cast(H2CurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))), + rgbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_RGB"))), + toneMethod(Gtk::manage(new MyComboBoxText())), + rgbshape(static_cast(rgbCurveEditorG->addCurve(CT_Diagonal, "", toneMethod))), + special(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_SPECIAL")))), + expmaskcol1(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWC1")))), + merMethod(Gtk::manage(new MyComboBoxText())), + mask7(Gtk::manage(new ToolParamBlock())), + mergecolMethod(Gtk::manage(new MyComboBoxText())), + mercol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_MERDCOL"), 0.0, 100.0, 0.5, 18.))), + opacol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_OPACOL"), 0.0, 100.0, 0.5, 60.))), + conthrcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTTHR"), 0.0, 100.0, 0.5, 0.))), + gridmerFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABGRIDMERG")))), + labgridmerg(Gtk::manage(new LabGrid(EvLocallabLabGridmergValue, M("TP_LOCALLAB_LABGRID_VALUES"), false))), + merlucol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_MERLUCOL"), 0.0, 100.0, 0.5, 32., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + expmaskcol(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWC")))), + showmaskcolMethod(Gtk::manage(new MyComboBoxText())), + showmaskcolMethodinv(Gtk::manage(new MyComboBoxText())), + enaColorMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + maskCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), + CCmaskshape(static_cast(maskCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskshape(static_cast(maskCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskshape(static_cast(maskCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + struFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABSTRUM")))), + strumaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUMASKCOL"), 0., 200., 0.1, 0.))), + toolcol(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), + blurFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LABBLURM")))), + fftColorMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTCOL_MASK")))), + contcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTCOL"), 0., 200., 0.5, 0.))), + blurcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCOL"), 0.2, 100., 0.5, 0.2))), + blendmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + shadmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), + maskHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), + HHhmaskshape(static_cast(maskHCurveEditorG->addCurve(CT_Flat, "H(H)", nullptr, false, true))), + mask2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskshape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + mask2CurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmaskcolshapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + csThresholdcol(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) +{ + float R, G, B; + + std::vector six_shape; + + for (int i = 0; i < 6; i++) { + const float x = static_cast(i) * (1.f / 6.f); + Color::hsv2rgb01(x, 0.5f, 0.5f, R, G, B); + six_shape.emplace_back(x, R, G, B); + } + + const LocallabParams::LocallabSpot defSpot; + + // Parameter Color & Light specific widgets + curvactivConn = curvactiv->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::curvactivChanged)); + + lightness->setAdjusterListener(this); + + contrast->setAdjusterListener(this); + + chroma->setAdjusterListener(this); + + gridFrame->set_label_align(0.025, 0.5); + + gridMethod->append(M("TP_LOCALLAB_GRIDONE")); + gridMethod->append(M("TP_LOCALLAB_GRIDTWO")); + gridMethod->set_active(0); + gridMethodConn = gridMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::gridMethodChanged)); + + strengthgrid->setAdjusterListener(this); + + sensi->setAdjusterListener(this); + + structcol->setAdjusterListener(this); + + blurcolde->setAdjusterListener(this); + + softradiuscol->setLogScale(10, 0); + softradiuscol->setAdjusterListener(this); + + inversConn = invers->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::inversChanged)); + invers->set_tooltip_text(M("TP_LOCALLAB_INVERS_TOOLTIP")); + + setExpandAlignProperties(expgradcol, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + strcol->setAdjusterListener(this); + + strcolab->setAdjusterListener(this); + strcolab->set_tooltip_text(M("TP_LOCALLAB_GRADSTRAB_TOOLTIP")); + + strcolh->setAdjusterListener(this); + strcolh->set_tooltip_text(M("TP_LOCALLAB_GRADSTRHUE_TOOLTIP")); + + angcol->setAdjusterListener(this); + + setExpandAlignProperties(expcurvcol, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + qualitycurveMethod->append(M("TP_LOCALLAB_CURVNONE")); + qualitycurveMethod->append(M("TP_LOCALLAB_CURVCURR")); + qualitycurveMethod->set_active(0); + qualitycurveMethodConn = qualitycurveMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::qualitycurveMethodChanged)); + + llCurveEditorG->setCurveListener(this); + + llshape->setResetCurve(DiagonalCurveType(defSpot.llcurve.at(0)), defSpot.llcurve); + llshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + llshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + llshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + ccshape->setResetCurve(DiagonalCurveType(defSpot.cccurve.at(0)), defSpot.cccurve); + ccshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + ccshape->setBottomBarColorProvider(this, 4); + ccshape->setLeftBarColorProvider(this, 1); + + llCurveEditorG->curveListComplete(); + + clCurveEditorG->setCurveListener(this); + + clshape->setResetCurve(DiagonalCurveType(defSpot.clcurve.at(0)), defSpot.clcurve); + clshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + clshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + clshape->setLeftBarColorProvider(this, 1); + + lcshape->setResetCurve(DiagonalCurveType(defSpot.lccurve.at(0)), defSpot.lccurve); + lcshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + lcshape->setBottomBarColorProvider(this, 4); + lcshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + clCurveEditorG->curveListComplete(); + + HCurveEditorG->setCurveListener(this); + + LHshape->setIdentityValue(0.); + LHshape->setResetCurve(FlatCurveType(defSpot.LHcurve.at(0)), defSpot.LHcurve); + LHshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + LHshape->setCurveColorProvider(this, 3); + LHshape->setBottomBarBgGradient(six_shape); + + HCurveEditorG->curveListComplete(); + + H2CurveEditorG->setCurveListener(this); + + HHshape->setIdentityValue(0.); + HHshape->setResetCurve(FlatCurveType(defSpot.HHcurve.at(0)), defSpot.HHcurve); + HHshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + HHshape->setCurveColorProvider(this, 3); + HHshape->setBottomBarBgGradient(six_shape); + + H2CurveEditorG->curveListComplete(); + + rgbCurveEditorG->setCurveListener(this); + + toneMethod->append(M("TP_EXPOSURE_TCMODE_STANDARD")); + toneMethod->append(M("TP_EXPOSURE_TCMODE_WEIGHTEDSTD")); + toneMethod->append(M("TP_EXPOSURE_TCMODE_LUMINANCE")); + toneMethod->append(M("TP_EXPOSURE_TCMODE_FILMLIKE")); + toneMethod->set_active(0); + toneMethodConn = toneMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::toneMethodChanged)); + + rgbshape->setResetCurve(DiagonalCurveType(defSpot.rgbcurve.at(0)), defSpot.rgbcurve); + rgbshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + rgbshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + rgbshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + rgbCurveEditorG->curveListComplete(); + + specialConn = special->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::specialChanged)); + + setExpandAlignProperties(expmaskcol1, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + merMethod->append(M("TP_LOCALLAB_MRONE")); +// merMethod->append(M("TP_LOCALLAB_MRTWO")); + merMethod->append(M("TP_LOCALLAB_MRTHR")); + merMethod->append(M("TP_LOCALLAB_MRFOU")); + merMethod->append(M("TP_LOCALLAB_MRFIV")); + merMethod->set_active(0); + merMethodConn = merMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::merMethodChanged)); + + mergecolMethod->append(M("TP_LOCALLAB_MERONE")); + mergecolMethod->append(M("TP_LOCALLAB_MERTWO")); + mergecolMethod->append(M("TP_LOCALLAB_MERTHR")); + mergecolMethod->append(M("TP_LOCALLAB_MERFOU")); + mergecolMethod->append(M("TP_LOCALLAB_MERFIV")); + mergecolMethod->append(M("TP_LOCALLAB_MERSIX")); + mergecolMethod->append(M("TP_LOCALLAB_MERSEV")); + mergecolMethod->append(M("TP_LOCALLAB_MERSEV0")); + mergecolMethod->append(M("TP_LOCALLAB_MERSEV1")); + mergecolMethod->append(M("TP_LOCALLAB_MERSEV2")); + mergecolMethod->append(M("TP_LOCALLAB_MERHEI")); + mergecolMethod->append(M("TP_LOCALLAB_MERNIN")); + mergecolMethod->append(M("TP_LOCALLAB_MERTEN")); + mergecolMethod->append(M("TP_LOCALLAB_MERELE")); + mergecolMethod->append(M("TP_LOCALLAB_MERTWE")); + mergecolMethod->append(M("TP_LOCALLAB_MERTHI")); + mergecolMethod->append(M("TP_LOCALLAB_MERFOR")); + mergecolMethod->append(M("TP_LOCALLAB_MERHUE")); + mergecolMethod->append(M("TP_LOCALLAB_MERSAT")); + mergecolMethod->append(M("TP_LOCALLAB_MERCOL")); + mergecolMethod->append(M("TP_LOCALLAB_MERLUM")); + mergecolMethod->set_active(0); + mergecolMethodConn = mergecolMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::mergecolMethodChanged)); + + mercol->setAdjusterListener(this); + + opacol->setAdjusterListener(this); + + conthrcol->setAdjusterListener(this); + + merlucol->setAdjusterListener(this); + + setExpandAlignProperties(expmaskcol, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmaskcolMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskcolMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskcolMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskcolMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskcolMethod->append(M("TP_LOCALLAB_SHOWSTRUC")); + showmaskcolMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskcolMethod->set_active(0); + showmaskcolMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskcolMethodConn = showmaskcolMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::showmaskcolMethodChanged)); + + showmaskcolMethodinv->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskcolMethodinv->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskcolMethodinv->set_active(0); + showmaskcolMethodinv->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskcolMethodConninv = showmaskcolMethodinv->signal_changed().connect(sigc::mem_fun(*this, &LocallabColor::showmaskcolMethodChangedinv)); + + enaColorMaskConn = enaColorMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::enaColorMaskChanged)); + + maskCurveEditorG->setCurveListener(this); + + CCmaskshape->setIdentityValue(0.); + CCmaskshape->setResetCurve(FlatCurveType(defSpot.CCmaskcurve.at(0)), defSpot.CCmaskcurve); + CCmaskshape->setBottomBarColorProvider(this, 1); + + LLmaskshape->setIdentityValue(0.); + LLmaskshape->setResetCurve(FlatCurveType(defSpot.LLmaskcurve.at(0)), defSpot.LLmaskcurve); + LLmaskshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmaskshape->setIdentityValue(0.); + HHmaskshape->setResetCurve(FlatCurveType(defSpot.HHmaskcurve.at(0)), defSpot.HHmaskcurve); + HHmaskshape->setCurveColorProvider(this, 2); + HHmaskshape->setBottomBarColorProvider(this, 2); + + maskCurveEditorG->curveListComplete(); + + struFrame->set_label_align(0.025, 0.5); + + strumaskcol->setAdjusterListener(this); + + toolcolConn = toolcol->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::toolcolChanged)); + + blurFrame->set_label_align(0.025, 0.5); + + fftColorMaskConn = fftColorMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabColor::fftColorMaskChanged)); + + contcol->setAdjusterListener(this); + + blurcol->setAdjusterListener(this); + + blendmaskcol->setAdjusterListener(this); + + radmaskcol->setLogScale(10, -10); + radmaskcol->setAdjusterListener(this); + + lapmaskcol->setAdjusterListener(this); + + chromaskcol->setAdjusterListener(this); + + gammaskcol->setAdjusterListener(this); + + slomaskcol->setAdjusterListener(this); + + shadmaskcol->setAdjusterListener(this); + + maskHCurveEditorG->setCurveListener(this); + + HHhmaskshape->setIdentityValue(0.); + HHhmaskshape->setResetCurve(FlatCurveType(defSpot.HHhmaskcurve.at(0)), defSpot.HHhmaskcurve); +// HHhmaskshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + HHhmaskshape->setCurveColorProvider(this, 2); + HHhmaskshape->setBottomBarColorProvider(this, 2); + + maskHCurveEditorG->curveListComplete(); + + mask2CurveEditorG->setCurveListener(this); + + Lmaskshape->setResetCurve(DiagonalCurveType(defSpot.Lmaskcurve.at(0)), defSpot.Lmaskcurve); + Lmaskshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmaskshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2CurveEditorG->curveListComplete(); + + mask2CurveEditorGwav->setCurveListener(this); + + LLmaskcolshapewav->setIdentityValue(0.); + LLmaskcolshapewav->setResetCurve(FlatCurveType(defSpot.LLmaskcolcurvewav.at(0)), defSpot.LLmaskcolcurvewav); + LLmaskcolshapewav->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2CurveEditorGwav->curveListComplete(); + + csThresholdcol->setAdjusterListener(this); + + // Add Color & Light specific widgets to GUI + Gtk::Frame* const superFrame = Gtk::manage(new Gtk::Frame()); + superFrame->set_label_align(0.025, 0.5); + // superFrame->set_label_widget(*curvactiv); + ToolParamBlock* const superBox = Gtk::manage(new ToolParamBlock()); + superBox->pack_start(*lightness); + superBox->pack_start(*contrast); + superBox->pack_start(*chroma); + ToolParamBlock* const gridBox = Gtk::manage(new ToolParamBlock()); + gridBox->pack_start(*labgrid); + gridBox->pack_start(*gridMethod); + gridBox->pack_start(*strengthgrid); + gridFrame->add(*gridBox); + superBox->pack_start(*gridFrame); + superFrame->add(*superBox); + pack_start(*superFrame); +// pack_start(*sensi); + pack_start(*structcol); + pack_start(*blurcolde); + pack_start(*softradiuscol); + pack_start(*invers); + ToolParamBlock* const gradcolBox = Gtk::manage(new ToolParamBlock()); + gradcolBox->pack_start(*strcol); + gradcolBox->pack_start(*strcolab); + gradcolBox->pack_start(*strcolh); + gradcolBox->pack_start(*angcol); + expgradcol->add(*gradcolBox, false); + pack_start(*expgradcol, false, false); + ToolParamBlock* const curvBox = Gtk::manage(new ToolParamBlock()); + Gtk::HBox* const qualcurvbox = Gtk::manage(new Gtk::HBox()); + qualcurvbox->pack_start(*labqualcurv, Gtk::PACK_SHRINK, 4); + qualcurvbox->pack_start(*qualitycurveMethod); + curvBox->pack_start(*qualcurvbox); + curvBox->pack_start(*llCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + curvBox->pack_start(*clCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + curvBox->pack_start(*HCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + curvBox->pack_start(*H2CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + curvBox->pack_start(*rgbCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + curvBox->pack_start(*special); + expcurvcol->add(*curvBox, false); + pack_start(*expcurvcol, false, false); + ToolParamBlock* const mask7Box = Gtk::manage(new ToolParamBlock()); + mask7Box->pack_start(*merMethod); + Gtk::Frame* const merge1colFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_MERGE1COLFRA"))); + merge1colFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const mergecolBox = Gtk::manage(new ToolParamBlock()); + Gtk::HSeparator* const separatormer = Gtk::manage(new Gtk::HSeparator()); + mergecolBox->pack_start(*separatormer, Gtk::PACK_SHRINK, 2); + mergecolBox->pack_start(*mergecolMethod); + mergecolBox->pack_start(*mercol); + mergecolBox->pack_start(*opacol); + mergecolBox->pack_start(*conthrcol); + ToolParamBlock* const gridmerBox = Gtk::manage(new ToolParamBlock()); + gridmerFrame->set_label_align(0.025, 0.5); + gridmerBox->pack_start(*labgridmerg); + gridmerBox->pack_start(*merlucol); + gridmerFrame->add(*gridmerBox); + mergecolBox->pack_start(*gridmerFrame); + merge1colFrame->add(*mergecolBox); + mask7->pack_start(*merge1colFrame); + mask7Box->pack_start(*mask7); + expmaskcol1->add(*mask7Box, false); + pack_start(*expmaskcol1, false, false); + Gtk::Frame* const mergecolFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_MERGECOLFRA"))); + mergecolFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const maskcolBox = Gtk::manage(new ToolParamBlock()); + maskcolBox->pack_start(*showmaskcolMethod, Gtk::PACK_SHRINK, 4); + maskcolBox->pack_start(*showmaskcolMethodinv, Gtk::PACK_SHRINK, 4); + maskcolBox->pack_start(*enaColorMask, Gtk::PACK_SHRINK, 0); + maskcolBox->pack_start(*maskCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + ToolParamBlock* const strumBox = Gtk::manage(new ToolParamBlock()); + strumBox->pack_start(*strumaskcol); + strumBox->pack_start(*toolcol); + struFrame->add(*strumBox); + maskcolBox->pack_start(*struFrame, Gtk::PACK_SHRINK, 0); + ToolParamBlock* const blurmBox = Gtk::manage(new ToolParamBlock()); + blurmBox->pack_start(*fftColorMask, Gtk::PACK_SHRINK, 0); + blurmBox->pack_start(*contcol); + blurmBox->pack_start(*blurcol); + blurFrame->add(*blurmBox); + maskcolBox->pack_start(*blurFrame, Gtk::PACK_SHRINK, 0); + maskcolBox->pack_start(*blendmaskcol, Gtk::PACK_SHRINK, 0); + Gtk::Frame* const toolcolFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_TOOLMASK"))); + toolcolFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const toolcolBox = Gtk::manage(new ToolParamBlock()); + toolcolBox->pack_start(*radmaskcol, Gtk::PACK_SHRINK, 0); + toolcolBox->pack_start(*lapmaskcol, Gtk::PACK_SHRINK, 0); + toolcolBox->pack_start(*chromaskcol, Gtk::PACK_SHRINK, 0); + toolcolBox->pack_start(*gammaskcol, Gtk::PACK_SHRINK, 0); + toolcolBox->pack_start(*slomaskcol, Gtk::PACK_SHRINK, 0); + toolcolBox->pack_start(*shadmaskcol, Gtk::PACK_SHRINK, 0); + toolcolBox->pack_start(*maskHCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolcolBox->pack_start(*mask2CurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolcolBox->pack_start(*mask2CurveEditorGwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolcolBox->pack_start(*csThresholdcol, Gtk::PACK_SHRINK, 0); + toolcolFrame->add(*toolcolBox); + maskcolBox->pack_start(*toolcolFrame); + mergecolFrame->add(*maskcolBox); + expmaskcol->add(*mergecolFrame, false); + pack_start(*expmaskcol, false, false); +} + +LocallabColor::~LocallabColor() +{ + delete llCurveEditorG; + delete clCurveEditorG; + delete HCurveEditorG; + delete H2CurveEditorG; + delete rgbCurveEditorG; + delete maskCurveEditorG; + delete maskHCurveEditorG; + delete mask2CurveEditorG; + delete mask2CurveEditorGwav; +} + +void LocallabColor::setListener(ToolPanelListener* tpl) +{ + LocallabTool::setListener(tpl); + + labgrid->setListener(tpl); + labgridmerg->setListener(tpl); +} + +bool LocallabColor::isMaskViewActive() +{ + return ((showmaskcolMethod->get_active_row_number() != 0) || (showmaskcolMethodinv->get_active_row_number() != 0)); +} + +void LocallabColor::resetMaskView() +{ + showmaskcolMethodConn.block(true); + showmaskcolMethodConninv.block(true); + + showmaskcolMethod->set_active(0); + showmaskcolMethodinv->set_active(0); + + showmaskcolMethodConn.block(false); + showmaskcolMethodConninv.block(false); +} + +void LocallabColor::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + colorMask = showmaskcolMethod->get_active_row_number(); + colorMaskinv = showmaskcolMethodinv->get_active_row_number(); +} + +void LocallabColor::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_EXPCOLOR_TOOLTIP")); + lightness->set_tooltip_text(M("TP_LOCALLAB_LIGHTN_TOOLTIP")); + structcol->set_tooltip_text(M("TP_LOCALLAB_STRUCT_TOOLTIP")); + sensi->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); + strcol->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); + angcol->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + qualitycurveMethod->set_tooltip_markup(M("TP_LOCALLAB_CURVEMETHOD_TOOLTIP")); + mercol->set_tooltip_text(M("TP_LOCALLAB_MERGEMER_TOOLTIP")); + opacol->set_tooltip_text(M("TP_LOCALLAB_MERGEOPA_TOOLTIP")); + conthrcol->set_tooltip_text(M("TP_LOCALLAB_MERGEOPA_TOOLTIP")); + gridmerFrame->set_tooltip_text(M("TP_LOCALLAB_GRIDFRAME_TOOLTIP")); + expmaskcol->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmaskshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + radmaskcol->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmaskcol->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + Lmaskshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + LLmaskcolshapewav->setTooltip(M("TP_LOCALLAB_LMASK_LEVEL_TOOLTIP")); + expmaskcol1->set_tooltip_text(M("TP_LOCALLAB_EXPMERGEFILE_TOOLTIP")); + blendmaskcol->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + struFrame->set_tooltip_text(M("TP_LOCALLAB_STRUMASK_TOOLTIP")); + blurFrame->set_tooltip_text(M("TP_LOCALLAB_BLURMASK_TOOLTIP")); + maskHCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_HHMASK_TOOLTIP")); + mask2CurveEditorGwav->set_tooltip_text(M("TP_LOCALLAB_WAVMASK_TOOLTIP")); + mask2CurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + special->set_tooltip_text(M("TP_LOCALLAB_SPECIAL_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + lightness->set_tooltip_text(""); + structcol->set_tooltip_text(""); + sensi->set_tooltip_text(""); + angcol->set_tooltip_text(M("")); + strcol->set_tooltip_text(""); + qualitycurveMethod->set_tooltip_text(""); + mercol->set_tooltip_text(""); + opacol->set_tooltip_text(""); + conthrcol->set_tooltip_text(""); + gridmerFrame->set_tooltip_text(""); + expmaskcol->set_tooltip_text(""); + CCmaskshape->setTooltip(""); + LLmaskshape->setTooltip(""); + HHmaskshape->setTooltip(""); + radmaskcol->set_tooltip_text(""); + lapmaskcol->set_tooltip_text(""); + Lmaskshape->setTooltip(""); + LLmaskcolshapewav->setTooltip(""); + expmaskcol1->set_tooltip_text(M("")); + blendmaskcol->set_tooltip_text(M("")); + struFrame->set_tooltip_text(M("")); + blurFrame->set_tooltip_text(M("")); + maskHCurveEditorG->set_tooltip_text(M("")); + mask2CurveEditorGwav->set_tooltip_text(M("")); + mask2CurveEditorG->set_tooltip_text(M("")); + special->set_tooltip_text(M("")); + } +} + +void LocallabColor::setDefaultExpanderVisibility() +{ + expgradcol->set_expanded(false); + expcurvcol->set_expanded(false); + expmaskcol1->set_expanded(false); + expmaskcol->set_expanded(false); +} + +void LocallabColor::disableListener() +{ + LocallabTool::disableListener(); + + curvactivConn.block(true); + gridMethodConn.block(true); + inversConn.block(true); + qualitycurveMethodConn.block(true); + toneMethodConn.block(true); + specialConn.block(true); + merMethodConn.block(true); + mergecolMethodConn.block(true); + showmaskcolMethodConn.block(true); + showmaskcolMethodConninv.block(true); + enaColorMaskConn.block(true); + toolcolConn.block(true); + fftColorMaskConn.block(true); +} + +void LocallabColor::enableListener() +{ + LocallabTool::enableListener(); + + curvactivConn.block(false); + gridMethodConn.block(false); + inversConn.block(false); + qualitycurveMethodConn.block(false); + toneMethodConn.block(false); + specialConn.block(false); + merMethodConn.block(false); + mergecolMethodConn.block(false); + showmaskcolMethodConn.block(false); + showmaskcolMethodConninv.block(false); + enaColorMaskConn.block(false); + toolcolConn.block(false); + fftColorMaskConn.block(false); +} + +void LocallabColor::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visicolor); + exp->setEnabled(spot.expcolor); + complexity->set_active(spot.complexcolor); + + curvactiv->set_active(spot.curvactiv); + lightness->setValue(spot.lightness); + contrast->setValue(spot.contrast); + chroma->setValue(spot.chroma); + labgrid->setParams(spot.labgridALow / LocallabParams::LABGRIDL_CORR_MAX, + spot.labgridBLow / LocallabParams::LABGRIDL_CORR_MAX, + spot.labgridAHigh / LocallabParams::LABGRIDL_CORR_MAX, + spot.labgridBHigh / LocallabParams::LABGRIDL_CORR_MAX, + false); + + if (spot.gridMethod == "one") { + gridMethod->set_active(0); + } else if (spot.gridMethod == "two") { + gridMethod->set_active(1); + } + + strengthgrid->setValue(spot.strengthgrid); + sensi->setValue(spot.sensi); + structcol->setValue(spot.structcol); + blurcolde->setValue(spot.blurcolde); + softradiuscol->setValue(spot.softradiuscol); + invers->set_active(spot.invers); + strcol->setValue(spot.strcol); + strcolab->setValue(spot.strcolab); + strcolh->setValue(spot.strcolh); + angcol->setValue(spot.angcol); + + if (spot.qualitycurveMethod == "none") { + qualitycurveMethod->set_active(0); + } else if (spot.qualitycurveMethod == "std") { + qualitycurveMethod->set_active(1); + } + + llshape->setCurve(spot.llcurve); + ccshape->setCurve(spot.cccurve); + clshape->setCurve(spot.clcurve); + lcshape->setCurve(spot.lccurve); + LHshape->setCurve(spot.LHcurve); + HHshape->setCurve(spot.HHcurve); + + if (spot.toneMethod == "one") { + toneMethod->set_active(0); + } else if (spot.toneMethod == "two") { + toneMethod->set_active(1); + } else if (spot.toneMethod == "thr") { + toneMethod->set_active(2); + } else if (spot.toneMethod == "fou") { + toneMethod->set_active(3); + } + + rgbshape->setCurve(spot.rgbcurve); + special->set_active(spot.special); + + if (spot.merMethod == "mone") { + merMethod->set_active(0); +// } else if (spot.merMethod == "mtwo") { +// merMethod->set_active(1); + } else if (spot.merMethod == "mthr") { + merMethod->set_active(1); + } else if (spot.merMethod == "mfou") { + merMethod->set_active(2); + } else if (spot.merMethod == "mfiv") { + merMethod->set_active(3); + } + + if (spot.mergecolMethod == "one") { + mergecolMethod->set_active(0); + } else if (spot.mergecolMethod == "two") { + mergecolMethod->set_active(1); + } else if (spot.mergecolMethod == "thr") { + mergecolMethod->set_active(2); + } else if (spot.mergecolMethod == "fou") { + mergecolMethod->set_active(3); + } else if (spot.mergecolMethod == "fiv") { + mergecolMethod->set_active(4); + } else if (spot.mergecolMethod == "six") { + mergecolMethod->set_active(5); + } else if (spot.mergecolMethod == "sev") { + mergecolMethod->set_active(6); + } else if (spot.mergecolMethod == "sev0") { + mergecolMethod->set_active(7); + } else if (spot.mergecolMethod == "sev1") { + mergecolMethod->set_active(8); + } else if (spot.mergecolMethod == "sev2") { + mergecolMethod->set_active(9); + } else if (spot.mergecolMethod == "hei") { + mergecolMethod->set_active(10); + } else if (spot.mergecolMethod == "nin") { + mergecolMethod->set_active(11); + } else if (spot.mergecolMethod == "ten") { + mergecolMethod->set_active(12); + } else if (spot.mergecolMethod == "ele") { + mergecolMethod->set_active(13); + } else if (spot.mergecolMethod == "twe") { + mergecolMethod->set_active(14); + } else if (spot.mergecolMethod == "thi") { + mergecolMethod->set_active(15); + } else if (spot.mergecolMethod == "for") { + mergecolMethod->set_active(16); + } else if (spot.mergecolMethod == "hue") { + mergecolMethod->set_active(17); + } else if (spot.mergecolMethod == "sat") { + mergecolMethod->set_active(18); + } else if (spot.mergecolMethod == "col") { + mergecolMethod->set_active(19); + } else if (spot.mergecolMethod == "lum") { + mergecolMethod->set_active(20); + } + + mercol->setValue(spot.mercol); + opacol->setValue(spot.opacol); + conthrcol->setValue(spot.conthrcol); + labgridmerg->setParams(0, 0, + spot.labgridAHighmerg / LocallabParams::LABGRIDL_CORR_MAX, + spot.labgridBHighmerg / LocallabParams::LABGRIDL_CORR_MAX, + false); + merlucol->setValue(spot.merlucol); + enaColorMask->set_active(spot.enaColorMask); + CCmaskshape->setCurve(spot.CCmaskcurve); + LLmaskshape->setCurve(spot.LLmaskcurve); + HHmaskshape->setCurve(spot.HHmaskcurve); + strumaskcol->setValue(spot.strumaskcol); + toolcol->set_active(spot.toolcol); + fftColorMask->set_active(spot.fftColorMask); + contcol->setValue(spot.contcol); + // Update GUI according to fftColorMash button state + // Note: Contrary to the others, shall be called before setting 'blurcol' value + updateColorGUI3(); + blurcol->setValue(spot.blurcol); + blendmaskcol->setValue(spot.blendmaskcol); + radmaskcol->setValue(spot.radmaskcol); + lapmaskcol->setValue(spot.lapmaskcol); + chromaskcol->setValue(spot.chromaskcol); + gammaskcol->setValue(spot.gammaskcol); + slomaskcol->setValue(spot.slomaskcol); + shadmaskcol->setValue(spot.shadmaskcol); + HHhmaskshape->setCurve(spot.HHhmaskcurve); + Lmaskshape->setCurve(spot.Lmaskcurve); + LLmaskcolshapewav->setCurve(spot.LLmaskcolcurvewav); + csThresholdcol->setValue(spot.csthresholdcol); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update GUI according to invers button state + updateColorGUI1(); + + // Update GUI according to merMethod combobox state + updateColorGUI2(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabColor::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expcolor = exp->getEnabled(); + spot.visicolor = exp->get_visible(); + spot.complexcolor = complexity->get_active_row_number(); + + spot.curvactiv = curvactiv->get_active(); + spot.lightness = lightness->getIntValue(); + spot.contrast = contrast->getIntValue(); + spot.chroma = chroma->getIntValue(); + labgrid->getParams(spot.labgridALow, + spot.labgridBLow, + spot.labgridAHigh, + spot.labgridBHigh); + spot.labgridALow *= LocallabParams::LABGRIDL_CORR_MAX; + spot.labgridAHigh *= LocallabParams::LABGRIDL_CORR_MAX; + spot.labgridBLow *= LocallabParams::LABGRIDL_CORR_MAX; + spot.labgridBHigh *= LocallabParams::LABGRIDL_CORR_MAX; + + if (gridMethod->get_active_row_number() == 0) { + spot.gridMethod = "one"; + } else if (gridMethod->get_active_row_number() == 1) { + spot.gridMethod = "two"; + } + + spot.strengthgrid = strengthgrid->getIntValue(); + spot.sensi = sensi->getIntValue(); + spot.structcol = structcol->getIntValue(); + spot.blurcolde = blurcolde->getIntValue(); + spot.softradiuscol = softradiuscol->getValue(); + spot.invers = invers->get_active(); + spot.strcol = strcol->getValue(); + spot.strcolab = strcolab->getValue(); + spot.strcolh = strcolh->getValue(); + spot.angcol = angcol->getValue(); + + if (qualitycurveMethod->get_active_row_number() == 0) { + spot.qualitycurveMethod = "none"; + } else if (qualitycurveMethod->get_active_row_number() == 1) { + spot.qualitycurveMethod = "std"; + } + + spot.llcurve = llshape->getCurve(); + spot.cccurve = ccshape->getCurve(); + spot.clcurve = clshape->getCurve(); + spot.lccurve = lcshape->getCurve(); + spot.LHcurve = LHshape->getCurve(); + spot.HHcurve = HHshape->getCurve(); + + if (toneMethod->get_active_row_number() == 0) { + spot.toneMethod = "one"; + } else if (toneMethod->get_active_row_number() == 1) { + spot.toneMethod = "two"; + } else if (toneMethod->get_active_row_number() == 2) { + spot.toneMethod = "thr"; + } else if (toneMethod->get_active_row_number() == 3) { + spot.toneMethod = "fou"; + } + + spot.rgbcurve = rgbshape->getCurve(); + spot.special = special->get_active(); + + if (merMethod->get_active_row_number() == 0) { + spot.merMethod = "mone"; +// } else if (merMethod->get_active_row_number() == 1) { +// spot.merMethod = "mtwo"; + } else if (merMethod->get_active_row_number() == 1) { + spot.merMethod = "mthr"; + } else if (merMethod->get_active_row_number() == 2) { + spot.merMethod = "mfou"; + } else if (merMethod->get_active_row_number() == 3) { + spot.merMethod = "mfiv"; + } + + if (mergecolMethod->get_active_row_number() == 0) { + spot.mergecolMethod = "one"; + } else if (mergecolMethod->get_active_row_number() == 1) { + spot.mergecolMethod = "two"; + } else if (mergecolMethod->get_active_row_number() == 2) { + spot.mergecolMethod = "thr"; + } else if (mergecolMethod->get_active_row_number() == 3) { + spot.mergecolMethod = "fou"; + } else if (mergecolMethod->get_active_row_number() == 4) { + spot.mergecolMethod = "fiv"; + } else if (mergecolMethod->get_active_row_number() == 5) { + spot.mergecolMethod = "six"; + } else if (mergecolMethod->get_active_row_number() == 6) { + spot.mergecolMethod = "sev"; + } else if (mergecolMethod->get_active_row_number() == 7) { + spot.mergecolMethod = "sev0"; + } else if (mergecolMethod->get_active_row_number() == 8) { + spot.mergecolMethod = "sev1"; + } else if (mergecolMethod->get_active_row_number() == 9) { + spot.mergecolMethod = "sev2"; + } else if (mergecolMethod->get_active_row_number() == 10) { + spot.mergecolMethod = "hei"; + } else if (mergecolMethod->get_active_row_number() == 11) { + spot.mergecolMethod = "nin"; + } else if (mergecolMethod->get_active_row_number() == 12) { + spot.mergecolMethod = "ten"; + } else if (mergecolMethod->get_active_row_number() == 13) { + spot.mergecolMethod = "ele"; + } else if (mergecolMethod->get_active_row_number() == 14) { + spot.mergecolMethod = "twe"; + } else if (mergecolMethod->get_active_row_number() == 15) { + spot.mergecolMethod = "thi"; + } else if (mergecolMethod->get_active_row_number() == 16) { + spot.mergecolMethod = "for"; + } else if (mergecolMethod->get_active_row_number() == 17) { + spot.mergecolMethod = "hue"; + } else if (mergecolMethod->get_active_row_number() == 18) { + spot.mergecolMethod = "sat"; + } else if (mergecolMethod->get_active_row_number() == 19) { + spot.mergecolMethod = "col"; + } else if (mergecolMethod->get_active_row_number() == 20) { + spot.mergecolMethod = "lum"; + } + + spot.mercol = mercol->getValue(); + spot.opacol = opacol->getValue(); + spot.conthrcol = conthrcol->getValue(); + labgridmerg->getParams(spot.labgridALowmerg, + spot.labgridBLowmerg, + spot.labgridAHighmerg, + spot.labgridBHighmerg); + spot.labgridALowmerg *= LocallabParams::LABGRIDL_CORR_MAX; + spot.labgridAHighmerg *= LocallabParams::LABGRIDL_CORR_MAX; + spot.labgridBLowmerg *= LocallabParams::LABGRIDL_CORR_MAX; + spot.labgridBHighmerg *= LocallabParams::LABGRIDL_CORR_MAX; + spot.merlucol = merlucol->getValue(); + spot.enaColorMask = enaColorMask->get_active(); + spot.CCmaskcurve = CCmaskshape->getCurve(); + spot.LLmaskcurve = LLmaskshape->getCurve(); + spot.HHmaskcurve = HHmaskshape->getCurve(); + spot.strumaskcol = strumaskcol->getValue(); + spot.toolcol = toolcol->get_active(); + spot.fftColorMask = fftColorMask->get_active(); + spot.contcol = contcol->getValue(); + spot.blurcol = blurcol->getValue(); + spot.blendmaskcol = blendmaskcol->getIntValue(); + spot.radmaskcol = radmaskcol->getValue(); + spot.lapmaskcol = lapmaskcol->getValue(); + spot.chromaskcol = chromaskcol->getValue(); + spot.gammaskcol = gammaskcol->getValue(); + spot.slomaskcol = slomaskcol->getValue(); + spot.shadmaskcol = shadmaskcol->getIntValue(); + spot.HHhmaskcurve = HHhmaskshape->getCurve(); + spot.Lmaskcurve = Lmaskshape->getCurve(); + spot.LLmaskcolcurvewav = LLmaskcolshapewav->getCurve(); + spot.csthresholdcol = csThresholdcol->getValue(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabColor::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default value for adjuster, labgrid and threshold adjuster widgets + lightness->setDefault((double)defSpot.lightness); + contrast->setDefault((double)defSpot.contrast); + chroma->setDefault((double)defSpot.chroma); + labgrid->setDefault(defSpot.labgridALow / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridBLow / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridAHigh / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridBHigh / LocallabParams::LABGRIDL_CORR_MAX); + strengthgrid->setDefault((double) defSpot.strengthgrid); + sensi->setDefault((double)defSpot.sensi); + structcol->setDefault((double)defSpot.structcol); + blurcolde->setDefault((double)defSpot.blurcolde); + softradiuscol->setDefault(defSpot.softradiuscol); + strcol->setDefault(defSpot.strcol); + strcolab->setDefault(defSpot.strcolab); + strcolh->setDefault(defSpot.strcolh); + angcol->setDefault(defSpot.angcol); + mercol->setDefault(defSpot.mercol); + opacol->setDefault(defSpot.opacol); + conthrcol->setDefault(defSpot.conthrcol); + labgridmerg->setDefault(defSpot.labgridALowmerg / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridBLowmerg / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridAHighmerg / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridBHighmerg / LocallabParams::LABGRIDL_CORR_MAX); + merlucol->setDefault(defSpot.merlucol); + strumaskcol->setDefault(defSpot.strumaskcol); + contcol->setDefault(defSpot.contcol); + blurcol->setDefault(defSpot.blurcol); + blendmaskcol->setDefault((double)defSpot.blendmaskcol); + radmaskcol->setDefault(defSpot.radmaskcol); + lapmaskcol->setDefault(defSpot.lapmaskcol); + chromaskcol->setDefault(defSpot.chromaskcol); + gammaskcol->setDefault(defSpot.gammaskcol); + slomaskcol->setDefault(defSpot.slomaskcol); + shadmaskcol->setDefault((double)defSpot.shadmaskcol); + csThresholdcol->setDefault(defSpot.csthresholdcol); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabColor::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == lightness) { + if (listener) { + listener->panelChanged(Evlocallablightness, + lightness->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == contrast) { + if (listener) { + listener->panelChanged(Evlocallabcontrast, + contrast->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chroma) { + if (listener) { + listener->panelChanged(Evlocallabchroma, + chroma->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strengthgrid) { + if (listener) { + listener->panelChanged(EvLocallabLabstrengthgrid, + strengthgrid->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensi) { + if (listener) { + listener->panelChanged(Evlocallabsensi, + sensi->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == structcol) { + if (listener) { + listener->panelChanged(Evlocallabstructcol, + structcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blurcolde) { + if (listener) { + listener->panelChanged(Evlocallabblurcolde, + blurcolde->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == softradiuscol) { + if (listener) { + listener->panelChanged(Evlocallabsoftradiuscol, + softradiuscol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strcol) { + if (listener) { + listener->panelChanged(Evlocallabstrcol, + strcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strcolab) { + if (listener) { + listener->panelChanged(Evlocallabstrcolab, + strcolab->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strcolh) { + if (listener) { + listener->panelChanged(Evlocallabstrcolh, + strcolh->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == angcol) { + if (listener) { + listener->panelChanged(Evlocallabangcol, + angcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == mercol) { + if (listener) { + listener->panelChanged(Evlocallabmercol, + mercol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == opacol) { + if (listener) { + listener->panelChanged(Evlocallabopacol, + opacol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == conthrcol) { + if (listener) { + listener->panelChanged(Evlocallabconthrcol, + conthrcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == merlucol) { + if (listener) { + listener->panelChanged(Evlocallabmerlucol, + merlucol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strumaskcol) { + if (listener) { + listener->panelChanged(Evlocallabstrumaskcol, + strumaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == contcol) { + if (listener) { + listener->panelChanged(Evlocallabcontcol, + contcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blurcol) { + if (listener) { + listener->panelChanged(Evlocallabblurcol, + blurcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskcol) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskcol, + blendmaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskcol) { + if (listener) { + listener->panelChanged(Evlocallabradmaskcol, + radmaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskcol) { + if (listener) { + listener->panelChanged(Evlocallablapmaskcol, + lapmaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskcol) { + if (listener) { + listener->panelChanged(Evlocallabchromaskcol, + chromaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskcol) { + if (listener) { + listener->panelChanged(Evlocallabgammaskcol, + gammaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskcol) { + if (listener) { + listener->panelChanged(Evlocallabslomaskcol, + slomaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shadmaskcol) { + if (listener) { + listener->panelChanged(Evlocallabshadmaskcol, + shadmaskcol->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == csThresholdcol) { + if (listener) { + listener->panelChanged(EvlocallabcsThresholdcol, + csThresholdcol->getHistoryString() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == llshape) { + if (listener) { + listener->panelChanged(Evlocallabllshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == ccshape) { + if (listener) { + listener->panelChanged(Evlocallabccshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == clshape) { + if (listener) { + listener->panelChanged(Evlocallabclshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == lcshape) { + if (listener) { + listener->panelChanged(Evlocallablcshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LHshape) { + if (listener) { + listener->panelChanged(EvlocallabLHshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHshape) { + if (listener) { + listener->panelChanged(EvlocallabHHshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == rgbshape) { + if (listener) { + listener->panelChanged(Evlocallabrgbshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == CCmaskshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHhmaskshape) { + if (listener) { + listener->panelChanged(EvlocallabHHhmaskshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmaskshape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskcolshapewav) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskcolshapewav, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenacolor, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenacolor, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + blurcolde->setValue((double)defSpot.blurcolde); + // softradiuscol->setValue(defSpot.softradiuscol); + strcolab->setValue(defSpot.strcolab); + strcolh->setValue(defSpot.strcolh); + clshape->setCurve(defSpot.clcurve); + lcshape->setCurve(defSpot.lccurve); + LHshape->setCurve(defSpot.LHcurve); + HHshape->setCurve(defSpot.HHcurve); + + if (defSpot.toneMethod == "one") { + toneMethod->set_active(0); + } else if (defSpot.toneMethod == "two") { + toneMethod->set_active(1); + } else if (defSpot.toneMethod == "thr") { + toneMethod->set_active(2); + } else if (defSpot.toneMethod == "fou") { + toneMethod->set_active(3); + } + + rgbshape->setCurve(defSpot.rgbcurve); + special->set_active(defSpot.special); + + if (defSpot.merMethod == "mone") { + merMethod->set_active(0); +// } else if (defSpot.merMethod == "mtwo") { +// merMethod->set_active(1); + } else if (defSpot.merMethod == "mthr") { + merMethod->set_active(1); + } else if (defSpot.merMethod == "mfou") { + merMethod->set_active(2); + } else if (defSpot.merMethod == "mfiv") { + merMethod->set_active(3); + } + + if (defSpot.mergecolMethod == "one") { + mergecolMethod->set_active(0); + } else if (defSpot.mergecolMethod == "two") { + mergecolMethod->set_active(1); + } else if (defSpot.mergecolMethod == "thr") { + mergecolMethod->set_active(2); + } else if (defSpot.mergecolMethod == "fou") { + mergecolMethod->set_active(3); + } else if (defSpot.mergecolMethod == "fiv") { + mergecolMethod->set_active(4); + } else if (defSpot.mergecolMethod == "six") { + mergecolMethod->set_active(5); + } else if (defSpot.mergecolMethod == "sev") { + mergecolMethod->set_active(6); + } else if (defSpot.mergecolMethod == "sev0") { + mergecolMethod->set_active(7); + } else if (defSpot.mergecolMethod == "sev1") { + mergecolMethod->set_active(8); + } else if (defSpot.mergecolMethod == "sev2") { + mergecolMethod->set_active(9); + } else if (defSpot.mergecolMethod == "hei") { + mergecolMethod->set_active(10); + } else if (defSpot.mergecolMethod == "nin") { + mergecolMethod->set_active(11); + } else if (defSpot.mergecolMethod == "ten") { + mergecolMethod->set_active(12); + } else if (defSpot.mergecolMethod == "ele") { + mergecolMethod->set_active(13); + } else if (defSpot.mergecolMethod == "twe") { + mergecolMethod->set_active(14); + } else if (defSpot.mergecolMethod == "thi") { + mergecolMethod->set_active(15); + } else if (defSpot.mergecolMethod == "for") { + mergecolMethod->set_active(16); + } else if (defSpot.mergecolMethod == "hue") { + mergecolMethod->set_active(17); + } else if (defSpot.mergecolMethod == "sat") { + mergecolMethod->set_active(18); + } else if (defSpot.mergecolMethod == "col") { + mergecolMethod->set_active(19); + } else if (defSpot.mergecolMethod == "lum") { + mergecolMethod->set_active(20); + } + + mercol->setValue(defSpot.mercol); + opacol->setValue(defSpot.opacol); + conthrcol->setValue(defSpot.conthrcol); + labgridmerg->setParams(0, 0, + defSpot.labgridAHighmerg / LocallabParams::LABGRIDL_CORR_MAX, + defSpot.labgridBHighmerg / LocallabParams::LABGRIDL_CORR_MAX, + false); + merlucol->setValue(defSpot.merlucol); + strumaskcol->setValue(defSpot.strumaskcol); + toolcol->set_active(defSpot.toolcol); + fftColorMask->set_active(defSpot.fftColorMask); + contcol->setValue(defSpot.contcol); + lapmaskcol->setValue(defSpot.lapmaskcol); + gammaskcol->setValue(defSpot.gammaskcol); + slomaskcol->setValue(defSpot.slomaskcol); + shadmaskcol->setValue((double)defSpot.shadmaskcol); + HHhmaskshape->setCurve(defSpot.HHhmaskcurve); + LLmaskcolshapewav->setCurve(defSpot.LLmaskcolcurvewav); + csThresholdcol->setValue(defSpot.csthresholdcol); + + // Enable all listeners + enableListener(); + + // Update GUI based on converted widget parameters: + // - Update GUI according to merMethod combobox state + updateColorGUI2(); + // - Update GUI according to fftColorMash button state + updateColorGUI3(); +} + +void LocallabColor::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + blurcolde->hide(); + softradiuscol->show(); + strcolab->hide(); + strcolh->hide(); + clCurveEditorG->hide(); + HCurveEditorG->hide(); + H2CurveEditorG->hide(); + rgbCurveEditorG->hide(); + special->hide(); + expmaskcol1->hide(); + struFrame->hide(); + blurFrame->hide(); + lapmaskcol->hide(); + gammaskcol->hide(); + slomaskcol->hide(); + shadmaskcol->hide(); + maskHCurveEditorG->hide(); + mask2CurveEditorGwav->hide(); + csThresholdcol->hide(); + } else { + // Advanced widgets are shown in Expert mode + blurcolde->show(); + + if (!invers->get_active()) { // Keep widget hidden when invers is toggled + softradiuscol->show(); + } + + strcolab->show(); + strcolh->show(); + + if (!invers->get_active()) { // Keep widgets hidden when invers is toggled + clCurveEditorG->show(); + HCurveEditorG->show(); + } + + H2CurveEditorG->show(); + rgbCurveEditorG->show(); + special->show(); + + if (!invers->get_active()) { // Keep widget hidden when invers is toggled + expmaskcol1->show(); + } + + struFrame->show(); + blurFrame->show(); + lapmaskcol->show(); + gammaskcol->show(); + slomaskcol->show(); + shadmaskcol->show(); + maskHCurveEditorG->show(); + mask2CurveEditorGwav->show(); + csThresholdcol->show(); + } +} + +void LocallabColor::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskshape->updateLocallabBackground(normChromar); + LLmaskshape->updateLocallabBackground(normLumar); + HHmaskshape->updateLocallabBackground(normHuer); + HHhmaskshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabColor::curvactivChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (curvactiv->get_active()) { + listener->panelChanged(Evlocallabcurvactiv, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabcurvactiv, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::gridMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvLocallabgridMethod, + gridMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabColor::inversChanged() +{ + updateColorGUI1(); // Update GUI according to invers button state + + // This event is called to transmit potentially reset mask state + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (invers->get_active()) { + listener->panelChanged(Evlocallabinvers, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabinvers, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::qualitycurveMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabqualitycurveMethod, + qualitycurveMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabColor::toneMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvLocallabtoneMethod, + toneMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabColor::specialChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (special->get_active()) { + listener->panelChanged(EvLocallabspecial, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabspecial, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::merMethodChanged() +{ + updateColorGUI2(); // Update GUI according to merMethod combobox state + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvLocallabmerMethod, + merMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabColor::mergecolMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvLocallabmergecolMethod, + mergecolMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabColor::showmaskcolMethodChanged() +{ + // If mask preview is activated, deactivate other Color & Light mask preview + showmaskcolMethodConninv.block(true); + showmaskcolMethodinv->set_active(0); + showmaskcolMethodConninv.block(false); + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabColor::showmaskcolMethodChangedinv() +{ + // If mask preview is activated, deactivate other Color & Light mask preview + showmaskcolMethodConn.block(true); + showmaskcolMethod->set_active(0); + showmaskcolMethodConn.block(false); + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabColor::enaColorMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enaColorMask->get_active()) { + listener->panelChanged(EvLocallabEnaColorMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaColorMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::toolcolChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (toolcol->get_active()) { + listener->panelChanged(EvLocallabtoolcol, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabtoolcol, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::fftColorMaskChanged() +{ + updateColorGUI3(); // Update GUI according to fftColorMash button state + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fftColorMask->get_active()) { + listener->panelChanged(EvLocallabfftColorMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabfftColorMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabColor::updateColorGUI1() +{ + const int mode = complexity->get_active_row_number(); + + if (invers->get_active()) { + gridFrame->hide(); + structcol->hide(); + softradiuscol->hide(); + expgradcol->hide(); + labqualcurv->hide(); + qualitycurveMethod->hide(); + clCurveEditorG->hide(); + HCurveEditorG->hide(); + expmaskcol1->hide(); + showmaskcolMethod->hide(); + // Reset hidden mask combobox + showmaskcolMethodConn.block(true); + showmaskcolMethod->set_active(0); + showmaskcolMethodConn.block(false); + showmaskcolMethodinv->show(); + contcol->hide(); + blurcol->hide(); + } else { + gridFrame->show(); + structcol->show(); + + if (mode == Normal) { // Keep widget hidden in Normal mode + softradiuscol->show(); + } + + expgradcol->show(); + labqualcurv->show(); + qualitycurveMethod->show(); + + if (mode == Normal) { // Keep widgets hidden in Normal mode + clCurveEditorG->show(); + HCurveEditorG->show(); + expmaskcol1->show(); + } + + showmaskcolMethod->show(); + showmaskcolMethodinv->hide(); + // Reset hidden mask combobox + showmaskcolMethodConninv.block(true); + showmaskcolMethodinv->set_active(0); + showmaskcolMethodConninv.block(false); + contcol->show(); + blurcol->show(); + } +} + +void LocallabColor::updateColorGUI2() +{ + // Note: When a merMethod is selected, invers button is set insensitive to avoid the combobox to disappear + switch (merMethod->get_active_row_number()) { + case 0: + invers->set_sensitive(true); + H2CurveEditorG->set_sensitive(true); + rgbCurveEditorG->set_sensitive(true); + special->set_sensitive(true); + mask7->hide(); + conthrcol->hide(); + gridmerFrame->hide(); + break; +/* + case 1: + invers->set_sensitive(false); + H2CurveEditorG->set_sensitive(true); + rgbCurveEditorG->set_sensitive(true); + special->set_sensitive(true); + mask7->hide(); + conthrcol->hide(); + gridmerFrame->hide(); + break; +*/ + case 1: + invers->set_sensitive(false); + H2CurveEditorG->set_sensitive(true); + rgbCurveEditorG->set_sensitive(false); + special->set_sensitive(false); + mask7->show(); + conthrcol->show(); + gridmerFrame->hide(); + break; + + case 2: + invers->set_sensitive(false); + H2CurveEditorG->set_sensitive(true); + rgbCurveEditorG->set_sensitive(false); + special->set_sensitive(false); + mask7->show(); + conthrcol->show(); + gridmerFrame->hide(); + break; + + case 3: + invers->set_sensitive(false); + H2CurveEditorG->set_sensitive(true); + rgbCurveEditorG->set_sensitive(false); + special->set_sensitive(false); + mask7->show(); + conthrcol->hide(); + gridmerFrame->show(); + } +} + +void LocallabColor::updateColorGUI3() +{ + const double temp = blurcol->getValue(); + + if (fftColorMask->get_active()) { + blurcol->setLimits(0.2, 1000., 0.5, 0.2); + } else { + blurcol->setLimits(0.2, 100., 0.5, 0.2); + } + + blurcol->setValue(temp); +} + +/* ==== LocallabExposure ==== */ +LocallabExposure::LocallabExposure(): + LocallabTool(this, M("TP_LOCALLAB_EXP_TOOLNAME"), M("TP_LOCALLAB_EXPOSE"), false), + + // Exposure specific widgets + expMethod(Gtk::manage(new MyComboBoxText())), + pdeFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_PDEFRA")))), + laplacexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPLACEXP"), 0.0, 100.0, 0.1, 0.))), + linear(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LINEAR"), 0., 1., 0.01, 0.05))), + balanexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BALANEXP"), 0.5, 1.5, 0.01, 1.0))), + gamm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMM"), 0.2, 1.3, 0.01, 0.4))), + exnoiseMethod(Gtk::manage(new MyComboBoxText())), + fatFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_FATFRA")))), + fatamount(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))), + fatdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATDETAIL"), -100., 300., 1., 0.))), + fatlevel(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATLEVEL"), 0.25, 2.5, 0.05, 1.))), + fatanchor(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHORA"), 0.1, 3.0, 0.05, 1.))), + sensiex(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 60))), + structexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUCCOL"), 0, 100, 1, 0))), + blurexpde(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURDE"), 2, 100, 1, 5))), + exptoolexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPTOOL")))), + expcomp(Gtk::manage(new Adjuster(M("TP_EXPOSURE_EXPCOMP"), MINEXP, MAXEXP, 0.02, 0.))), + black(Gtk::manage(new Adjuster(M("TP_EXPOSURE_BLACKLEVEL"), -16384, 32768, 10, 0))), + hlcompr(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTS"), 0, 500, 1, 0))), + hlcomprthresh(Gtk::manage(new Adjuster(M("TP_EXPOSURE_COMPRHIGHLIGHTSTHRESHOLD"), 0, 100, 1, 0))), + shadex(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHADEX"), 0, 100, 1, 0))), + shcompr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHADEXCOMP"), 0, 100, 1, 50))), + expchroma(Gtk::manage(new Adjuster(M("TP_LOCALLAB_EXPCHROMA"), -50, 100, 1, 5))), + curveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_CURVEEDITOR_TONES_LABEL"))), + shapeexpos(static_cast(curveEditorG->addCurve(CT_Diagonal, ""))), + expgradexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPGRAD")))), + strexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -4., 4., 0.05, 0.))), + angexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))), + softradiusexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + inversex(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))), + expmaskexp(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWE")))), + showmaskexpMethod(Gtk::manage(new MyComboBoxText())), + showmaskexpMethodinv(Gtk::manage(new MyComboBoxText())), + enaExpMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enaExpMaskaft(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASKAFT")))), + maskexpCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + gradFramemask(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRADFRA")))), + strmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -2., 2., 0.05, 0.))), + angmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180., 180., 0.1, 0.))), + mask2expCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskexpshape(static_cast(mask2expCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter Exposure specific widgets + expMethod->append(M("TP_LOCALLAB_STD")); + expMethod->append(M("TP_LOCALLAB_PDE")); + expMethod->set_active(0); + expMethodConn = expMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabExposure::expMethodChanged)); + + pdeFrame->set_label_align(0.025, 0.5); + + laplacexp->setAdjusterListener(this); + + linear->setAdjusterListener(this); + + balanexp->setAdjusterListener(this); + + gamm->setAdjusterListener(this); + + exnoiseMethod->append(M("TP_LOCALLAB_NONENOISE")); + exnoiseMethod->append(M("TP_LOCALLAB_MEDIAN")); + exnoiseMethod->append(M("TP_LOCALLAB_WEDIANHI")); + exnoiseMethod->set_active(0); + exnoiseMethodConn = exnoiseMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabExposure::exnoiseMethodChanged)); + + fatFrame->set_label_align(0.025, 0.5); + + fatamount->setAdjusterListener(this); + + fatdetail->setAdjusterListener(this); + + fatlevel->setAdjusterListener(this); + + fatanchor->setAdjusterListener(this); + + sensiex->setAdjusterListener(this); + + structexp->setAdjusterListener(this); + + blurexpde->setAdjusterListener(this); + + setExpandAlignProperties(exptoolexp, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + expcomp->setAdjusterListener(this); + + black->setAdjusterListener(this); + + hlcompr->setAdjusterListener(this); + + hlcomprthresh->setAdjusterListener(this); + + shadex->setAdjusterListener(this); + + shcompr->setAdjusterListener(this); + + expchroma->setAdjusterListener(this); + + curveEditorG->setCurveListener(this); + + shapeexpos->setResetCurve(DiagonalCurveType(defSpot.excurve.at(0)), defSpot.excurve); + shapeexpos->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1}}); + shapeexpos->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1}}); + + curveEditorG->curveListComplete(); + + setExpandAlignProperties(expgradexp, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + strexp->setAdjusterListener(this); + + angexp->setAdjusterListener(this); + angexp->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + + softradiusexp->setLogScale(10, 0); + softradiusexp->setAdjusterListener(this); + + inversexConn = inversex->signal_toggled().connect(sigc::mem_fun(*this, &LocallabExposure::inversexChanged)); + inversex->set_tooltip_text(M("TP_LOCALLAB_INVERS_TOOLTIP")); + + setExpandAlignProperties(expmaskexp, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmaskexpMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskexpMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskexpMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskexpMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskexpMethod->append(M("TP_LOCALLAB_SHOWSTRUCEX")); + showmaskexpMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskexpMethod->set_active(0); + showmaskexpMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskexpMethodConn = showmaskexpMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabExposure::showmaskexpMethodChanged)); + + showmaskexpMethodinv->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskexpMethodinv->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskexpMethodinv->set_active(0); + showmaskexpMethodinv->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskexpMethodConninv = showmaskexpMethodinv->signal_changed().connect(sigc::mem_fun(*this, &LocallabExposure::showmaskexpMethodChangedinv)); + + enaExpMaskConn = enaExpMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabExposure::enaExpMaskChanged)); + + enaExpMaskaftConn = enaExpMaskaft->signal_toggled().connect(sigc::mem_fun(*this, &LocallabExposure::enaExpMaskaftChanged)); + + maskexpCurveEditorG->setCurveListener(this); + + CCmaskexpshape->setIdentityValue(0.); + CCmaskexpshape->setResetCurve(FlatCurveType(defSpot.CCmaskexpcurve.at(0)), defSpot.CCmaskexpcurve); + CCmaskexpshape->setBottomBarColorProvider(this, 1); + + LLmaskexpshape->setIdentityValue(0.); + LLmaskexpshape->setResetCurve(FlatCurveType(defSpot.LLmaskexpcurve.at(0)), defSpot.LLmaskexpcurve); + LLmaskexpshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1}}); + + HHmaskexpshape->setIdentityValue(0.); + HHmaskexpshape->setResetCurve(FlatCurveType(defSpot.HHmaskexpcurve.at(0)), defSpot.HHmaskexpcurve); + HHmaskexpshape->setCurveColorProvider(this, 2); + HHmaskexpshape->setBottomBarColorProvider(this, 2); + + maskexpCurveEditorG->curveListComplete(); + + blendmaskexp->setAdjusterListener(this); + + radmaskexp->setLogScale(10, -10); + radmaskexp->setAdjusterListener(this); + + lapmaskexp->setAdjusterListener(this); + + chromaskexp->setAdjusterListener(this); + + gammaskexp->setAdjusterListener(this); + + slomaskexp->setAdjusterListener(this); + + gradFramemask->set_label_align(0.025, 0.5); + + strmaskexp->setAdjusterListener(this); + + angmaskexp->setAdjusterListener(this); + angmaskexp->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + + mask2expCurveEditorG->setCurveListener(this); + + Lmaskexpshape->setResetCurve(DiagonalCurveType(defSpot.Lmaskexpcurve.at(0)), defSpot.Lmaskexpcurve); + Lmaskexpshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1}}); + Lmaskexpshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1}}); + + mask2expCurveEditorG->curveListComplete(); + + // Add Color & Light specific widgets to GUI + pack_start(*expMethod); + ToolParamBlock* const pdeBox = Gtk::manage(new ToolParamBlock()); + pdeBox->pack_start(*laplacexp); + pdeBox->pack_start(*linear); + pdeBox->pack_start(*balanexp); + pdeBox->pack_start(*gamm); + Gtk::HBox* const ctboxexpmethod = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labelexpmethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_NOISEMETH") + ":")); + ctboxexpmethod->pack_start(*labelexpmethod, Gtk::PACK_SHRINK, 4); + ctboxexpmethod->pack_start(*exnoiseMethod); + pdeBox->pack_start(*ctboxexpmethod); + pdeFrame->add(*pdeBox); + pack_start(*pdeFrame); + ToolParamBlock* const fatBox = Gtk::manage(new ToolParamBlock()); + fatBox->pack_start(*fatamount); + fatBox->pack_start(*fatdetail); + fatBox->pack_start(*fatlevel); + fatBox->pack_start(*fatanchor); + fatFrame->add(*fatBox); + pack_start(*fatFrame); + pack_start(*expcomp); + pack_start(*sensiex); + pack_start(*structexp); + pack_start(*blurexpde); + ToolParamBlock* const toolBox = Gtk::manage(new ToolParamBlock()); +// toolBox->pack_start(*expcomp); + toolBox->pack_start(*black); + toolBox->pack_start(*hlcompr); + toolBox->pack_start(*hlcomprthresh); + toolBox->pack_start(*shadex); + toolBox->pack_start(*shcompr); + toolBox->pack_start(*expchroma); + toolBox->pack_start(*curveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + exptoolexp->add(*toolBox, false); + pack_start(*exptoolexp); + ToolParamBlock* const gradBox = Gtk::manage(new ToolParamBlock()); + gradBox->pack_start(*strexp); + gradBox->pack_start(*angexp); + expgradexp->add(*gradBox, false); + pack_start(*expgradexp); + pack_start(*softradiusexp); + pack_start(*inversex); + ToolParamBlock* const maskexpBox = Gtk::manage(new ToolParamBlock()); + maskexpBox->pack_start(*showmaskexpMethod, Gtk::PACK_SHRINK, 4); + maskexpBox->pack_start(*showmaskexpMethodinv, Gtk::PACK_SHRINK, 4); + maskexpBox->pack_start(*enaExpMask, Gtk::PACK_SHRINK, 0); + // maskexpBox->pack_start(*enaExpMaskaft, Gtk::PACK_SHRINK, 0); + maskexpBox->pack_start(*maskexpCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskexpBox->pack_start(*blendmaskexp, Gtk::PACK_SHRINK, 0); + maskexpBox->pack_start(*radmaskexp, Gtk::PACK_SHRINK, 0); + maskexpBox->pack_start(*lapmaskexp, Gtk::PACK_SHRINK, 0); + maskexpBox->pack_start(*chromaskexp, Gtk::PACK_SHRINK, 0); + maskexpBox->pack_start(*gammaskexp, Gtk::PACK_SHRINK, 0); + maskexpBox->pack_start(*slomaskexp, Gtk::PACK_SHRINK, 0); + ToolParamBlock* const gradmaskBox = Gtk::manage(new ToolParamBlock()); + gradmaskBox->pack_start(*strmaskexp); + gradmaskBox->pack_start(*angmaskexp); + gradFramemask->add(*gradmaskBox); + maskexpBox->pack_start(*gradFramemask); + maskexpBox->pack_start(*mask2expCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + expmaskexp->add(*maskexpBox, false); + pack_start(*expmaskexp, false, false); +} + +LocallabExposure::~LocallabExposure() +{ + delete curveEditorG; + delete maskexpCurveEditorG; + delete mask2expCurveEditorG; +} + +bool LocallabExposure::isMaskViewActive() +{ + return ((showmaskexpMethod->get_active_row_number() != 0) || (showmaskexpMethodinv->get_active_row_number() != 0)); +} + +void LocallabExposure::resetMaskView() +{ + showmaskexpMethodConn.block(true); + showmaskexpMethodConninv.block(true); + + showmaskexpMethod->set_active(0); + showmaskexpMethodinv->set_active(0); + + showmaskexpMethodConn.block(false); + showmaskexpMethodConninv.block(false); +} + +void LocallabExposure::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + expMask = showmaskexpMethod->get_active_row_number(); + expMaskinv = showmaskexpMethodinv->get_active_row_number(); +} + +void LocallabExposure::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_EXPOSURE_TOOLTIP")); + expMethod->set_tooltip_text(M("TP_LOCALLAB_EXPMETHOD_TOOLTIP")); + structexp->set_tooltip_text(M("TP_LOCALLAB_STRUCT_TOOLTIP")); + pdeFrame->set_tooltip_text(M("TP_LOCALLAB_PDEFRAME_TOOLTIP")); + laplacexp->set_tooltip_text(M("TP_LOCALLAB_EXPLAP_TOOLTIP")); + balanexp->set_tooltip_text(M("TP_LOCALLAB_EXPLAPBAL_TOOLTIP")); + gamm->set_tooltip_text(M("TP_LOCALLAB_EXPLAPGAMM_TOOLTIP")); + linear->set_tooltip_text(M("TP_LOCALLAB_EXPLAPLIN_TOOLTIP")); + exnoiseMethod->set_tooltip_text(M("TP_LOCALLAB_EXPNOISEMETHOD_TOOLTIP")); + fatFrame->set_tooltip_text(M("TP_LOCALLAB_FATFRAME_TOOLTIP")); + sensiex->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); + shapeexpos->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_TONES_TOOLTIP")); + strexp->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); + expmaskexp->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmaskexpshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskexpshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskexpshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + radmaskexp->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmaskexp->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + strmaskexp->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); + Lmaskexpshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + blendmaskexp->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2expCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + expchroma->set_tooltip_text(M("TP_LOCALLAB_EXPCHROMA_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + expMethod->set_tooltip_text(""); + structexp->set_tooltip_text(""); + pdeFrame->set_tooltip_text(""); + exnoiseMethod->set_tooltip_text(""); + laplacexp->set_tooltip_text(M("")); + balanexp->set_tooltip_text(M("")); + gamm->set_tooltip_text(M("")); + linear->set_tooltip_text(M("")); + fatFrame->set_tooltip_text(""); + sensiex->set_tooltip_text(""); + shapeexpos->setTooltip(""); + strexp->set_tooltip_text(""); + expmaskexp->set_tooltip_text(""); + CCmaskexpshape->setTooltip(""); + LLmaskexpshape->setTooltip(""); + HHmaskexpshape->setTooltip(""); + radmaskexp->set_tooltip_text(""); + lapmaskexp->set_tooltip_text(""); + strmaskexp->set_tooltip_text(""); + Lmaskexpshape->setTooltip(""); + blendmaskexp->set_tooltip_text(M("")); + mask2expCurveEditorG->set_tooltip_text(M("")); + expchroma->set_tooltip_text(M("")); + } +} + +void LocallabExposure::setDefaultExpanderVisibility() +{ + exptoolexp->set_expanded(false); + expgradexp->set_expanded(false); + expmaskexp->set_expanded(false); +} + +void LocallabExposure::disableListener() +{ + LocallabTool::disableListener(); + + expMethodConn.block(true); + exnoiseMethodConn.block(true); + inversexConn.block(true); + showmaskexpMethodConn.block(true); + showmaskexpMethodConninv.block(true); + enaExpMaskConn.block(true); + enaExpMaskaftConn.block(true); +} + +void LocallabExposure::enableListener() +{ + LocallabTool::enableListener(); + + expMethodConn.block(false); + exnoiseMethodConn.block(false); + inversexConn.block(false); + showmaskexpMethodConn.block(false); + showmaskexpMethodConninv.block(false); + enaExpMaskConn.block(false); + enaExpMaskaftConn.block(false); +} + +void LocallabExposure::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visiexpose); + exp->setEnabled(spot.expexpose); + complexity->set_active(spot.complexexpose); + + if (spot.expMethod == "std") { + expMethod->set_active(0); + } else if (spot.expMethod == "pde") { + expMethod->set_active(1); + } + + laplacexp->setValue(spot.laplacexp); + linear->setValue(spot.linear); + balanexp->setValue(spot.balanexp); + gamm->setValue(spot.gamm); + + if (spot.exnoiseMethod == "one") { + exnoiseMethod->set_active(0); + } else if (spot.exnoiseMethod == "med") { + exnoiseMethod->set_active(1); + } else if (spot.exnoiseMethod == "medhi") { + exnoiseMethod->set_active(2); + } + + fatamount->setValue(spot.fatamount); + fatdetail->setValue(spot.fatdetail); + fatlevel->setValue(spot.fatlevel); + fatanchor->setValue(spot.fatanchor); + sensiex->setValue(spot.sensiex); + structexp->setValue(spot.structexp); + blurexpde->setValue(spot.blurexpde); + expcomp->setValue(spot.expcomp); + black->setValue(spot.black); + hlcompr->setValue(spot.hlcompr); + hlcomprthresh->setValue(spot.hlcomprthresh); + shadex->setValue(spot.shadex); + shcompr->setValue(spot.shcompr); + expchroma->setValue(spot.expchroma); + shapeexpos->setCurve(spot.excurve); + strexp->setValue(spot.strexp); + angexp->setValue(spot.angexp); + softradiusexp->setValue(spot.softradiusexp); + inversex->set_active(spot.inversex); + enaExpMask->set_active(spot.enaExpMask); + enaExpMaskaft->set_active(spot.enaExpMaskaft); + CCmaskexpshape->setCurve(spot.CCmaskexpcurve); + LLmaskexpshape->setCurve(spot.LLmaskexpcurve); + HHmaskexpshape->setCurve(spot.HHmaskexpcurve); + blendmaskexp->setValue(spot.blendmaskexp); + radmaskexp->setValue(spot.radmaskexp); + lapmaskexp->setValue(spot.lapmaskexp); + chromaskexp->setValue(spot.chromaskexp); + gammaskexp->setValue(spot.gammaskexp); + slomaskexp->setValue(spot.slomaskexp); + strmaskexp->setValue(spot.strmaskexp); + angmaskexp->setValue(spot.angmaskexp); + Lmaskexpshape->setCurve(spot.Lmaskexpcurve); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update shcompr sensitivity according to black and shadex value + updateExposureGUI1(); + + // Update exposure GUI according to expMethod value + updateExposureGUI2(); + + // Update exposure GUI according to inversex button state + updateExposureGUI3(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabExposure::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expexpose = exp->getEnabled(); + spot.visiexpose = exp->get_visible(); + spot.complexexpose = complexity->get_active_row_number(); + + if (expMethod->get_active_row_number() == 0) { + spot.expMethod = "std"; + } else if (expMethod->get_active_row_number() == 1) { + spot.expMethod = "pde"; + } + + spot.laplacexp = laplacexp->getValue(); + spot.linear = linear->getValue(); + spot.balanexp = balanexp->getValue(); + spot.gamm = gamm->getValue(); + + if (exnoiseMethod->get_active_row_number() == 0) { + spot.exnoiseMethod = "none"; + } else if (exnoiseMethod->get_active_row_number() == 1) { + spot.exnoiseMethod = "med"; + } else if (exnoiseMethod->get_active_row_number() == 2) { + spot.exnoiseMethod = "medhi"; + } + + spot.fatamount = fatamount->getValue(); + spot.fatdetail = fatdetail->getValue(); + spot.fatlevel = fatlevel->getValue(); + spot.fatanchor = fatanchor->getValue(); + spot.sensiex = sensiex->getIntValue(); + spot.structexp = structexp->getIntValue(); + spot.blurexpde = blurexpde->getIntValue(); + spot.expcomp = expcomp->getValue(); + spot.black = black->getIntValue(); + spot.hlcompr = hlcompr->getIntValue(); + spot.hlcomprthresh = hlcomprthresh->getIntValue(); + spot.shadex = shadex->getIntValue(); + spot.shcompr = shcompr->getIntValue(); + spot.expchroma = expchroma->getIntValue(); + spot.excurve = shapeexpos->getCurve(); + spot.strexp = strexp->getValue(); + spot.angexp = angexp->getValue(); + spot.softradiusexp = softradiusexp->getValue(); + spot.inversex = inversex->get_active(); + spot.enaExpMask = enaExpMask->get_active(); + spot.enaExpMaskaft = enaExpMaskaft->get_active(); + spot.CCmaskexpcurve = CCmaskexpshape->getCurve(); + spot.LLmaskexpcurve = LLmaskexpshape->getCurve(); + spot.HHmaskexpcurve = HHmaskexpshape->getCurve(); + spot.blendmaskexp = blendmaskexp->getIntValue(); + spot.radmaskexp = radmaskexp->getValue(); + spot.lapmaskexp = lapmaskexp->getValue(); + spot.chromaskexp = chromaskexp->getValue(); + spot.gammaskexp = gammaskexp->getValue(); + spot.slomaskexp = slomaskexp->getValue(); + spot.strmaskexp = strmaskexp->getValue(); + spot.angmaskexp = angmaskexp->getValue(); + spot.Lmaskexpcurve = Lmaskexpshape->getCurve(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabExposure::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + laplacexp->setDefault(defSpot.laplacexp); + linear->setDefault(defSpot.linear); + balanexp->setDefault(defSpot.balanexp); + gamm->setDefault(defSpot.gamm); + fatamount->setDefault(defSpot.fatamount); + fatdetail->setDefault(defSpot.fatdetail); + fatlevel->setDefault(defSpot.fatlevel); + fatanchor->setDefault(defSpot.fatanchor); + sensiex->setDefault((double)defSpot.sensiex); + structexp->setDefault((double)defSpot.structexp); + blurexpde->setDefault((double)defSpot.blurexpde); + expcomp->setDefault(defSpot.expcomp); + black->setDefault((double)defSpot.black); + hlcompr->setDefault((double)defSpot.hlcompr); + hlcomprthresh->setDefault((double)defSpot.hlcomprthresh); + shadex->setDefault((double)defSpot.shadex); + shcompr->setDefault((double)defSpot.shcompr); + expchroma->setDefault((double)defSpot.expchroma); + strexp->setDefault(defSpot.strexp); + angexp->setDefault(defSpot.angexp); + softradiusexp->setDefault(defSpot.softradiusexp); + blendmaskexp->setDefault((double)defSpot.blendmaskexp); + radmaskexp->setDefault(defSpot.radmaskexp); + lapmaskexp->setDefault(defSpot.lapmaskexp); + chromaskexp->setDefault(defSpot.chromaskexp); + gammaskexp->setDefault(defSpot.gammaskexp); + slomaskexp->setDefault(defSpot.slomaskexp); + strmaskexp->setDefault(defSpot.strmaskexp); + angmaskexp->setDefault(defSpot.angmaskexp); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabExposure::adjusterChanged(Adjuster* a, double newval) +{ + // Update shcompr sensitivity according to black and shadex value + if (a == black || a == shadex) { + updateExposureGUI1(); + } + + if (isLocActivated && exp->getEnabled()) { + if (a == laplacexp) { + if (listener) { + listener->panelChanged(Evlocallablaplacexp, + laplacexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == linear) { + if (listener) { + listener->panelChanged(Evlocallablinear, + linear->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == balanexp) { + if (listener) { + listener->panelChanged(Evlocallabbalanexp, + balanexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gamm) { + if (listener) { + listener->panelChanged(Evlocallabgamm, + gamm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == fatamount) { + if (listener) { + listener->panelChanged(Evlocallabfatamount, + fatamount->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == fatdetail) { + if (listener) { + listener->panelChanged(Evlocallabfatdetail, + fatdetail->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == fatlevel) { + if (listener) { + listener->panelChanged(Evlocallabfatlevel, + fatlevel->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == fatanchor) { + if (listener) { + listener->panelChanged(Evlocallabfatanchor, + fatanchor->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensiex) { + if (listener) { + listener->panelChanged(Evlocallabsensiex, + sensiex->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == structexp) { + if (listener) { + listener->panelChanged(Evlocallabstructexp, + structexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blurexpde) { + if (listener) { + listener->panelChanged(Evlocallabblurexpde, + blurexpde->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == expcomp) { + if (listener) { + listener->panelChanged(Evlocallabexpcomp, + expcomp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == black) { + if (listener) { + listener->panelChanged(Evlocallabblack, + black->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == hlcompr) { + if (listener) { + listener->panelChanged(Evlocallabhlcompr, + hlcompr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == hlcomprthresh) { + if (listener) { + listener->panelChanged(Evlocallabhlcomprthresh, + hlcomprthresh->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shadex) { + if (listener) { + listener->panelChanged(Evlocallabshadex, + shadex->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shcompr) { + if (listener) { + listener->panelChanged(Evlocallabshcompr, + shcompr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == expchroma) { + if (listener) { + listener->panelChanged(Evlocallabexpchroma, + expchroma->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strexp) { + if (listener) { + listener->panelChanged(Evlocallabstrexp, + strexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == angexp) { + if (listener) { + listener->panelChanged(Evlocallabangexp, + angexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == softradiusexp) { + if (listener) { + listener->panelChanged(Evlocallabsoftradiusexp, + softradiusexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskexp) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskexp, + blendmaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskexp) { + if (listener) { + listener->panelChanged(Evlocallabradmaskexp, + radmaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskexp) { + if (listener) { + listener->panelChanged(Evlocallablapmaskexp, + lapmaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskexp) { + if (listener) { + listener->panelChanged(Evlocallabchromaskexp, + chromaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskexp) { + if (listener) { + listener->panelChanged(Evlocallabgammaskexp, + gammaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskexp) { + if (listener) { + listener->panelChanged(Evlocallabslomaskexp, + slomaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strmaskexp) { + if (listener) { + listener->panelChanged(Evlocallabstrmaskexp, + strmaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == angmaskexp) { + if (listener) { + listener->panelChanged(Evlocallabangmaskexp, + angmaskexp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabExposure::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == shapeexpos) { + if (listener) { + listener->panelChanged(Evlocallabshapeexpos, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == CCmaskexpshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskexpshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskexpshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskexpshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskexpshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskexpshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmaskexpshape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskexpshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabExposure::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenaexpose, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenaexpose, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabExposure::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + blurexpde->setValue((double)defSpot.blurexpde); + lapmaskexp->setValue(defSpot.lapmaskexp); + gammaskexp->setValue(defSpot.gammaskexp); + slomaskexp->setValue(defSpot.slomaskexp); + strmaskexp->setValue(defSpot.strmaskexp); + angmaskexp->setValue(defSpot.angmaskexp); + // laplacexp->setValue(defSpot.laplacexp); + // linear->setValue(defSpot.linear); + // balanexp->setValue(defSpot.balanexp); + // gamm->setValue(defSpot.gamm); + + // Enable all listeners + enableListener(); +} + +void LocallabExposure::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + lapmaskexp->hide(); + gammaskexp->hide(); + slomaskexp->hide(); + gradFramemask->hide(); + blurexpde->hide(); + // pdeFrame->hide(); + } else { + // Advanced widgets are shown in Expert mode + lapmaskexp->show(); + gammaskexp->show(); + slomaskexp->show(); + gradFramemask->show(); + blurexpde->show(); + // pdeFrame->show(); + } +} + +void LocallabExposure::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskexpshape->updateLocallabBackground(normChromar); + LLmaskexpshape->updateLocallabBackground(normLumar); + HHmaskexpshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabExposure::expMethodChanged() +{ + // Update exposure GUI according to expMethod value + updateExposureGUI2(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabexpMethod, + expMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabExposure::exnoiseMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabexnoiseMethod, + exnoiseMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabExposure::inversexChanged() +{ + // Update exposure GUI according to inversex button state + updateExposureGUI3(); + + // This event is called to transmit potentially reset mask state + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (inversex->get_active()) { + listener->panelChanged(Evlocallabinversex, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabinversex, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabExposure::showmaskexpMethodChanged() +{ + // If mask preview is activated, deactivate other Exposure mask preview + showmaskexpMethodConninv.block(true); + showmaskexpMethodinv->set_active(0); + showmaskexpMethodConninv.block(false); + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabExposure::showmaskexpMethodChangedinv() +{ + // If mask preview is activated, deactivate other Exposure mask preview + showmaskexpMethodConn.block(true); + showmaskexpMethod->set_active(0); + showmaskexpMethodConn.block(false); + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabExposure::enaExpMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enaExpMask->get_active()) { + listener->panelChanged(EvLocallabEnaExpMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaExpMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabExposure::enaExpMaskaftChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enaExpMaskaft->get_active()) { + listener->panelChanged(EvLocallabEnaExpMaskaft, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaExpMaskaft, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabExposure::updateExposureGUI1() +{ + // Update shcompr sensitivity according to black and shadex value + if (black->getIntValue() == 0 && shadex->getIntValue() == 0) { + shcompr->set_sensitive(false); + } else { + shcompr->set_sensitive(true); + } +} + +void LocallabExposure::updateExposureGUI2() +{ + // Update exposure GUI according to expMethod value + if (expMethod->get_active_row_number() == 0) { + pdeFrame->hide(); + fatFrame->hide(); + softradiusexp->set_sensitive(true); + sensiex->set_sensitive(false); + } else if (expMethod->get_active_row_number() == 1) { + pdeFrame->show(); + fatFrame->show(); + softradiusexp->set_sensitive(false); + sensiex->set_sensitive(true); + } +} + +void LocallabExposure::updateExposureGUI3() +{ + // Update exposure GUI according to inversex button state + if (inversex->get_active()) { + expMethod->hide(); + + // Manage specific case where expMethod is different from 0 + if (expMethod->get_active_row_number() > 0) { + expMethodConn.block(true); + expMethod->set_active(0); + expMethodConn.block(false); + + // Update GUI accordingly + updateExposureGUI2(); + } + + structexp->hide(); + shadex->hide(); + softradiusexp->hide(); + expgradexp->hide(); + showmaskexpMethod->hide(); + // Reset hidden mask combobox + showmaskexpMethodConn.block(true); + showmaskexpMethod->set_active(0); + showmaskexpMethodConn.block(false); + showmaskexpMethodinv->show(); + } else { + expMethod->show(); + structexp->show(); + shadex->show(); + softradiusexp->show(); + expgradexp->show(); + showmaskexpMethodinv->hide(); + // Reset hidden mask combobox + showmaskexpMethodConninv.block(true); + showmaskexpMethodinv->set_active(0); + showmaskexpMethodConninv.block(false); + showmaskexpMethod->show(); + } +} + +/* ==== LocallabShadow ==== */ +LocallabShadow::LocallabShadow(): + LocallabTool(this, M("TP_LOCALLAB_SH_TOOLNAME"), M("TP_LOCALLAB_SHADHIGH"), false), + + // Shadow highlight specific widgets + shMethod(Gtk::manage(new MyComboBoxText())), + multipliersh([]() -> std::array + { + std::array res = {}; + + for (unsigned int i = 0; i < res.size(); ++i) { + Glib::ustring ss = Glib::ustring::format(i); + + if (i == 0) { + ss += Glib::ustring::compose(" (%1)", M("TP_LOCALLAB_LUMADARKEST")); + } else if (i == 4) { + ss += Glib::ustring::compose(" (%1)", M("TP_LOCALLAB_LUMAWHITESEST")); + } + + res[i] = Gtk::manage(new Adjuster(std::move(ss), -100, 100, 1, 0)); + } + + return res; + } + ()), + detailSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DETAILSH"), -5, 5, 1, 0))), + highlights(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), 0, 100, 1, 0))), + h_tonalwidth(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_HLTONALW"), 10, 100, 1, 70))), + shadows(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_SHADOWS"), 0, 100, 1, 0))), + s_tonalwidth(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_SHTONALW"), 10, 100, 1, 30))), + sh_radius(Gtk::manage(new Adjuster(M("TP_SHADOWSHLIGHTS_RADIUS"), 0, 100, 1, 40))), + sensihs(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 15))), + blurSHde(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURDE"), 2, 100, 1, 5))), + gamFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GAMFRA")))), + gamSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMSH"), 0.25, 15.0, 0.01, 2.4))), + sloSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOSH"), 0.0, 150.0, 0.01, 12.92))), + expgradsh(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPGRAD")))), + strSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -4., 4., 0.05, 0.))), + angSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))), + inverssh(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))), + expmasksh(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWS")))), + showmaskSHMethod(Gtk::manage(new MyComboBoxText())), + showmaskSHMethodinv(Gtk::manage(new MyComboBoxText())), + enaSHMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + maskSHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + mask2SHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + LmaskSHshape(static_cast(mask2SHCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + fatSHFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_FATSHFRA")))), + fatamountSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))), + fatanchorSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATANCHOR"), 1., 100., 1., 50., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter Shadow highlight specific widgets + shMethod->append(M("TP_LOCALLAB_SH1")); + shMethod->append(M("TP_LOCALLAB_SH2")); + shMethod->set_active(0); + shMethodConn = shMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabShadow::shMethodChanged)); + + for (unsigned int i = 0; i < multipliersh.size(); i++) { + multipliersh[i]->setAdjusterListener(this); + } + + detailSH->setAdjusterListener(this); + + highlights->setAdjusterListener(this); + + h_tonalwidth->setAdjusterListener(this); + + shadows->setAdjusterListener(this); + + s_tonalwidth->setAdjusterListener(this); + + sh_radius->setAdjusterListener(this); + + sensihs->setAdjusterListener(this); + + blurSHde->setAdjusterListener(this); + + gamSH->setAdjusterListener(this); + + sloSH->setAdjusterListener(this); + + setExpandAlignProperties(expgradsh, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + strSH->setAdjusterListener(this); + + angSH->setAdjusterListener(this); + angSH->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + + inversshConn = inverssh->signal_toggled().connect(sigc::mem_fun(*this, &LocallabShadow::inversshChanged)); + inverssh->set_tooltip_text(M("TP_LOCALLAB_INVERS_TOOLTIP")); + + setExpandAlignProperties(expmasksh, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmaskSHMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskSHMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskSHMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskSHMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskSHMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskSHMethod->set_active(0); + showmaskSHMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskSHMethodConn = showmaskSHMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabShadow::showmaskSHMethodChanged)); + + showmaskSHMethodinv->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskSHMethodinv->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskSHMethodinv->set_active(0); + showmaskSHMethodinv->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskSHMethodConninv = showmaskSHMethodinv->signal_changed().connect(sigc::mem_fun(*this, &LocallabShadow::showmaskSHMethodChangedinv)); + + enaSHMaskConn = enaSHMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabShadow::enaSHMaskChanged)); + + maskSHCurveEditorG->setCurveListener(this); + + CCmaskSHshape->setIdentityValue(0.); + CCmaskSHshape->setResetCurve(FlatCurveType(defSpot.CCmaskSHcurve.at(0)), defSpot.CCmaskSHcurve); + CCmaskSHshape->setBottomBarColorProvider(this, 1); + + LLmaskSHshape->setIdentityValue(0.); + LLmaskSHshape->setResetCurve(FlatCurveType(defSpot.LLmaskSHcurve.at(0)), defSpot.LLmaskSHcurve); + LLmaskSHshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmaskSHshape->setIdentityValue(0.); + HHmaskSHshape->setResetCurve(FlatCurveType(defSpot.HHmaskSHcurve.at(0)), defSpot.HHmaskSHcurve); + HHmaskSHshape->setCurveColorProvider(this, 2); + HHmaskSHshape->setBottomBarColorProvider(this, 2); + + maskSHCurveEditorG->curveListComplete(); + + blendmaskSH->setAdjusterListener(this); + + radmaskSH->setLogScale(10, -10); + radmaskSH->setAdjusterListener(this); + + lapmaskSH->setAdjusterListener(this); + + chromaskSH->setAdjusterListener(this); + + gammaskSH->setAdjusterListener(this); + + slomaskSH->setAdjusterListener(this); + + mask2SHCurveEditorG->setCurveListener(this); + + LmaskSHshape->setResetCurve(DiagonalCurveType(defSpot.LmaskSHcurve.at(0)), defSpot.LmaskSHcurve); + LmaskSHshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + LmaskSHshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2SHCurveEditorG->curveListComplete(); + + fatSHFrame->set_label_align(0.025, 0.5); + + fatamountSH->setAdjusterListener(this); + + fatanchorSH->setAdjusterListener(this); + + // Add Shadow highlight specific widgets to GUI + pack_start(*shMethod); + + for (int i = 0; i < 5; ++i) { + pack_start(*multipliersh[i]); + } + + pack_start(*detailSH); + pack_start(*highlights); + pack_start(*h_tonalwidth); + pack_start(*shadows); + pack_start(*s_tonalwidth); + pack_start(*sh_radius); +// pack_start(*sensihs); + pack_start(*blurSHde); +// Gtk::Frame* const gamFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GAMFRA"))); + gamFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const gammBox = Gtk::manage(new ToolParamBlock()); + gammBox->pack_start(*gamSH); + gammBox->pack_start(*sloSH); + gamFrame->add(*gammBox); + pack_start(*gamFrame); + ToolParamBlock* const gradSHBox = Gtk::manage(new ToolParamBlock()); + gradSHBox->pack_start(*strSH); + gradSHBox->pack_start(*angSH); + expgradsh->add(*gradSHBox, false); + pack_start(*expgradsh); + pack_start(*inverssh); + ToolParamBlock* const maskSHBox = Gtk::manage(new ToolParamBlock()); + maskSHBox->pack_start(*showmaskSHMethod, Gtk::PACK_SHRINK, 4); + maskSHBox->pack_start(*showmaskSHMethodinv, Gtk::PACK_SHRINK, 4); + maskSHBox->pack_start(*enaSHMask, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*maskSHCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskSHBox->pack_start(*blendmaskSH, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*radmaskSH, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*lapmaskSH, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*chromaskSH, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*gammaskSH, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*slomaskSH, Gtk::PACK_SHRINK, 0); + maskSHBox->pack_start(*mask2SHCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + ToolParamBlock* const fatSHBox = Gtk::manage(new ToolParamBlock()); + fatSHBox->pack_start(*fatamountSH); + fatSHBox->pack_start(*fatanchorSH); + fatSHFrame->add(*fatSHBox); +// maskSHBox->pack_start(*fatSHFrame); + expmasksh->add(*maskSHBox, false); + pack_start(*expmasksh, false, false); +} + +LocallabShadow::~LocallabShadow() +{ + delete maskSHCurveEditorG; + delete mask2SHCurveEditorG; +} + +bool LocallabShadow::isMaskViewActive() +{ + return ((showmaskSHMethod->get_active_row_number() != 0) || (showmaskSHMethodinv->get_active_row_number() != 0)); +} + +void LocallabShadow::resetMaskView() +{ + showmaskSHMethodConn.block(true); + showmaskSHMethodConninv.block(true); + + showmaskSHMethod->set_active(0); + showmaskSHMethodinv->set_active(0); + + showmaskSHMethodConn.block(false); + showmaskSHMethodConninv.block(false); +} + +void LocallabShadow::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + shMask = showmaskSHMethod->get_active_row_number(); + shMaskinv = showmaskSHMethodinv->get_active_row_number(); +} + +void LocallabShadow::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_SHADOWHIGHLIGHT_TOOLTIP")); + strSH->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); + expmasksh->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmaskSHshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskSHshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskSHshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + radmaskSH->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmaskSH->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + LmaskSHshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + for (unsigned int i = 0; i < multipliersh.size(); i++) { + multipliersh[i]->set_tooltip_text(M("TP_LOCALLAB_MULTIPL_TOOLTIP")); + } + gamSH->set_tooltip_text(M("TP_LOCALLAB_SHTRC_TOOLTIP")); + sloSH->set_tooltip_text(M("TP_LOCALLAB_SHTRC_TOOLTIP")); + blendmaskSH->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2SHCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + strSH->set_tooltip_text(""); + expmasksh->set_tooltip_text(""); + CCmaskSHshape->setTooltip(""); + LLmaskSHshape->setTooltip(""); + HHmaskSHshape->setTooltip(""); + radmaskSH->set_tooltip_text(""); + lapmaskSH->set_tooltip_text(""); + LmaskSHshape->setTooltip(""); + for (unsigned int i = 0; i < multipliersh.size(); i++) { + multipliersh[i]->set_tooltip_text(M("")); + } + gamSH->set_tooltip_text(M("")); + sloSH->set_tooltip_text(M("")); + blendmaskSH->set_tooltip_text(M("")); + mask2SHCurveEditorG->set_tooltip_text(M("")); + } +} + +void LocallabShadow::setDefaultExpanderVisibility() +{ + expgradsh->set_expanded(false); + expmasksh->set_expanded(false); +} + +void LocallabShadow::disableListener() +{ + LocallabTool::disableListener(); + + shMethodConn.block(true); + inversshConn.block(true); + showmaskSHMethodConn.block(true); + showmaskSHMethodConninv.block(true); + enaSHMaskConn.block(true); +} + +void LocallabShadow::enableListener() +{ + LocallabTool::enableListener(); + + shMethodConn.block(false); + inversshConn.block(false); + showmaskSHMethodConn.block(false); + showmaskSHMethodConninv.block(false); + enaSHMaskConn.block(false); +} + +void LocallabShadow::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visishadhigh); + exp->setEnabled(spot.expshadhigh); + complexity->set_active(spot.complexshadhigh); + + if (spot.shMethod == "std") { + shMethod->set_active(0); + } else if (spot.shMethod == "tone") { + shMethod->set_active(1); + } + + for (int i = 0; i < 5; i++) { + multipliersh[i]->setValue((double)spot.multsh[i]); + } + + detailSH->setValue((double)spot.detailSH); + highlights->setValue((double)spot.highlights); + h_tonalwidth->setValue((double)spot.h_tonalwidth); + shadows->setValue(spot.shadows); + s_tonalwidth->setValue((double)spot.s_tonalwidth); + sh_radius->setValue((double)spot.sh_radius); + sensihs->setValue((double)spot.sensihs); + blurSHde->setValue((double)spot.blurSHde); + gamSH->setValue(spot.gamSH); + sloSH->setValue(spot.sloSH); + strSH->setValue(spot.strSH); + angSH->setValue(spot.angSH); + inverssh->set_active(spot.inverssh); + enaSHMask->set_active(spot.enaSHMask); + CCmaskSHshape->setCurve(spot.CCmaskSHcurve); + LLmaskSHshape->setCurve(spot.LLmaskSHcurve); + HHmaskSHshape->setCurve(spot.HHmaskSHcurve); + blendmaskSH->setValue((double)spot.blendmaskSH); + radmaskSH->setValue(spot.radmaskSH); + lapmaskSH->setValue(spot.lapmaskSH); + chromaskSH->setValue(spot.chromaskSH); + gammaskSH->setValue(spot.gammaskSH); + slomaskSH->setValue(spot.slomaskSH); + LmaskSHshape->setCurve(spot.LmaskSHcurve); + fatamountSH->setValue(spot.fatamountSH); + fatanchorSH->setValue(spot.fatanchorSH); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update shadow highlight GUI according to inverssh button state + updateShadowGUI1(); + + // Update shadow highlight GUI according to shMethod combobox state + updateShadowGUI2(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabShadow::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expshadhigh = exp->getEnabled(); + spot.visishadhigh = exp->get_visible(); + spot.complexshadhigh = complexity->get_active_row_number(); + + if (shMethod->get_active_row_number() == 0) { + spot.shMethod = "std"; + } else if (shMethod->get_active_row_number() == 1) { + spot.shMethod = "tone"; + } + + for (int i = 0; i < 5; i++) { + spot.multsh[i] = multipliersh[i]->getIntValue(); + } + + spot.detailSH = detailSH->getIntValue(); + spot.highlights = highlights->getIntValue(); + spot.h_tonalwidth = h_tonalwidth->getIntValue(); + spot.shadows = shadows->getIntValue(); + spot.s_tonalwidth = s_tonalwidth->getIntValue(); + spot.sh_radius = sh_radius->getIntValue(); + spot.sensihs = sensihs->getIntValue(); + spot.blurSHde = blurSHde->getIntValue(); + spot.gamSH = gamSH->getValue(); + spot.sloSH = sloSH->getValue(); + spot.strSH = strSH->getValue(); + spot.angSH = angSH->getValue(); + spot.inverssh = inverssh->get_active(); + spot.enaSHMask = enaSHMask->get_active(); + spot.LLmaskSHcurve = LLmaskSHshape->getCurve(); + spot.CCmaskSHcurve = CCmaskSHshape->getCurve(); + spot.HHmaskSHcurve = HHmaskSHshape->getCurve(); + spot.blendmaskSH = blendmaskSH->getIntValue(); + spot.radmaskSH = radmaskSH->getValue(); + spot.lapmaskSH = lapmaskSH->getValue(); + spot.chromaskSH = chromaskSH->getValue(); + spot.gammaskSH = gammaskSH->getValue(); + spot.slomaskSH = slomaskSH->getValue(); + spot.LmaskSHcurve = LmaskSHshape->getCurve(); + spot.fatamountSH = fatamountSH->getValue(); + spot.fatanchorSH = fatanchorSH->getValue(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabShadow::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + for (int i = 0; i < 5; i++) { + multipliersh[i]->setDefault(defSpot.multsh[i]); + } + + detailSH->setDefault((double)defSpot.detailSH); + highlights->setDefault((double)defSpot.highlights); + h_tonalwidth->setDefault((double)defSpot.h_tonalwidth); + shadows->setDefault((double)defSpot.shadows); + s_tonalwidth->setDefault((double)defSpot.s_tonalwidth); + sh_radius->setDefault((double)defSpot.sh_radius); + sensihs->setDefault((double)defSpot.sensihs); + blurSHde->setDefault((double)defSpot.blurSHde); + gamSH->setDefault(defSpot.gamSH); + sloSH->setDefault(defSpot.sloSH); + strSH->setDefault(defSpot.strSH); + angSH->setDefault(defSpot.angSH); + blendmaskSH->setDefault((double)defSpot.blendmaskSH); + radmaskSH->setDefault(defSpot.radmaskSH); + lapmaskSH->setDefault(defSpot.lapmaskSH); + chromaskSH->setDefault(defSpot.chromaskSH); + gammaskSH->setDefault(defSpot.gammaskSH); + slomaskSH->setDefault(defSpot.slomaskSH); + fatamountSH->setDefault(defSpot.fatamountSH); + fatanchorSH->setDefault(defSpot.fatanchorSH); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabShadow::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == multipliersh[0] || a == multipliersh[1] || a == multipliersh[2] || a == multipliersh[3] || a == multipliersh[4]) { + if (listener) { + listener->panelChanged(EvlocallabEqualizersh, + Glib::ustring::compose("%1, %2, %3, %4, %5", + Glib::ustring::format(std::fixed, std::setprecision(2), multipliersh[0]->getIntValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multipliersh[1]->getIntValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multipliersh[2]->getIntValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multipliersh[3]->getIntValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multipliersh[4]->getIntValue())) + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == detailSH) { + if (listener) { + listener->panelChanged(EvlocallabdetailSH, + detailSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == highlights) { + if (listener) { + listener->panelChanged(Evlocallabhighlights, + highlights->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == h_tonalwidth) { + if (listener) { + listener->panelChanged(Evlocallabh_tonalwidth, + h_tonalwidth->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shadows) { + if (listener) { + listener->panelChanged(Evlocallabshadows, + shadows->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == s_tonalwidth) { + if (listener) { + listener->panelChanged(Evlocallabs_tonalwidth, + s_tonalwidth->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sh_radius) { + if (listener) { + listener->panelChanged(Evlocallabsh_radius, + sh_radius->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensihs) { + if (listener) { + listener->panelChanged(Evlocallabsensihs, + sensihs->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blurSHde) { + if (listener) { + listener->panelChanged(EvlocallabblurSHde, + blurSHde->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gamSH) { + if (listener) { + listener->panelChanged(EvlocallabgamSH, + gamSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sloSH) { + if (listener) { + listener->panelChanged(EvlocallabsloSH, + sloSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strSH) { + if (listener) { + listener->panelChanged(EvlocallabstrSH, + strSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == angSH) { + if (listener) { + listener->panelChanged(EvlocallabangSH, + angSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskSH) { + if (listener) { + listener->panelChanged(EvlocallabblendmaskSH, + blendmaskSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskSH) { + if (listener) { + listener->panelChanged(EvlocallabradmaskSH, + radmaskSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskSH) { + if (listener) { + listener->panelChanged(EvlocallablapmaskSH, + lapmaskSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskSH) { + if (listener) { + listener->panelChanged(EvlocallabchromaskSH, + chromaskSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskSH) { + if (listener) { + listener->panelChanged(EvlocallabgammaskSH, + gammaskSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskSH) { + if (listener) { + listener->panelChanged(EvlocallabslomaskSH, + slomaskSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == fatamountSH) { + if (listener) { + listener->panelChanged(EvlocallabfatamountSH, + fatamountSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + + if (a == fatanchorSH) { + if (listener) { + listener->panelChanged(EvlocallabfatanchorSH, + fatanchorSH->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabShadow::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == CCmaskSHshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskSHshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskSHshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskSHshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskSHshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskSHshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LmaskSHshape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskSHshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabShadow::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenashadhigh, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenashadhigh, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabShadow::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + blurSHde->setValue((double)defSpot.blurSHde); + lapmaskSH->setValue(defSpot.lapmaskSH); + gammaskSH->setValue(defSpot.gammaskSH); + slomaskSH->setValue(defSpot.slomaskSH); + fatamountSH->setValue(defSpot.fatamountSH); + fatanchorSH->setValue(defSpot.fatanchorSH); + + // Enable all listeners + enableListener(); +} + +void LocallabShadow::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + blurSHde->hide(); + lapmaskSH->hide(); + gammaskSH->hide(); + slomaskSH->hide(); + fatSHFrame->hide(); + } else { + // Advanced widgets are shown in Expert mode + blurSHde->show(); + lapmaskSH->show(); + gammaskSH->show(); + slomaskSH->show(); + fatSHFrame->show(); + } +} + +void LocallabShadow::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskSHshape->updateLocallabBackground(normChromar); + LLmaskSHshape->updateLocallabBackground(normLumar); + HHmaskSHshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabShadow::shMethodChanged() +{ + + // Update shadow highlight GUI according to shMethod combobox state + updateShadowGUI2(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabshMethod, + shMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabShadow::inversshChanged() +{ + // Update shadow highlight GUI according to inverssh button state + updateShadowGUI1(); + + // This event is called to transmit potentially reset mask state + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (inverssh->get_active()) { + listener->panelChanged(Evlocallabinverssh, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabinverssh, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabShadow::showmaskSHMethodChanged() +{ + // If mask preview is activated, deactivate other Shadow highlight mask preview + showmaskSHMethodConninv.block(true); + showmaskSHMethodinv->set_active(0); + showmaskSHMethodConninv.block(false); + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabShadow::showmaskSHMethodChangedinv() +{ + // If mask preview is activated, deactivate other Shadow highlight mask preview + showmaskSHMethodConn.block(true); + showmaskSHMethod->set_active(0); + showmaskSHMethodConn.block(false); + + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabShadow::enaSHMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enaSHMask->get_active()) { + listener->panelChanged(EvLocallabEnaSHMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaSHMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabShadow::updateShadowGUI1() +{ + // Update shadow highlight GUI according to inverssh button state + if (inverssh->get_active()) { + expgradsh->hide(); + showmaskSHMethod->hide(); + // Reset hidden mask combobox + showmaskSHMethodConn.block(true); + showmaskSHMethod->set_active(0); + showmaskSHMethodConn.block(false); + showmaskSHMethodinv->show(); + } else { + expgradsh->show(); + showmaskSHMethod->show(); + showmaskSHMethodinv->hide(); + // Reset hidden mask combobox + showmaskSHMethodConninv.block(true); + showmaskSHMethodinv->set_active(0); + showmaskSHMethodConninv.block(false); + } +} + +void LocallabShadow::updateShadowGUI2() +{ + // Update shadow highlight GUI according to shMethod combobox state + if (shMethod->get_active_row_number() == 0) { + for (int i = 0; i < 5; i++) { + multipliersh[i]->hide(); + } + gamFrame->hide(); + detailSH->hide(); + highlights->show(); + h_tonalwidth->show(); + shadows->show(); + s_tonalwidth->show(); + sh_radius->show(); + } else if (shMethod->get_active_row_number() == 1) { + for (int i = 0; i < 5; i++) { + multipliersh[i]->show(); + } + gamFrame->show(); + + detailSH->show(); + highlights->hide(); + h_tonalwidth->hide(); + shadows->hide(); + s_tonalwidth->hide(); + sh_radius->hide(); + } +} + +/* ==== LocallabVibrance ==== */ +LocallabVibrance::LocallabVibrance(): + LocallabTool(this, M("TP_LOCALLAB_VIB_TOOLNAME"), M("TP_LOCALLAB_VIBRANCE"), false), + + // Vibrance specific widgets + saturated(Gtk::manage(new Adjuster(M("TP_VIBRANCE_SATURATED"), -100., 100., 1., 0.))), + pastels(Gtk::manage(new Adjuster(M("TP_VIBRANCE_PASTELS"), -100., 100., 1., 0.))), + warm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_WARM"), -100., 100., 1., 0., Gtk::manage(new RTImage("circle-blue-small.png")), Gtk::manage(new RTImage("circle-orange-small.png"))))), + psThreshold(Gtk::manage(new ThresholdAdjuster(M("TP_VIBRANCE_PSTHRESHOLD"), -100., 100., 0., M("TP_VIBRANCE_PSTHRESHOLD_WEIGTHING"), 0, 0., 100., 75., M("TP_VIBRANCE_PSTHRESHOLD_SATTHRESH"), 0, this, false))), + protectSkins(Gtk::manage(new Gtk::CheckButton(M("TP_VIBRANCE_PROTECTSKINS")))), + avoidColorShift(Gtk::manage(new Gtk::CheckButton(M("TP_VIBRANCE_AVOIDCOLORSHIFT")))), + pastSatTog(Gtk::manage(new Gtk::CheckButton(M("TP_VIBRANCE_PASTSATTOG")))), + sensiv(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 15))), + curveEditorGG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL"))), + skinTonesCurve(static_cast(curveEditorGG->addCurve(CT_Diagonal, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES")))), + expgradvib(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPGRAD")))), + strvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -4., 4., 0.05, 0.))), + strvibab(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTRCHRO"), -4., 4., 0.05, 0.))), + strvibh(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTRHUE2"), -6., 6., 0.05, 0.))), + angvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))), + expmaskvib(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWVI")))), + showmaskvibMethod(Gtk::manage(new MyComboBoxText())), + enavibMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + maskvibCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + mask2vibCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskvibshape(static_cast(mask2vibCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) +{ + float R, G, B; + + const LocallabParams::LocallabSpot defSpot; + + // Parameter Vibrance specific widgets + saturated->setAdjusterListener(this); + + pastels->setAdjusterListener(this); + + warm->setAdjusterListener(this); + + psThreshold->set_tooltip_markup(M("TP_VIBRANCE_PSTHRESHOLD_TOOLTIP")); + psThreshold->setAdjusterListener(this); + + pskinsConn = protectSkins->signal_toggled().connect(sigc::mem_fun(*this, &LocallabVibrance::protectskins_toggled)); + + ashiftConn = avoidColorShift->signal_toggled().connect(sigc::mem_fun(*this, &LocallabVibrance::avoidcolorshift_toggled)); + + pastsattogConn = pastSatTog->signal_toggled().connect(sigc::mem_fun(*this, &LocallabVibrance::pastsattog_toggled)); + + sensiv->setAdjusterListener(this); + + curveEditorGG->setCurveListener(this); + + skinTonesCurve->setTooltip(M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_TOOLTIP")); + std::vector mskinTonesCurve; + // -0.1 rad < Hue < 1.6 rad + Color::hsv2rgb01(0.92f, 0.45f, 0.6f, R, G, B); + mskinTonesCurve.emplace_back(0.0, R, G, B); + Color::hsv2rgb01(0.14056f, 0.45f, 0.6f, R, G, B); + mskinTonesCurve.emplace_back(0.0, R, G, B); + skinTonesCurve->setBottomBarBgGradient(mskinTonesCurve); + skinTonesCurve->setLeftBarBgGradient(mskinTonesCurve); + skinTonesCurve->setRangeLabels( + M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE1"), M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE2"), + M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE3"), M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE4") + ); + skinTonesCurve->setRangeDefaultMilestones(0.1, 0.4, 0.85); + + curveEditorGG->curveListComplete(); + + setExpandAlignProperties(expgradvib, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + strvib->setAdjusterListener(this); + + strvibab->set_tooltip_text(M("TP_LOCALLAB_GRADSTRAB_TOOLTIP")); + strvibab->setAdjusterListener(this); + + strvibh->set_tooltip_text(M("TP_LOCALLAB_GRADSTRHUE_TOOLTIP")); + strvibh->setAdjusterListener(this); + + angvib->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + angvib->setAdjusterListener(this); + + setExpandAlignProperties(expmaskvib, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmaskvibMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskvibMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskvibMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskvibMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskvibMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskvibMethod->set_active(0); + showmaskvibMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskvibMethodConn = showmaskvibMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabVibrance::showmaskvibMethodChanged)); + + enavibMaskConn = enavibMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabVibrance::enavibMaskChanged)); + + maskvibCurveEditorG->setCurveListener(this); + + CCmaskvibshape->setIdentityValue(0.); + CCmaskvibshape->setResetCurve(FlatCurveType(defSpot.CCmaskvibcurve.at(0)), defSpot.CCmaskvibcurve); + CCmaskvibshape->setBottomBarColorProvider(this, 1); + + LLmaskvibshape->setIdentityValue(0.); + LLmaskvibshape->setResetCurve(FlatCurveType(defSpot.LLmaskvibcurve.at(0)), defSpot.LLmaskvibcurve); + LLmaskvibshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmaskvibshape->setIdentityValue(0.); + HHmaskvibshape->setResetCurve(FlatCurveType(defSpot.HHmaskvibcurve.at(0)), defSpot.HHmaskvibcurve); + HHmaskvibshape->setCurveColorProvider(this, 2); + HHmaskvibshape->setBottomBarColorProvider(this, 2); + + maskvibCurveEditorG->curveListComplete(); + + blendmaskvib->setAdjusterListener(this); + + radmaskvib->setLogScale(10, -10); + radmaskvib->setAdjusterListener(this); + + lapmaskvib->setAdjusterListener(this); + + chromaskvib->setAdjusterListener(this); + + gammaskvib->setAdjusterListener(this); + + slomaskvib->setAdjusterListener(this); + + mask2vibCurveEditorG->setCurveListener(this); + + Lmaskvibshape->setResetCurve(DiagonalCurveType(defSpot.Lmaskvibcurve.at(0)), defSpot.Lmaskvibcurve); + Lmaskvibshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmaskvibshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2vibCurveEditorG->curveListComplete(); + + // Add Vibrance specific widgets to GUI + pack_start(*saturated, Gtk::PACK_SHRINK, 0); + pack_start(*pastels, Gtk::PACK_SHRINK, 0); + pack_start(*warm, Gtk::PACK_SHRINK, 0); + pack_start(*psThreshold, Gtk::PACK_SHRINK, 0); + pack_start(*protectSkins, Gtk::PACK_SHRINK, 0); + pack_start(*avoidColorShift, Gtk::PACK_SHRINK, 0); + pack_start(*pastSatTog, Gtk::PACK_SHRINK, 0); +// pack_start(*sensiv, Gtk::PACK_SHRINK, 0); + pack_start(*curveEditorGG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + ToolParamBlock* const gradvibBox = Gtk::manage(new ToolParamBlock()); + gradvibBox->pack_start(*strvib); + gradvibBox->pack_start(*strvibab); + gradvibBox->pack_start(*strvibh); + gradvibBox->pack_start(*angvib); + expgradvib->add(*gradvibBox, false); + pack_start(*expgradvib); + ToolParamBlock* const maskvibBox = Gtk::manage(new ToolParamBlock()); + maskvibBox->pack_start(*showmaskvibMethod, Gtk::PACK_SHRINK, 4); + maskvibBox->pack_start(*enavibMask, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*maskvibCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskvibBox->pack_start(*blendmaskvib, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*radmaskvib, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*lapmaskvib, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*chromaskvib, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*gammaskvib, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*slomaskvib, Gtk::PACK_SHRINK, 0); + maskvibBox->pack_start(*mask2vibCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + expmaskvib->add(*maskvibBox, false); + pack_start(*expmaskvib, false, false); +} + +LocallabVibrance::~LocallabVibrance() +{ + delete curveEditorGG; + delete maskvibCurveEditorG; + delete mask2vibCurveEditorG; +} + +bool LocallabVibrance::isMaskViewActive() +{ + return (showmaskvibMethod->get_active_row_number() != 0); +} + +void LocallabVibrance::resetMaskView() +{ + showmaskvibMethodConn.block(true); + showmaskvibMethod->set_active(0); + showmaskvibMethodConn.block(false); +} + +void LocallabVibrance::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + vibMask = showmaskvibMethod->get_active_row_number(); +} + +void LocallabVibrance::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + warm->set_tooltip_text(M("TP_LOCALLAB_WARM_TOOLTIP")); + strvib->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); + expmaskvib->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmaskvibshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskvibshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskvibshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + Lmaskvibshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + blendmaskvib->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2vibCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + } else { + warm->set_tooltip_text(""); + strvib->set_tooltip_text(""); + expmaskvib->set_tooltip_text(""); + CCmaskvibshape->setTooltip(""); + LLmaskvibshape->setTooltip(""); + HHmaskvibshape->setTooltip(""); + Lmaskvibshape->setTooltip(""); + blendmaskvib->set_tooltip_text(M("")); + mask2vibCurveEditorG->set_tooltip_text(M("")); + } +} + +void LocallabVibrance::setDefaultExpanderVisibility() +{ + expgradvib->set_expanded(false); + expmaskvib->set_expanded(false); +} + +void LocallabVibrance::disableListener() +{ + LocallabTool::disableListener(); + + pskinsConn.block(true); + ashiftConn.block(true); + pastsattogConn.block(true); + showmaskvibMethodConn.block(true); + enavibMaskConn.block(true); +} + +void LocallabVibrance::enableListener() +{ + LocallabTool::enableListener(); + + pskinsConn.block(false); + ashiftConn.block(false); + pastsattogConn.block(false); + showmaskvibMethodConn.block(false); + enavibMaskConn.block(false); +} + +void LocallabVibrance::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visivibrance); + exp->setEnabled(spot.expvibrance); + complexity->set_active(spot.complexvibrance); + + saturated->setValue(spot.saturated); + pastels->setValue(spot.pastels); + warm->setValue(spot.warm); + psThreshold->setValue(spot.psthreshold); + protectSkins->set_active(spot.protectskins); + avoidColorShift->set_active(spot.avoidcolorshift); + pastSatTog->set_active(spot.pastsattog); + sensiv->setValue(spot.sensiv); + skinTonesCurve->setCurve(spot.skintonescurve); + strvib->setValue(spot.strvib); + strvibab->setValue(spot.strvibab); + strvibh->setValue(spot.strvibh); + angvib->setValue(spot.angvib); + enavibMask->set_active(spot.enavibMask); + CCmaskvibshape->setCurve(spot.CCmaskvibcurve); + LLmaskvibshape->setCurve(spot.LLmaskvibcurve); + HHmaskvibshape->setCurve(spot.HHmaskvibcurve); + blendmaskvib->setValue(spot.blendmaskvib); + radmaskvib->setValue(spot.radmaskvib); + lapmaskvib->setValue(spot.lapmaskvib); + chromaskvib->setValue(spot.chromaskvib); + gammaskvib->setValue(spot.gammaskvib); + slomaskvib->setValue(spot.slomaskvib); + Lmaskvibshape->setCurve(spot.Lmaskvibcurve); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update vibrance GUI according to pastsattog button state + updateVibranceGUI(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabVibrance::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expvibrance = exp->getEnabled(); + spot.visivibrance = exp->get_visible(); + spot.complexvibrance = complexity->get_active_row_number(); + + spot.saturated = saturated->getIntValue(); + spot.pastels = pastels->getIntValue(); + spot.warm = warm->getIntValue(); + spot.psthreshold = psThreshold->getValue(); + spot.protectskins = protectSkins->get_active(); + spot.avoidcolorshift = avoidColorShift->get_active(); + spot.pastsattog = pastSatTog->get_active(); + spot.sensiv = sensiv->getIntValue(); + spot.skintonescurve = skinTonesCurve->getCurve(); + spot.strvib = strvib->getValue(); + spot.strvibab = strvibab->getValue(); + spot.strvibh = strvibh->getValue(); + spot.angvib = angvib->getValue(); + spot.enavibMask = enavibMask->get_active(); + spot.CCmaskvibcurve = CCmaskvibshape->getCurve(); + spot.LLmaskvibcurve = LLmaskvibshape->getCurve(); + spot.HHmaskvibcurve = HHmaskvibshape->getCurve(); + spot.blendmaskvib = blendmaskvib->getIntValue(); + spot.radmaskvib = radmaskvib->getValue(); + spot.lapmaskvib = lapmaskvib->getValue(); + spot.chromaskvib = chromaskvib->getValue(); + spot.gammaskvib = gammaskvib->getValue(); + spot.slomaskvib = slomaskvib->getValue(); + spot.Lmaskvibcurve = Lmaskvibshape->getCurve(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabVibrance::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster and threshold adjuster widgets + saturated->setDefault((double)defSpot.saturated); + pastels->setDefault((double)defSpot.pastels); + warm->setDefault((double)defSpot.warm); + psThreshold->setDefault(defSpot.psthreshold); + sensiv->setDefault((double)defSpot.sensiv); + strvib->setDefault(defSpot.strvib); + strvibab->setDefault(defSpot.strvibab); + strvibh->setDefault(defSpot.strvibh); + angvib->setDefault(defSpot.angvib); + blendmaskvib->setDefault((double)defSpot.blendmaskvib); + radmaskvib->setDefault(defSpot.radmaskvib); + lapmaskvib->setDefault(defSpot.lapmaskvib); + chromaskvib->setDefault(defSpot.chromaskvib); + gammaskvib->setDefault(defSpot.gammaskvib); + slomaskvib->setDefault(defSpot.slomaskvib); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabVibrance::adjusterChanged(Adjuster* a, double newval) +{ + // Copy pastels adjuster value to saturated one according to pastSatTog button state + if (a == pastels && pastSatTog->get_active()) { + saturated->setValue(newval); + } + + if (isLocActivated && exp->getEnabled()) { + if (a == saturated && !pastSatTog->get_active()) { + if (listener) { + listener->panelChanged(EvlocallabSaturated, + saturated->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == pastels) { + if (listener) { + listener->panelChanged(EvlocallabPastels, + pastels->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == warm) { + if (listener) { + listener->panelChanged(Evlocallabwarm, + warm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensiv) { + if (listener) { + listener->panelChanged(Evlocallabsensiv, + sensiv->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strvib) { + if (listener) { + listener->panelChanged(Evlocallabstrvib, + strvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strvibab) { + if (listener) { + listener->panelChanged(Evlocallabstrvibab, + strvibab->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strvibh) { + if (listener) { + listener->panelChanged(Evlocallabstrvibh, + strvibh->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == angvib) { + if (listener) { + listener->panelChanged(Evlocallabangvib, + angvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskvib) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskvi, + blendmaskvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskvib) { + if (listener) { + listener->panelChanged(Evlocallabradmaskvib, + radmaskvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskvib) { + if (listener) { + listener->panelChanged(Evlocallablapmaskvib, + lapmaskvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskvib) { + if (listener) { + listener->panelChanged(Evlocallabchromaskvib, + chromaskvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskvib) { + if (listener) { + listener->panelChanged(Evlocallabgammaskvib, + gammaskvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskvib) { + if (listener) { + listener->panelChanged(Evlocallabslomaskvib, + slomaskvib->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabPastSatThreshold, + psThreshold->getHistoryString() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +std::vector LocallabVibrance::getCurvePoints(ThresholdSelector* tAdjuster) const +{ + std::vector points; + double threshold, transitionWeighting; + tAdjuster->getPositions(transitionWeighting, threshold); // ( range -100;+100, range 0;+100 ) + transitionWeighting /= 100.; // range -1., +1. + threshold /= 100.; // range 0., +1. + + // Initial point + points.push_back(0.); + points.push_back(0.); + + double p2 = 3.0 * threshold / 4.0; // same one than in ipvibrance.cc + double s0 = threshold + (1.0 - threshold) / 4.0; // same one than in ipvibrance.cc + + // point at the beginning of the first linear transition + points.push_back(p2); + points.push_back(0.); + + // Y value of the chroma mean point, calculated to get a straight line between p2 and s0 + double chromaMean = (threshold / 4.0) / (s0 - p2); + + // move chromaMean up or down depending on transitionWeighting + if (transitionWeighting > 0.0) { + // positive values -> give more weight to Saturated + chromaMean = (1.0 - chromaMean) * transitionWeighting + chromaMean; + } else if (transitionWeighting < 0.0) { + // negative values -> give more weight to Pastels + chromaMean = chromaMean * transitionWeighting + chromaMean; + } + + // point at the location of the Top cursor, at the end of the first linear transition and the beginning of the second one + points.push_back(threshold); + points.push_back(chromaMean); + + if (threshold < 1.0) { + // point at the end of the second linear transition + points.push_back(s0); + points.push_back(1.0); + + // end point + points.push_back(1.0); + points.push_back(1.0); + } + + return points; +} + +void LocallabVibrance::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == skinTonesCurve) { + if (listener) { + listener->panelChanged(EvlocallabSkinTonesCurve, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == CCmaskvibshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskvibshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskvibshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskvibshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskvibshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskvibshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmaskvibshape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskvibshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenavibrance, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenashadhigh, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + saturated->setValue((double)defSpot.saturated); + psThreshold->setValue(defSpot.psthreshold); + protectSkins->set_active(defSpot.protectskins); + avoidColorShift->set_active(defSpot.avoidcolorshift); + pastSatTog->set_active(defSpot.pastsattog); + skinTonesCurve->setCurve(defSpot.skintonescurve); + strvibab->setValue(defSpot.strvibab); + strvibh->setValue(defSpot.strvibh); + lapmaskvib->setValue(defSpot.lapmaskvib); + gammaskvib->setValue(defSpot.gammaskvib); + slomaskvib->setValue(defSpot.slomaskvib); + + // Enable all listeners + enableListener(); + + // Update GUI based on converted widget parameters: + // - Update vibrance GUI according to pastsattog button state + updateVibranceGUI(); +} + +void LocallabVibrance::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + saturated->hide(); + pastels->setLabel(M("TP_LOCALLAB_PASTELS2")); + psThreshold->hide(); + protectSkins->hide(); + avoidColorShift->hide(); + pastSatTog->hide(); + curveEditorGG->hide(); + strvibab->hide(); + strvibh->hide(); + lapmaskvib->hide(); + gammaskvib->hide(); + slomaskvib->hide(); + } else { + // Advanced widgets are shown in Expert mode + saturated->show(); + pastels->setLabel(M("TP_VIBRANCE_PASTELS")); + psThreshold->show(); + protectSkins->show(); + avoidColorShift->show(); + pastSatTog->show(); + curveEditorGG->show(); + strvibab->show(); + strvibh->show(); + lapmaskvib->show(); + gammaskvib->show(); + slomaskvib->show(); + } +} + +void LocallabVibrance::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskvibshape->updateLocallabBackground(normChromar); + LLmaskvibshape->updateLocallabBackground(normLumar); + HHmaskvibshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabVibrance::protectskins_toggled() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (protectSkins->get_active()) { + listener->panelChanged(EvlocallabProtectSkins, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvlocallabProtectSkins, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::avoidcolorshift_toggled() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (avoidColorShift->get_active()) { + listener->panelChanged(EvlocallabAvoidColorShift, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvlocallabAvoidColorShift, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::pastsattog_toggled() +{ + // Update vibrance GUI according to pastsattog button state + updateVibranceGUI(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (pastSatTog->get_active()) { + listener->panelChanged(EvlocallabPastSatTog, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvlocallabPastSatTog, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::showmaskvibMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabVibrance::enavibMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enavibMask->get_active()) { + listener->panelChanged(EvLocallabEnavibMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnavibMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabVibrance::updateVibranceGUI() +{ + // Update vibrance GUI according to pastsattog button state + if (pastSatTog->get_active()) { + // Link both slider, so we set saturated and psThresholds unsensitive + psThreshold->set_sensitive(false); + saturated->set_sensitive(false); + saturated->setValue(pastels->getValue()); // Pastels and Saturated are linked + } else { + // Separate sliders, so we set saturated and psThresholds sensitive again + psThreshold->set_sensitive(true); + saturated->set_sensitive(true); + } +} + +/* ==== LocallabSoft ==== */ +LocallabSoft::LocallabSoft(): + LocallabTool(this, M("TP_LOCALLAB_SOFT_TOOLNAME"), M("TP_LOCALLAB_SOFT"), false), + + // Soft light specific widgets + softMethod(Gtk::manage(new MyComboBoxText())), + ctboxsoftmethod(Gtk::manage(new Gtk::HBox())), + showmasksoftMethod(Gtk::manage(new MyComboBoxText())), + streng(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRENG"), 1, 100, 1, 1))), + laplace(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPLACE"), 0., 100., 0.5, 25.))), + sensisf(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 1, 100, 1, 30))) +{ + // Parameter Soft light specific widgets + softMethod->append(M("TP_LOCALLAB_SOFTM")); + softMethod->append(M("TP_LOCALLAB_RETIM")); + softMethod->set_active(0); + softMethodConn = softMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabSoft::softMethodChanged)); + + showmasksoftMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmasksoftMethod->append(M("TP_LOCALLAB_SHOWLAPLACE")); + showmasksoftMethod->append(M("TP_LOCALLAB_SHOWFOURIER")); + showmasksoftMethod->append(M("TP_LOCALLAB_SHOWPOISSON")); + showmasksoftMethod->append(M("TP_LOCALLAB_SHOWNORMAL")); + showmasksoftMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmasksoftMethod->set_active(0); +// showmasksoftMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP")); + showmasksoftMethodConn = showmasksoftMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabSoft::showmasksoftMethodChanged)); + + streng->setAdjusterListener(this); + + laplace->setAdjusterListener(this); + + sensisf->setAdjusterListener(this); + + // Add Soft light specific widgets to GUI + pack_start(*softMethod); + Gtk::Label* const labelsoftmethod = Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_SHOWDCT") + ":")); + ctboxsoftmethod->pack_start(*labelsoftmethod, Gtk::PACK_SHRINK, 4); + ctboxsoftmethod->pack_start(*showmasksoftMethod); + pack_start(*ctboxsoftmethod); + pack_start(*streng); + pack_start(*laplace); + pack_start(*sensisf); +} + +bool LocallabSoft::isMaskViewActive() +{ + return (showmasksoftMethod->get_active_row_number() != 0); +} + +void LocallabSoft::resetMaskView() +{ + showmasksoftMethodConn.block(true); + showmasksoftMethod->set_active(0); + showmasksoftMethodConn.block(false); +} + +void LocallabSoft::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + softMask = showmasksoftMethod->get_active_row_number(); +} + +void LocallabSoft::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + softMethod->set_tooltip_markup(M("TP_LOCALLAB_SOFTMETHOD_TOOLTIP")); + // ctboxsoftmethod->set_tooltip_markup(M("TP_LOCALLAB_ORRETISTEP_TOOLTIP")); + showmasksoftMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKSOFT_TOOLTIP")); + streng->set_tooltip_text(M("TP_LOCALLAB_ORRETISTREN_TOOLTIP")); + laplace->set_tooltip_text(M("TP_LOCALLAB_ORRETILAP_TOOLTIP")); + } else { + softMethod->set_tooltip_markup(M("")); + // ctboxsoftmethod->set_tooltip_markup(M("")); + showmasksoftMethod->set_tooltip_markup(M("")); + streng->set_tooltip_text(M("")); + laplace->set_tooltip_text(M("")); + } +} + +void LocallabSoft::disableListener() +{ + LocallabTool::disableListener(); + + softMethodConn.block(true); + showmasksoftMethodConn.block(true); +} + +void LocallabSoft::enableListener() +{ + LocallabTool::enableListener(); + + softMethodConn.block(false); + showmasksoftMethodConn.block(false); +} + +void LocallabSoft::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visisoft); + exp->setEnabled(spot.expsoft); + complexity->set_active(spot.complexsoft); + + if (spot.softMethod == "soft") { + softMethod->set_active(0); + } else if (spot.softMethod == "reti") { + softMethod->set_active(1); + } + + streng->setValue((double)spot.streng); + sensisf->setValue((double)spot.sensisf); + laplace->setValue(spot.laplace); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update soft light GUI according to softMethod combobox + updateSoftGUI(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabSoft::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expsoft = exp->getEnabled(); + spot.visisoft = exp->get_visible(); + spot.complexsoft = complexity->get_active_row_number(); + + if (softMethod->get_active_row_number() == 0) { + spot.softMethod = "soft"; + } else if (softMethod->get_active_row_number() == 1) { + spot.softMethod = "reti"; + } + + spot.streng = streng->getIntValue(); + spot.sensisf = sensisf->getIntValue(); + spot.laplace = laplace->getValue(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabSoft::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default value for adjuster widgets + streng->setDefault((double)defSpot.streng); + laplace->setDefault(defSpot.laplace); + sensisf->setDefault((double)defSpot.sensisf); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabSoft::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == streng) { + if (listener) { + listener->panelChanged(Evlocallabstreng, + streng->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensisf) { + if (listener) { + listener->panelChanged(Evlocallabsensisf, + sensisf->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == laplace) { + if (listener) { + listener->panelChanged(Evlocallablaplace, + laplace->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabSoft::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenasoft, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenasoft, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabSoft::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + if (defSpot.softMethod == "soft") { + softMethod->set_active(0); + } else if (defSpot.softMethod == "reti") { + softMethod->set_active(1); + } + + // Enable all listeners + enableListener(); + + // Update GUI based on converted widget parameters: + // - Update soft light GUI according to softMethod combobox + updateSoftGUI(); +} + +void LocallabSoft::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + softMethod->hide(); + } else { + // Advanced widgets are shown in Expert mode + softMethod->show(); + } +} + +void LocallabSoft::softMethodChanged() +{ + // Update soft light GUI according to softMethod combobox + updateSoftGUI(); + + // This event is called to transmit potentially reset mask state + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabsoftMethod, + softMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabSoft::showmasksoftMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabSoft::updateSoftGUI() +{ + // Update soft light GUI according to softMethod combobox + if (softMethod->get_active_row_number() == 0) { + ctboxsoftmethod->hide(); + // Reset hidden mask combobox + showmasksoftMethodConn.block(true); + showmasksoftMethod->set_active(0); + showmasksoftMethodConn.block(false); + laplace->hide(); + } else { + ctboxsoftmethod->show(); + laplace->show(); + } +} + +/* ==== LocallabBlur ==== */ +LocallabBlur::LocallabBlur(): + LocallabTool(this, M("TP_LOCALLAB_BLUR_TOOLNAME"), M("TP_LOCALLAB_BLUFR"), true), + + // Blur, Noise & Denoise specific widgets + expblnoise(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_BLNOI_EXP")))), + blMethod(Gtk::manage(new MyComboBoxText())), + fftwbl(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTWBLUR")))), + radius(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADIUS"), MINRAD, MAXRAD, 0.1, 1.5, nullptr, nullptr, &blurSlider2radius, &blurRadius2Slider))), + strength(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRENGTH"), 0, 100, 1, 0))), + grainFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRAINFRA")))), + isogr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_ISOGR"), 20, 6400, 1, 0))), + strengr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRENGR"), 0, 100, 1, 0))), + scalegr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SCALEGR"), 0, 100, 1, 100))), + medMethod(Gtk::manage(new MyComboBoxText())), + itera(Gtk::manage(new Adjuster(M("TP_DIRPYRDENOISE_MEDIAN_PASSES"), 1, 4, 1, 1))), + guidbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GUIDBL"), 0, 1000, 1, 0))), + strbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRBL"), 0, 100, 1, 50))), + epsbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_EPSBL"), -10, 10, 1, 0))), + sensibn(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIBN"), 0, 100, 1, 40))), + blurMethod(Gtk::manage(new MyComboBoxText())), + chroMethod(Gtk::manage(new MyComboBoxText())), + activlum(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ACTIV")))), + expdenoise(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_DENOI_EXP")))), + LocalcurveEditorwavden(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVDEN"))), + wavshapeden(static_cast(LocalcurveEditorwavden->addCurve(CT_Flat, "", nullptr, false, false))), + noiselumf0(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMFINEZERO"), MINCHRO, MAXCHRO, 0.01, 0.))), + noiselumf(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMFINE"), MINCHRO, MAXCHRO, 0.01, 0.))), + noiselumf2(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMFINETWO"), MINCHRO, MAXCHRO, 0.01, 0.))), + noiselumc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMCOARSE"), MINCHRO, MAXCHROCC, 0.01, 0.))), + noiselumdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMDETAIL"), 0., 100., 0.01, 0.))), + noiselequal(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELEQUAL"), -2, 10, 1, 7, Gtk::manage(new RTImage("circle-white-small.png")), Gtk::manage(new RTImage("circle-black-small.png"))))), + noisechrof(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROFINE"), MINCHRO, MAXCHRO, 0.01, 0.))), + noisechroc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROCOARSE"), MINCHRO, MAXCHROCC, 0.01, 0.))), + noisechrodetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHRODETAIL"), 0., 100., 0.01, 0.))), + detailthr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DETAILTHR"), 0, 100, 1, 0))), + adjblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_ADJ"), -100., 100., 1., 0., Gtk::manage(new RTImage("circle-blue-yellow-small.png")), Gtk::manage(new RTImage("circle-red-green-small.png"))))), + bilateral(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BILATERAL"), 0, 100, 1, 0))), + sensiden(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIDEN"), 0, 100, 1, 60))), + expmaskbl(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWPLUS")))), + showmaskblMethod(Gtk::manage(new MyComboBoxText())), + showmaskblMethodtyp(Gtk::manage(new MyComboBoxText())), + enablMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + maskblCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmaskblshape(static_cast(maskblCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskblshape(static_cast(maskblCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskblshape(static_cast(maskblCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + strumaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STRUMASKCOL"), 0., 200., 0.1, 0.))), + toolbl(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TOOLCOL")))), + blendmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), + slomaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + shadmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_HIGHMASKCOL"), 0, 100, 1, 0))), + shadmaskblsha(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), + mask2blCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + Lmaskblshape(static_cast(mask2blCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + mask2blCurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmaskblshapewav(static_cast(mask2blCurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + csThresholdblur(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter Blur, Noise & Denoise specific widgets + setExpandAlignProperties(expblnoise, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + blMethod->append(M("TP_LOCALLAB_BLUR")); + blMethod->append(M("TP_LOCALLAB_BLMED")); + blMethod->append(M("TP_LOCALLAB_BLGUID")); + blMethod->set_active(0); + blMethodConn = blMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabBlur::blMethodChanged)); + + fftwblConn = fftwbl->signal_toggled().connect(sigc::mem_fun(*this, &LocallabBlur::fftwblChanged)); + + radius->setAdjusterListener(this); + + strength->setAdjusterListener(this); + + grainFrame->set_label_align(0.025, 0.5); + + isogr->setAdjusterListener(this); + + strengr->setAdjusterListener(this); + + scalegr->setAdjusterListener(this); + + medMethod->append(M("TP_LOCALLAB_MEDNONE")); + medMethod->append(M("TP_DIRPYRDENOISE_TYPE_3X3")); + medMethod->append(M("TP_DIRPYRDENOISE_TYPE_5X5")); + medMethod->append(M("TP_DIRPYRDENOISE_TYPE_7X7")); + medMethod->append(M("TP_DIRPYRDENOISE_TYPE_9X9")); + medMethod->set_active(0); + medMethodConn = medMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabBlur::medMethodChanged)); + + itera->setAdjusterListener(this); + + guidbl->setLogScale(100, 0); + guidbl->setAdjusterListener(this); + + strbl->setAdjusterListener(this); + + epsbl->setAdjusterListener(this); + + sensibn->setAdjusterListener(this); + + blurMethod->append(M("TP_LOCALLAB_BLNORM")); + blurMethod->append(M("TP_LOCALLAB_BLINV")); + blurMethod->set_active(0); + blurMethodConn = blurMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabBlur::blurMethodChanged)); + + chroMethod->append(M("TP_LOCALLAB_BLLO")); + chroMethod->append(M("TP_LOCALLAB_BLCO")); + chroMethod->append(M("TP_LOCALLAB_BLLC")); + chroMethod->set_active(0); + chroMethodConn = chroMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabBlur::chroMethodChanged)); + + activlumConn = activlum->signal_toggled().connect(sigc::mem_fun(*this, &LocallabBlur::activlumChanged)); + + setExpandAlignProperties(expdenoise, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + LocalcurveEditorwavden->setCurveListener(this); + + wavshapeden->setIdentityValue(0.); + wavshapeden->setResetCurve(FlatCurveType(defSpot.locwavcurveden.at(0)), defSpot.locwavcurveden); + + LocalcurveEditorwavden->curveListComplete(); + + noiselumf0->setAdjusterListener(this); + + noiselumf->setAdjusterListener(this); + + noiselumf2->setAdjusterListener(this); + + noiselumc->setAdjusterListener(this); + + noiselumdetail->setAdjusterListener(this); + + noiselequal->setAdjusterListener(this); + + noisechrof->setAdjusterListener(this); + + noisechroc->set_tooltip_text(M("TP_LOCALLAB_NOISECHROC_TOOLTIP")); + noisechroc->setAdjusterListener(this); + + noisechrodetail->setAdjusterListener(this); + + detailthr->setAdjusterListener(this); + + adjblur->setAdjusterListener(this); + + bilateral->setAdjusterListener(this); + + sensiden->setAdjusterListener(this); + + setExpandAlignProperties(expmaskbl, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmaskblMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskblMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskblMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskblMethod->append(M("TP_LOCALLAB_SHOWMASK")); +// showmaskblMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskblMethod->set_active(0); + showmaskblMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskblMethodConn = showmaskblMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabBlur::showmaskblMethodChanged)); + + showmaskblMethodtyp->append(M("TP_LOCALLAB_SHOWMASKTYP1")); + showmaskblMethodtyp->append(M("TP_LOCALLAB_SHOWMASKTYP2")); + showmaskblMethodtyp->append(M("TP_LOCALLAB_SHOWMASKTYP3")); + showmaskblMethodtyp->set_active(0); + showmaskblMethodtypConn = showmaskblMethodtyp->signal_changed().connect(sigc::mem_fun(*this, &LocallabBlur::showmaskblMethodtypChanged)); + + enablMaskConn = enablMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabBlur::enablMaskChanged)); + + maskblCurveEditorG->setCurveListener(this); + + CCmaskblshape->setIdentityValue(0.); + CCmaskblshape->setResetCurve(FlatCurveType(defSpot.CCmaskblcurve.at(0)), defSpot.CCmaskblcurve); + CCmaskblshape->setBottomBarColorProvider(this, 1); + + LLmaskblshape->setIdentityValue(0.); + LLmaskblshape->setResetCurve(FlatCurveType(defSpot.LLmaskblcurve.at(0)), defSpot.LLmaskblcurve); + LLmaskblshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmaskblshape->setIdentityValue(0.); + HHmaskblshape->setResetCurve(FlatCurveType(defSpot.HHmaskblcurve.at(0)), defSpot.HHmaskblcurve); + HHmaskblshape->setCurveColorProvider(this, 2); + HHmaskblshape->setBottomBarColorProvider(this, 2); + + maskblCurveEditorG->curveListComplete(); + + strumaskbl->setAdjusterListener(this); + + toolblConn = toolbl->signal_toggled().connect(sigc::mem_fun(*this, &LocallabBlur::toolblChanged)); + + blendmaskbl->setAdjusterListener(this); + + radmaskbl->setAdjusterListener(this); + + lapmaskbl->setAdjusterListener(this); + + chromaskbl->setAdjusterListener(this); + + gammaskbl->setAdjusterListener(this); + + slomaskbl->setAdjusterListener(this); + + shadmaskbl->setAdjusterListener(this); + + shadmaskblsha->setAdjusterListener(this); + + mask2blCurveEditorG->setCurveListener(this); + + Lmaskblshape->setResetCurve(DiagonalCurveType(defSpot.Lmaskblcurve.at(0)), defSpot.Lmaskblcurve); + Lmaskblshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmaskblshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + mask2blCurveEditorG->curveListComplete(); + + mask2blCurveEditorGwav->setCurveListener(this); + + LLmaskblshapewav->setIdentityValue(0.); + LLmaskblshapewav->setResetCurve(FlatCurveType(defSpot.LLmaskblcurvewav.at(0)), defSpot.LLmaskblcurvewav); + LLmaskblshapewav->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2blCurveEditorGwav->curveListComplete(); + + csThresholdblur->setAdjusterListener(this); + + // Add Blur, Noise & Denoise specific widgets to GUI + ToolParamBlock* const blnoisebox = Gtk::manage(new ToolParamBlock()); + blnoisebox->pack_start(*blMethod); + blnoisebox->pack_start(*fftwbl, Gtk::PACK_SHRINK, 0); + blnoisebox->pack_start(*radius); + blnoisebox->pack_start(*strength); + ToolParamBlock* const grainBox = Gtk::manage(new ToolParamBlock()); + grainBox->pack_start(*isogr); + grainBox->pack_start(*strengr); + grainBox->pack_start(*scalegr); + grainFrame->add(*grainBox); + blnoisebox->pack_start(*grainFrame); + blnoisebox->pack_start(*medMethod); + blnoisebox->pack_start(*itera); + blnoisebox->pack_start(*guidbl); + blnoisebox->pack_start(*strbl); + blnoisebox->pack_start(*epsbl); + blnoisebox->pack_start(*sensibn); + blnoisebox->pack_start(*blurMethod); + blnoisebox->pack_start(*chroMethod); + // blnoisebox->pack_start(*activlum); + expblnoise->add(*blnoisebox, false); + pack_start(*expblnoise); + ToolParamBlock* const denoisebox = Gtk::manage(new ToolParamBlock()); + Gtk::Frame* const wavFrame = Gtk::manage(new Gtk::Frame()); + ToolParamBlock* const wavBox = Gtk::manage(new ToolParamBlock()); + wavBox->pack_start(*LocalcurveEditorwavden, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + // wavBox->pack_start(*noiselumf0); + // wavBox->pack_start(*noiselumf); + // wavBox->pack_start(*noiselumf2); + // wavBox->pack_start(*noiselumc); + wavBox->pack_start(*noiselumdetail); + wavBox->pack_start(*noiselequal); + wavBox->pack_start(*noisechrof); + wavBox->pack_start(*noisechroc); + wavBox->pack_start(*noisechrodetail); + wavBox->pack_start(*detailthr); + wavBox->pack_start(*adjblur); + wavFrame->add(*wavBox); + denoisebox->pack_start(*wavFrame); + denoisebox->pack_start(*bilateral); + denoisebox->pack_start(*sensiden); + expdenoise->add(*denoisebox, false); + pack_start(*expdenoise); + ToolParamBlock* const maskblBox = Gtk::manage(new ToolParamBlock()); + maskblBox->pack_start(*showmaskblMethod, Gtk::PACK_SHRINK, 4); + maskblBox->pack_start(*showmaskblMethodtyp, Gtk::PACK_SHRINK, 4); + maskblBox->pack_start(*enablMask, Gtk::PACK_SHRINK, 0); + maskblBox->pack_start(*maskblCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskblBox->pack_start(*strumaskbl, Gtk::PACK_SHRINK, 0); + maskblBox->pack_start(*toolbl, Gtk::PACK_SHRINK, 0); + Gtk::HSeparator* const separatorstrubl = Gtk::manage(new Gtk::HSeparator()); + maskblBox->pack_start(*separatorstrubl, Gtk::PACK_SHRINK, 2); + maskblBox->pack_start(*blendmaskbl, Gtk::PACK_SHRINK, 0); + Gtk::Frame* const toolblFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_TOOLMASK"))); + toolblFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const toolblBox = Gtk::manage(new ToolParamBlock()); + toolblBox->pack_start(*radmaskbl, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*lapmaskbl, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*chromaskbl, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*gammaskbl, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*slomaskbl, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*shadmaskblsha, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*shadmaskbl, Gtk::PACK_SHRINK, 0); + toolblBox->pack_start(*mask2blCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolblBox->pack_start(*mask2blCurveEditorGwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolblBox->pack_start(*csThresholdblur, Gtk::PACK_SHRINK, 0); + toolblFrame->add(*toolblBox); + maskblBox->pack_start(*toolblFrame); + expmaskbl->add(*maskblBox, false); + pack_start(*expmaskbl); +} + +LocallabBlur::~LocallabBlur() +{ + delete maskblCurveEditorG; + delete mask2blCurveEditorG; + delete mask2blCurveEditorGwav; + delete LocalcurveEditorwavden; +} + +bool LocallabBlur::isMaskViewActive() +{ + return (showmaskblMethod->get_active_row_number() != 0); +} + +void LocallabBlur::resetMaskView() +{ + showmaskblMethodConn.block(true); + showmaskblMethod->set_active(0); + showmaskblMethodConn.block(false); +} + +void LocallabBlur::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + blMask = showmaskblMethod->get_active_row_number(); +} + +void LocallabBlur::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + expblnoise->set_tooltip_markup(M("TP_LOCALLAB_BLUMETHOD_TOOLTIP")); + radius->set_tooltip_text(M("TP_LOCALLAB_RADIUS_TOOLTIP")); + sensibn->set_tooltip_text(M("TP_LOCALLAB_SENSIH_TOOLTIP")); + blurMethod->set_tooltip_markup(M("TP_LOCALLAB_BLMETHOD_TOOLTIP")); + expdenoise->set_tooltip_markup(M("TP_LOCALLAB_DENOI_TOOLTIP")); + wavshapeden->setTooltip(M("TP_LOCALLAB_WASDEN_TOOLTIP")); + noiselumc->set_tooltip_text(M("TP_LOCALLAB_NOISECHROC_TOOLTIP")); + expmaskbl->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmaskblshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskblshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskblshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + radmaskbl->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmaskbl->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + Lmaskblshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + LLmaskblshapewav->setTooltip(M("TP_LOCALLAB_LMASK_LEVEL_TOOLTIP")); + blendmaskbl->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + showmaskblMethodtyp->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKTYP_TOOLTIP")); + } else { + expblnoise->set_tooltip_text(""); + radius->set_tooltip_text(""); + sensibn->set_tooltip_text(""); + blurMethod->set_tooltip_text(""); + expdenoise->set_tooltip_text(""); + wavshapeden->setTooltip(""); + noiselumc->set_tooltip_text(""); + expmaskbl->set_tooltip_text(""); + CCmaskblshape->setTooltip(""); + LLmaskblshape->setTooltip(""); + HHmaskblshape->setTooltip(""); + radmaskbl->set_tooltip_text(""); + lapmaskbl->set_tooltip_text(""); + Lmaskblshape->setTooltip(""); + LLmaskblshapewav->setTooltip(""); + blendmaskbl->set_tooltip_text(M("")); + showmaskblMethodtyp->set_tooltip_markup(M("")); + } +} + +void LocallabBlur::setDefaultExpanderVisibility() +{ + expblnoise->set_expanded(false); + expdenoise->set_expanded(false); + expmaskbl->set_expanded(false); +} + +void LocallabBlur::disableListener() +{ + LocallabTool::disableListener(); + + blMethodConn.block(true); + fftwblConn.block(true); + medMethodConn.block(true); + blurMethodConn.block(true); + chroMethodConn.block(true); + activlumConn.block(true); + showmaskblMethodConn.block(true); + showmaskblMethodtypConn.block(true); + enablMaskConn.block(true); + toolblConn.block(true); +} + +void LocallabBlur::enableListener() +{ + LocallabTool::enableListener(); + + blMethodConn.block(false); + fftwblConn.block(false); + medMethodConn.block(false); + blurMethodConn.block(false); + chroMethodConn.block(false); + activlumConn.block(false); + showmaskblMethodConn.block(false); + showmaskblMethodtypConn.block(false); + enablMaskConn.block(false); + toolblConn.block(false); +} + +void LocallabBlur::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visiblur); + exp->setEnabled(spot.expblur); + complexity->set_active(spot.complexblur); + + if (spot.blMethod == "blur") { + blMethod->set_active(0); + } else if (spot.blMethod == "med") { + blMethod->set_active(1); + } else if (spot.blMethod == "guid") { + blMethod->set_active(2); + } + + fftwbl->set_active(spot.fftwbl); + radius->setValue(spot.radius); + strength->setValue(spot.strength); + isogr->setValue((double)spot.isogr); + strengr->setValue((double)spot.strengr); + scalegr->setValue((double)spot.scalegr); + + if (spot.medMethod == "none") { + medMethod->set_active(0); + } else if (spot.medMethod == "33") { + medMethod->set_active(1); + } else if (spot.medMethod == "55") { + medMethod->set_active(2); + } else if (spot.medMethod == "77") { + medMethod->set_active(3); + } else if (spot.medMethod == "99") { + medMethod->set_active(4); + } + + itera->setValue((double)spot.itera); + guidbl->setValue((double)spot.guidbl); + strbl->setValue((double)spot.strbl); + epsbl->setValue((double)spot.epsbl); + sensibn->setValue((double)spot.sensibn); + + if (spot.blurMethod == "norm") { + blurMethod->set_active(0); + } else if (spot.blurMethod == "inv") { + blurMethod->set_active(1); + } + + if (spot.chroMethod == "lum") { + chroMethod->set_active(0); + } else if (spot.chroMethod == "chr") { + chroMethod->set_active(1); + } else if (spot.chroMethod == "all") { + chroMethod->set_active(2); + } + + + if (spot.showmaskblMethodtyp == "blur") { + showmaskblMethodtyp ->set_active(0); + } else if (spot.showmaskblMethodtyp == "nois") { + showmaskblMethodtyp->set_active(1); + } else if (spot.showmaskblMethodtyp == "all") { + showmaskblMethodtyp->set_active(2); + } + + activlum->set_active(spot.activlum); + wavshapeden->setCurve(spot.locwavcurveden); + noiselumf0->setValue(spot.noiselumf0); + noiselumf->setValue(spot.noiselumf); + noiselumf2->setValue(spot.noiselumf2); + noiselumc->setValue(spot.noiselumc); + noiselumdetail->setValue(spot.noiselumdetail); + noiselequal->setValue((double)spot.noiselequal); + noisechrof->setValue(spot.noisechrof); + noisechroc->setValue(spot.noisechroc); + noisechrodetail->setValue(spot.noisechrodetail); + detailthr->setValue((double)spot.detailthr); + adjblur->setValue((double)spot.adjblur); + bilateral->setValue((double)spot.bilateral); + sensiden->setValue((double)spot.sensiden); + enablMask->set_active(spot.enablMask); + CCmaskblshape->setCurve(spot.CCmaskblcurve); + LLmaskblshape->setCurve(spot.LLmaskblcurve); + HHmaskblshape->setCurve(spot.HHmaskblcurve); + strumaskbl->setValue(spot.strumaskbl); + toolbl->set_active(spot.toolbl); + blendmaskbl->setValue((double)spot.blendmaskbl); + radmaskbl->setValue(spot.radmaskbl); + lapmaskbl->setValue(spot.lapmaskbl); + chromaskbl->setValue(spot.chromaskbl); + gammaskbl->setValue(spot.gammaskbl); + slomaskbl->setValue(spot.slomaskbl); + shadmaskbl->setValue((double)spot.shadmaskbl); + shadmaskblsha->setValue((double)spot.shadmaskblsha); + Lmaskblshape->setCurve(spot.Lmaskblcurve); + LLmaskblshapewav->setCurve(spot.LLmaskblcurvewav); + csThresholdblur->setValue(spot.csthresholdblur); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update Blur & Noise GUI according to blMethod combobox state + updateBlurGUI(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabBlur::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expblur = exp->getEnabled(); + spot.visiblur = exp->get_visible(); + spot.complexblur = complexity->get_active_row_number(); + + if (blMethod->get_active_row_number() == 0) { + spot.blMethod = "blur"; + } else if (blMethod->get_active_row_number() == 1) { + spot.blMethod = "med"; + } else if (blMethod->get_active_row_number() == 2) { + spot.blMethod = "guid"; + } + + spot.fftwbl = fftwbl->get_active(); + spot.radius = radius->getValue(); + spot.strength = strength->getIntValue(); + spot.isogr = isogr->getIntValue(); + spot.strengr = strengr->getIntValue(); + spot.scalegr = scalegr->getIntValue(); + + if (medMethod->get_active_row_number() == 0) { + spot.medMethod = "none"; + } else if (medMethod->get_active_row_number() == 1) { + spot.medMethod = "33"; + } else if (medMethod->get_active_row_number() == 2) { + spot.medMethod = "55"; + } else if (medMethod->get_active_row_number() == 3) { + spot.medMethod = "77"; + } else if (medMethod->get_active_row_number() == 4) { + spot.medMethod = "99"; + } + + spot.itera = itera->getIntValue(); + spot.guidbl = guidbl->getIntValue(); + spot.strbl = strbl->getIntValue(); + spot.epsbl = epsbl->getIntValue(); + spot.sensibn = sensibn->getIntValue(); + + if (blurMethod->get_active_row_number() == 0) { + spot.blurMethod = "norm"; + } else if (blurMethod->get_active_row_number() == 1) { + spot.blurMethod = "inv"; + } + + if (chroMethod->get_active_row_number() == 0) { + spot.chroMethod = "lum"; + } else if (chroMethod->get_active_row_number() == 1) { + spot.chroMethod = "chr"; + } else if (chroMethod->get_active_row_number() == 2) { + spot.chroMethod = "all"; + } + + + if (showmaskblMethodtyp->get_active_row_number() == 0) { + spot.showmaskblMethodtyp = "blur"; + } else if (showmaskblMethodtyp->get_active_row_number() == 1) { + spot.showmaskblMethodtyp = "nois"; + } else if (showmaskblMethodtyp->get_active_row_number() == 2) { + spot.showmaskblMethodtyp = "all"; + } + + spot.activlum = activlum->get_active(); + spot.locwavcurveden = wavshapeden->getCurve(); + spot.noiselumf0 = noiselumf0->getValue(); + spot.noiselumf = noiselumf->getValue(); + spot.noiselumf2 = noiselumf2->getValue(); + spot.noiselumc = noiselumc->getValue(); + spot.noiselumdetail = noiselumdetail->getValue(); + spot.noiselequal = noiselequal->getIntValue(); + spot.noisechrof = noisechrof->getValue(); + spot.noisechroc = noisechroc->getValue(); + spot.noisechrodetail = noisechrodetail->getValue(); + spot.detailthr = detailthr->getIntValue(); + spot.adjblur = adjblur->getIntValue(); + spot.bilateral = bilateral->getIntValue(); + spot.sensiden = sensiden->getIntValue(); + spot.enablMask = enablMask->get_active(); + spot.LLmaskblcurve = LLmaskblshape->getCurve(); + spot.CCmaskblcurve = CCmaskblshape->getCurve(); + spot.HHmaskblcurve = HHmaskblshape->getCurve(); + spot.strumaskbl = strumaskbl->getValue(); + spot.toolbl = toolbl->get_active(); + spot.blendmaskbl = blendmaskbl->getIntValue(); + spot.radmaskbl = radmaskbl->getValue(); + spot.lapmaskbl = lapmaskbl->getValue(); + spot.chromaskbl = chromaskbl->getValue(); + spot.gammaskbl = gammaskbl->getValue(); + spot.slomaskbl = slomaskbl->getValue(); + spot.shadmaskbl = shadmaskbl->getIntValue(); + spot.shadmaskblsha = shadmaskblsha->getIntValue(); + spot.Lmaskblcurve = Lmaskblshape->getCurve(); + spot.LLmaskblcurvewav = LLmaskblshapewav->getCurve(); + spot.csthresholdblur = csThresholdblur->getValue(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabBlur::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default value for adjuster and threshold adjuster widgets + radius->setDefault(defSpot.radius); + strength->setDefault((double)defSpot.strength); + isogr->setDefault((double)defSpot.isogr); + strengr->setDefault((double)defSpot.strengr); + scalegr->setDefault((double)defSpot.scalegr); + itera->setDefault((double)defSpot.itera); + guidbl->setDefault((double)defSpot.guidbl); + strbl->setDefault((double)defSpot.strbl); + epsbl->setDefault((double)defSpot.epsbl); + sensibn->setDefault((double)defSpot.sensibn); + noiselumf0->setDefault(defSpot.noiselumf0); + noiselumf->setDefault(defSpot.noiselumf); + noiselumf2->setDefault(defSpot.noiselumf2); + noiselumc->setDefault(defSpot.noiselumc); + noiselumdetail->setDefault(defSpot.noiselumdetail); + noiselequal->setDefault((double)defSpot.noiselequal); + noisechrof->setDefault(defSpot.noisechrof); + noisechroc->setDefault(defSpot.noisechroc); + noisechrodetail->setDefault(defSpot.noisechrodetail); + detailthr->setDefault((double)defSpot.detailthr); + adjblur->setDefault((double)defSpot.adjblur); + bilateral->setDefault((double)defSpot.bilateral); + sensiden->setDefault((double)defSpot.sensiden); + strumaskbl->setDefault(defSpot.strumaskbl); + blendmaskbl->setDefault((double)defSpot.blendmaskbl); + radmaskbl->setDefault(defSpot.radmaskbl); + lapmaskbl->setDefault(defSpot.lapmaskbl); + chromaskbl->setDefault(defSpot.chromaskbl); + gammaskbl->setDefault(defSpot.gammaskbl); + slomaskbl->setDefault(defSpot.slomaskbl); + shadmaskbl->setDefault(defSpot.shadmaskbl); + shadmaskblsha->setDefault(defSpot.shadmaskblsha); + csThresholdblur->setDefault(defSpot.csthresholdblur); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabBlur::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == radius) { + if (listener) { + listener->panelChanged(Evlocallabradius, + radius->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strength) { + if (listener) { + listener->panelChanged(Evlocallabstrength, + strength->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == isogr) { + if (listener) { + listener->panelChanged(Evlocallabisogr, + isogr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strengr) { + if (listener) { + listener->panelChanged(Evlocallabstrengr, + strengr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == scalegr) { + if (listener) { + listener->panelChanged(Evlocallabscalegr, + scalegr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == itera) { + if (listener) { + listener->panelChanged(Evlocallabitera, + itera->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == guidbl) { + if (listener) { + listener->panelChanged(Evlocallabguidbl, + guidbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strbl) { + if (listener) { + listener->panelChanged(Evlocallabstrbl, + strbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == epsbl) { + if (listener) { + listener->panelChanged(Evlocallabepsbl, + epsbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensibn) { + if (listener) { + listener->panelChanged(Evlocallabsensibn, + sensibn->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noiselumf0) { + if (listener) { + listener->panelChanged(Evlocallabnoiselumf0, + noiselumf0->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noiselumf) { + if (listener) { + listener->panelChanged(Evlocallabnoiselumf, + noiselumf->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noiselumf2) { + if (listener) { + listener->panelChanged(Evlocallabnoiselumf2, + noiselumf2->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noiselumc) { + if (listener) { + listener->panelChanged(Evlocallabnoiselumc, + noiselumc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noiselumdetail) { + if (listener) { + listener->panelChanged(Evlocallabnoiselumdetail, + noiselumdetail->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noiselequal) { + if (listener) { + listener->panelChanged(Evlocallabnoiselequal, + noiselequal->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noisechrof) { + if (listener) { + listener->panelChanged(Evlocallabnoisechrof, + noisechrof->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noisechroc) { + if (listener) { + listener->panelChanged(Evlocallabnoisechroc, + noisechroc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == noisechrodetail) { + if (listener) { + listener->panelChanged(Evlocallabnoisechrodetail, + noisechrodetail->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == detailthr) { + if (listener) { + listener->panelChanged(Evlocallabdetailthr, + detailthr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == adjblur) { + if (listener) { + listener->panelChanged(Evlocallabadjblur, + adjblur->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == bilateral) { + if (listener) { + listener->panelChanged(Evlocallabbilateral, + bilateral->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensiden) { + if (listener) { + listener->panelChanged(Evlocallabsensiden, + sensiden->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strumaskbl) { + if (listener) { + listener->panelChanged(Evlocallabstrumaskbl, + strumaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskbl) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskbl, + blendmaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskbl) { + if (listener) { + listener->panelChanged(Evlocallabradmaskbl, + radmaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskbl) { + if (listener) { + listener->panelChanged(Evlocallablapmaskbl, + lapmaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskbl) { + if (listener) { + listener->panelChanged(Evlocallabchromaskbl, + chromaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskbl) { + if (listener) { + listener->panelChanged(Evlocallabgammaskbl, + gammaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskbl) { + if (listener) { + listener->panelChanged(Evlocallabslomaskbl, + slomaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shadmaskbl) { + if (listener) { + listener->panelChanged(Evlocallabshadmaskbl, + shadmaskbl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shadmaskblsha) { + if (listener) { + listener->panelChanged(Evlocallabshadmaskblsha, + shadmaskblsha->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + } +} + +void LocallabBlur::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabcsThresholdblur, + csThresholdblur->getHistoryString() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabBlur::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == wavshapeden) { + if (listener) { + listener->panelChanged(EvlocallabwavCurveden, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == CCmaskblshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskblshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskblshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskblshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskblshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskblshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmaskblshape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskblshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskblshapewav) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskblshapewav, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabBlur::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenablur, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenablur, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabBlur::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + fftwbl->set_active(defSpot.fftwbl); + strumaskbl->setValue(defSpot.strumaskbl); + toolbl->set_active(defSpot.toolbl); + lapmaskbl->setValue(defSpot.lapmaskbl); +// gammaskbl->setValue(defSpot.gammaskbl); +// slomaskbl->setValue(defSpot.slomaskbl); + shadmaskbl->setValue((double)defSpot.shadmaskbl); + shadmaskblsha->setValue((double)defSpot.shadmaskblsha); + LLmaskblshapewav->setCurve(defSpot.LLmaskblcurvewav); + csThresholdblur->setValue(defSpot.csthresholdblur); + + // Enable all listeners + enableListener(); +} + +void LocallabBlur::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + fftwbl->hide(); + strumaskbl->hide(); + toolbl->hide(); + lapmaskbl->hide(); + gammaskbl->show(); + slomaskbl->show(); + shadmaskbl->hide(); + shadmaskblsha->hide(); + mask2blCurveEditorGwav->hide(); + csThresholdblur->hide(); + } else { + // Advanced widgets are shown in Expert mode + if (blMethod->get_active_row_number() == 0) { // Keep widget hidden when blMethod is > 0 + fftwbl->show(); + } + + strumaskbl->show(); + toolbl->show(); + lapmaskbl->show(); + gammaskbl->show(); + slomaskbl->show(); + shadmaskbl->show(); + shadmaskblsha->show(); + mask2blCurveEditorGwav->show(); + csThresholdblur->show(); + } +} + +void LocallabBlur::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskblshape->updateLocallabBackground(normChromar); + LLmaskblshape->updateLocallabBackground(normLumar); + HHmaskblshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabBlur::blMethodChanged() +{ + // Update Blur & Noise GUI according to blMethod combobox state + updateBlurGUI(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabblMethod, + blMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabBlur::fftwblChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fftwbl->get_active()) { + listener->panelChanged(Evlocallabfftwbl, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabfftwbl, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabBlur::medMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabmedMethod, + medMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabBlur::blurMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabblurMethod, + blurMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabBlur::chroMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabchroMethod, + chroMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabBlur::activlumChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (activlum->get_active()) { + listener->panelChanged(Evlocallabactivlum, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabactivlum, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabBlur::showmaskblMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabBlur::showmaskblMethodtypChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmasktypMethod, + showmaskblMethodtyp->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } +} + +void LocallabBlur::enablMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enablMask->get_active()) { + listener->panelChanged(EvLocallabEnablMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnablMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabBlur::toolblChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (toolbl->get_active()) { + listener->panelChanged(Evlocallabtoolbl, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabtoolbl, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabBlur::updateBlurGUI() +{ + if (blMethod->get_active_row_number() == 0) { + fftwbl->show(); + radius->show(); + strength->show(); + grainFrame->show(); + medMethod->hide(); + itera->hide(); + guidbl->hide(); + strbl->hide(); + epsbl->hide(); + activlum->show(); + } else if (blMethod->get_active_row_number() == 1) { + fftwbl->hide(); + radius->hide(); + strength->hide(); + grainFrame->hide(); + medMethod->show(); + itera->show(); + guidbl->hide(); + strbl->hide(); + epsbl->hide(); + activlum->show(); + } else if (blMethod->get_active_row_number() == 2) { + fftwbl->hide(); + radius->hide(); + strength->hide(); + grainFrame->hide(); + medMethod->hide(); + itera->hide(); + guidbl->show(); + strbl->show(); + epsbl->show(); + activlum->hide(); + } +} diff --git a/rtgui/locallabtools.h b/rtgui/locallabtools.h new file mode 100644 index 000000000..4e6b30f00 --- /dev/null +++ b/rtgui/locallabtools.h @@ -0,0 +1,1199 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath frame + * + * + * 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 . + * 2019 Pierre Cabrera + */ +#ifndef _LOCALLABTOOLS_H_ +#define _LOCALLABTOOLS_H_ + +#include "curveeditorgroup.h" +#include "curveeditor.h" +#include "labgrid.h" +#include "thresholdadjuster.h" +#include "toolpanel.h" +#include "adjuster.h" + +/* ==== LocallabToolListener ==== */ +class LocallabTool; +class LocallabToolListener +{ +public: + LocallabToolListener() {}; + virtual ~LocallabToolListener() {}; + + virtual void resetOtherMaskView(LocallabTool* current) = 0; + virtual void toolRemoved(LocallabTool* current) = 0; +}; + + +/* ==== LocallabTool ==== */ +class LocallabTool: + public ToolPanel, + public CurveListener, + public ColorProvider, + public AdjusterListener +{ +protected: + // LocallabTool mode enumeration + enum modeType { + Expert = 0, + Normal = 1 + }; + + // LocallabTool parameters + bool needMode; + bool isLocActivated; + Glib::ustring spotName; + LocallabToolListener* locToolListener; + + // LocallabTool generic widgets + MyExpander* exp; + MyComboBoxText* const complexity; + + sigc::connection enaExpConn, complexityConn; + + IdleRegister idle_register; + +public: + // Locallab tool constructor/destructor + LocallabTool(Gtk::Box* content, Glib::ustring toolName, Glib::ustring UILabel, bool need11 = false, bool needMode = true); + virtual ~LocallabTool(); + + // Getter for Locallab tool expander + MyExpander* getExpander() override + { + return exp; + } + + // Getter/setter for Locallab tool expanded status + void setExpanded(bool expanded) override + { + exp->set_expanded(expanded); + } + + bool getExpanded() override + { + return exp->get_expanded(); + } + + // Setter for Locallab activation indicator + void isLocallabActivated(bool cond) + { + isLocActivated = cond; + } + + // Setter for spot name + void setSpotName(const Glib::ustring &spotname) + { + spotName = spotname; + } + + // Setter for Locallab tool listener + void setLocallabToolListener(LocallabToolListener* ltl) + { + locToolListener = ltl; + } + + // Management functions to add/remove Locallab tool + void addLocallabTool(bool raiseEvent); + void removeLocallabTool(bool raiseEvent); + bool isLocallabToolAdded(); + + // Mask background management function + void refChanged(const double huer, const double lumar, const double chromar); + + // Mask preview functions + virtual bool isMaskViewActive() + { + return false; + }; + virtual void resetMaskView() {}; + virtual void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) {}; + + // Advice tooltips management function + virtual void updateAdviceTooltips(const bool showTooltips) {}; + + /* Notes: + - callerId #1: Mask CC shape (bottom bar) + Color CC/LC shape (left bar) + - callerId #2: Mask HH shape (main curve and bottom bar) + - callerId #3: Color LH/HH shape (main curve) + - callerId #4: Color CC/LC shape (bottom bar) + */ + void colorForValue(double valX, double valY, enum ColorCaller::ElemType elemType, int callerId, ColorCaller* caller) override; + + // To be implemented + virtual void setDefaultExpanderVisibility() {}; + virtual void disableListener(); + virtual void enableListener(); + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override {}; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override {}; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override {}; + void adjusterChanged(Adjuster* a, double newval) override {}; + void curveChanged(CurveEditor* ce) override {}; + +protected: + // To be implemented + virtual void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) {}; // Only necessary when using mask + +private: + // Remove button event function + bool on_remove_change(GdkEventButton* event); + + // Tool expander event function + void foldThemAll(GdkEventButton* event); + + // Complexity mode event function + void complexityModeChanged(); + + // To be implemented + virtual void enabledChanged() {}; + virtual void convertParamToNormal() {}; // Only necessary when using mode + virtual void updateGUIToMode(const modeType new_type) {}; // Only necessary when using mode +}; + +/* ==== LocallabColor ==== */ +class LocallabColor: + public Gtk::VBox, + public LocallabTool, + public ThresholdAdjusterListener +{ +private: + // Color & Light specific widgets + Gtk::CheckButton* const curvactiv; + Adjuster* const lightness; + Adjuster* const contrast; + Adjuster* const chroma; + Gtk::Frame* const gridFrame; + LabGrid* const labgrid; + MyComboBoxText* const gridMethod; + Adjuster* const strengthgrid; + Adjuster* const sensi; + Adjuster* const structcol; + Adjuster* const blurcolde; + Adjuster* const softradiuscol; + Gtk::CheckButton* const invers; + MyExpander* const expgradcol; + Adjuster* const strcol; + Adjuster* const strcolab; + Adjuster* const strcolh; + Adjuster* const angcol; + MyExpander* const expcurvcol; + Gtk::Label* const labqualcurv; + MyComboBoxText* const qualitycurveMethod; + CurveEditorGroup* const llCurveEditorG; + DiagonalCurveEditor* const llshape; + DiagonalCurveEditor* const ccshape; + CurveEditorGroup* const clCurveEditorG; + DiagonalCurveEditor* const clshape; + DiagonalCurveEditor* const lcshape; + CurveEditorGroup* const HCurveEditorG; + FlatCurveEditor* const LHshape; + CurveEditorGroup* const H2CurveEditorG; + FlatCurveEditor* const HHshape; + CurveEditorGroup* const rgbCurveEditorG; + MyComboBoxText* const toneMethod; + DiagonalCurveEditor* const rgbshape; + Gtk::CheckButton* const special; + MyExpander* const expmaskcol1; + MyComboBoxText* const merMethod; + ToolParamBlock* const mask7; + MyComboBoxText* const mergecolMethod; + Adjuster* const mercol; + Adjuster* const opacol; + Adjuster* const conthrcol; + Gtk::Frame* const gridmerFrame; + LabGrid* const labgridmerg; + Adjuster* const merlucol; + MyExpander* const expmaskcol; + MyComboBoxText* const showmaskcolMethod; + MyComboBoxText* const showmaskcolMethodinv; + Gtk::CheckButton* const enaColorMask; + CurveEditorGroup* const maskCurveEditorG; + FlatCurveEditor* const CCmaskshape; + FlatCurveEditor* const LLmaskshape; + FlatCurveEditor* const HHmaskshape; + Gtk::Frame* const struFrame; + Adjuster* const strumaskcol; + Gtk::CheckButton* const toolcol; + Gtk::Frame* const blurFrame; + Gtk::CheckButton* const fftColorMask; + Adjuster* const contcol; + Adjuster* const blurcol; + Adjuster* const blendmaskcol; + Adjuster* const radmaskcol; + Adjuster* const lapmaskcol; + Adjuster* const chromaskcol; + Adjuster* const gammaskcol; + Adjuster* const slomaskcol; + Adjuster* const shadmaskcol; + CurveEditorGroup* const maskHCurveEditorG; + FlatCurveEditor* const HHhmaskshape; + CurveEditorGroup* const mask2CurveEditorG; + DiagonalCurveEditor* const Lmaskshape; + CurveEditorGroup* const mask2CurveEditorGwav; + FlatCurveEditor* const LLmaskcolshapewav; + ThresholdAdjuster* const csThresholdcol; + + sigc::connection curvactivConn, gridMethodConn, inversConn, qualitycurveMethodConn, toneMethodConn, specialConn, merMethodConn, mergecolMethodConn, showmaskcolMethodConn, showmaskcolMethodConninv, enaColorMaskConn, toolcolConn, fftColorMaskConn; + +public: + LocallabColor(); + ~LocallabColor(); + + void setListener(ToolPanelListener* tpl) override; + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override {}; // Not used + void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void curvactivChanged(); + void gridMethodChanged(); + void inversChanged(); + void qualitycurveMethodChanged(); + void toneMethodChanged(); + void specialChanged(); + void merMethodChanged(); + void mergecolMethodChanged(); + void showmaskcolMethodChanged(); + void showmaskcolMethodChangedinv(); + void enaColorMaskChanged(); + void toolcolChanged(); + void fftColorMaskChanged(); + + void updateColorGUI1(); + void updateColorGUI2(); + void updateColorGUI3(); +}; + +/* ==== LocallabExposure ==== */ +class LocallabExposure: + public Gtk::VBox, + public LocallabTool +{ +private: + // Exposure specific widgets + MyComboBoxText* const expMethod; + Gtk::Frame* const pdeFrame; + Adjuster* const laplacexp; + Adjuster* const linear; + Adjuster* const balanexp; + Adjuster* const gamm; + MyComboBoxText* const exnoiseMethod; + Gtk::Frame* const fatFrame; + Adjuster* const fatamount; + Adjuster* const fatdetail; + Adjuster* const fatlevel; + Adjuster* const fatanchor; + Adjuster* const sensiex; + Adjuster* const structexp; + Adjuster* const blurexpde; + MyExpander* const exptoolexp; + Adjuster* const expcomp; + Adjuster* const black; + Adjuster* const hlcompr; + Adjuster* const hlcomprthresh; + Adjuster* const shadex; + Adjuster* const shcompr; + Adjuster* const expchroma; + CurveEditorGroup* const curveEditorG; + DiagonalCurveEditor* shapeexpos; + MyExpander* const expgradexp; + Adjuster* const strexp; + Adjuster* const angexp; + Adjuster* const softradiusexp; + Gtk::CheckButton* const inversex; + MyExpander* const expmaskexp; + MyComboBoxText* const showmaskexpMethod; + MyComboBoxText* const showmaskexpMethodinv; + Gtk::CheckButton* const enaExpMask; + Gtk::CheckButton* const enaExpMaskaft; + CurveEditorGroup* const maskexpCurveEditorG; + FlatCurveEditor* const CCmaskexpshape; + FlatCurveEditor* const LLmaskexpshape; + FlatCurveEditor* const HHmaskexpshape; + Adjuster* const blendmaskexp; + Adjuster* const radmaskexp; + Adjuster* const lapmaskexp; + Adjuster* const chromaskexp; + Adjuster* const gammaskexp; + Adjuster* const slomaskexp; + Gtk::Frame* const gradFramemask; + Adjuster* const strmaskexp; + Adjuster* const angmaskexp; + CurveEditorGroup* const mask2expCurveEditorG; + DiagonalCurveEditor* const Lmaskexpshape; + + sigc::connection expMethodConn, exnoiseMethodConn, inversexConn, showmaskexpMethodConn, showmaskexpMethodConninv, enaExpMaskConn, enaExpMaskaftConn; + +public: + LocallabExposure(); + ~LocallabExposure(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void expMethodChanged(); + void exnoiseMethodChanged(); + void inversexChanged(); + void showmaskexpMethodChanged(); + void showmaskexpMethodChangedinv(); + void enaExpMaskChanged(); + void enaExpMaskaftChanged(); + + void updateExposureGUI1(); + void updateExposureGUI2(); + void updateExposureGUI3(); +}; + +/* ==== LocallabShadow ==== */ +class LocallabShadow: + public Gtk::VBox, + public LocallabTool +{ +private: + // Shadow highlight specific widgets + MyComboBoxText* const shMethod; + const std::array multipliersh; + Adjuster* const detailSH; + Adjuster* const highlights; + Adjuster* const h_tonalwidth; + Adjuster* const shadows; + Adjuster* const s_tonalwidth; + Adjuster* const sh_radius; + Adjuster* const sensihs; + Adjuster* const blurSHde; + Gtk::Frame* const gamFrame; + Adjuster* const gamSH; + Adjuster* const sloSH; + MyExpander* const expgradsh; + Adjuster* const strSH; + Adjuster* const angSH; + Gtk::CheckButton* const inverssh; + MyExpander* const expmasksh; + MyComboBoxText* const showmaskSHMethod; + MyComboBoxText* const showmaskSHMethodinv; + Gtk::CheckButton* const enaSHMask; + CurveEditorGroup* const maskSHCurveEditorG; + FlatCurveEditor* const CCmaskSHshape; + FlatCurveEditor* const LLmaskSHshape; + FlatCurveEditor* const HHmaskSHshape; + Adjuster* const blendmaskSH; + Adjuster* const radmaskSH; + Adjuster* const lapmaskSH; + Adjuster* const chromaskSH; + Adjuster* const gammaskSH; + Adjuster* const slomaskSH; + CurveEditorGroup* const mask2SHCurveEditorG; + DiagonalCurveEditor* const LmaskSHshape; + Gtk::Frame* const fatSHFrame; + Adjuster* const fatamountSH; + Adjuster* const fatanchorSH; + + sigc::connection shMethodConn, inversshConn, showmaskSHMethodConn, showmaskSHMethodConninv, enaSHMaskConn; + +public: + LocallabShadow(); + ~LocallabShadow(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void shMethodChanged(); + void inversshChanged(); + void showmaskSHMethodChanged(); + void showmaskSHMethodChangedinv(); + void enaSHMaskChanged(); + + void updateShadowGUI1(); + void updateShadowGUI2(); +}; + +/* ==== LocallabVibrance ==== */ +class LocallabVibrance: + public Gtk::VBox, + public LocallabTool, + public ThresholdAdjusterListener, + public ThresholdCurveProvider +{ +private: + // Vibrance specific widgets + Adjuster* const saturated; + Adjuster* const pastels; + Adjuster* const warm; + ThresholdAdjuster* const psThreshold; + Gtk::CheckButton* const protectSkins; + Gtk::CheckButton* const avoidColorShift; + Gtk::CheckButton* const pastSatTog; + Adjuster* const sensiv; + CurveEditorGroup* const curveEditorGG; + DiagonalCurveEditor* const skinTonesCurve; + MyExpander* const expgradvib; + Adjuster* const strvib; + Adjuster* const strvibab; + Adjuster* const strvibh; + Adjuster* const angvib; + MyExpander* const expmaskvib; + MyComboBoxText* const showmaskvibMethod; + Gtk::CheckButton* const enavibMask; + CurveEditorGroup* const maskvibCurveEditorG; + FlatCurveEditor* const CCmaskvibshape; + FlatCurveEditor* const LLmaskvibshape; + FlatCurveEditor* const HHmaskvibshape; + Adjuster* const blendmaskvib; + Adjuster* const radmaskvib; + Adjuster* const lapmaskvib; + Adjuster* const chromaskvib; + Adjuster* const gammaskvib; + Adjuster* const slomaskvib; + CurveEditorGroup* const mask2vibCurveEditorG; + DiagonalCurveEditor* const Lmaskvibshape; + + sigc::connection pskinsConn, ashiftConn, pastsattogConn, showmaskvibMethodConn, enavibMaskConn; + +public: + LocallabVibrance(); + ~LocallabVibrance(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override; + void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override {}; // Not used + void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override {}; // Not used + std::vector getCurvePoints(ThresholdSelector* tAdjuster) const override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void protectskins_toggled(); + void avoidcolorshift_toggled(); + void pastsattog_toggled(); + void showmaskvibMethodChanged(); + void enavibMaskChanged(); + + void updateVibranceGUI(); +}; + +/* ==== LocallabSoft ==== */ +class LocallabSoft: + public Gtk::VBox, + public LocallabTool +{ +private: + // Soft light specific widgets + MyComboBoxText* const softMethod; + Gtk::HBox* const ctboxsoftmethod; + MyComboBoxText* const showmasksoftMethod; + Adjuster* const streng; + Adjuster* const laplace; + Adjuster* const sensisf; + + sigc::connection softMethodConn, showmasksoftMethodConn; + +public: + LocallabSoft(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void softMethodChanged(); + void showmasksoftMethodChanged(); + + void updateSoftGUI(); +}; + +/* ==== LocallabBlur ==== */ +class LocallabBlur: + public Gtk::VBox, + public LocallabTool, + public ThresholdAdjusterListener +{ +private: + // Blur & Noise specific widgets + MyExpander* const expblnoise; + MyComboBoxText* const blMethod; + Gtk::CheckButton* const fftwbl; + Adjuster* const radius; + Adjuster* const strength; + Gtk::Frame* const grainFrame; + Adjuster* const isogr; + Adjuster* const strengr; + Adjuster* const scalegr; + MyComboBoxText* const medMethod; + Adjuster* const itera; + Adjuster* const guidbl; + Adjuster* const strbl; + Adjuster* const epsbl; + Adjuster* const sensibn; + MyComboBoxText* const blurMethod; + MyComboBoxText* const chroMethod; + Gtk::CheckButton* const activlum; + MyExpander* const expdenoise; + CurveEditorGroup* const LocalcurveEditorwavden; + FlatCurveEditor* const wavshapeden; + Adjuster* const noiselumf0; + Adjuster* const noiselumf; + Adjuster* const noiselumf2; + Adjuster* const noiselumc; + Adjuster* const noiselumdetail; + Adjuster* const noiselequal; + Adjuster* const noisechrof; + Adjuster* const noisechroc; + Adjuster* const noisechrodetail; + Adjuster* const detailthr; + Adjuster* const adjblur; + Adjuster* const bilateral; + Adjuster* const sensiden; + MyExpander* const expmaskbl; + MyComboBoxText* const showmaskblMethod; + MyComboBoxText* const showmaskblMethodtyp; + Gtk::CheckButton* const enablMask; + CurveEditorGroup* const maskblCurveEditorG; + FlatCurveEditor* const CCmaskblshape; + FlatCurveEditor* const LLmaskblshape; + FlatCurveEditor* const HHmaskblshape; + Adjuster* const strumaskbl; + Gtk::CheckButton* const toolbl; + Adjuster* const blendmaskbl; + Adjuster* const radmaskbl; + Adjuster* const lapmaskbl; + Adjuster* const chromaskbl; + Adjuster* const gammaskbl; + Adjuster* const slomaskbl; + Adjuster* const shadmaskbl; + Adjuster* const shadmaskblsha; + CurveEditorGroup* const mask2blCurveEditorG; + DiagonalCurveEditor* const Lmaskblshape; + CurveEditorGroup* const mask2blCurveEditorGwav; + FlatCurveEditor* const LLmaskblshapewav; + ThresholdAdjuster* const csThresholdblur; + + sigc::connection blMethodConn, fftwblConn, medMethodConn, blurMethodConn, chroMethodConn, activlumConn, showmaskblMethodConn, showmaskblMethodtypConn, enablMaskConn, toolblConn; + +public: + LocallabBlur(); + ~LocallabBlur(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override {}; // Not used + void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void blMethodChanged(); + void fftwblChanged(); + void medMethodChanged(); + void blurMethodChanged(); + void chroMethodChanged(); + void activlumChanged(); + void showmaskblMethodChanged(); + void showmaskblMethodtypChanged(); + void enablMaskChanged(); + void toolblChanged(); + + void updateBlurGUI(); +}; + +/* ==== LocallabTone ==== */ +class LocallabTone: + public Gtk::VBox, + public LocallabTool +{ +private: + // Tone Mapping specific widgets + Adjuster* const amount; + Adjuster* const stren; + Gtk::CheckButton* const equiltm; + Adjuster* const gamma; + Adjuster* const satur; + Adjuster* const estop; + Adjuster* const scaltm; + Adjuster* const rewei; + Adjuster* const softradiustm; + Adjuster* const sensitm; + MyExpander* const expmasktm; + MyComboBoxText* const showmasktmMethod; + Gtk::CheckButton* const enatmMask; + Gtk::CheckButton* const enatmMaskaft; + CurveEditorGroup* const masktmCurveEditorG; + FlatCurveEditor* const CCmasktmshape; + FlatCurveEditor* const LLmasktmshape; + FlatCurveEditor* const HHmasktmshape; + Adjuster* const blendmasktm; + Adjuster* const lapmasktm; + Adjuster* const radmasktm; + Adjuster* const chromasktm; + Adjuster* const gammasktm; + Adjuster* const slomasktm; + CurveEditorGroup* const mask2tmCurveEditorG; + DiagonalCurveEditor* const Lmasktmshape; + + sigc::connection equiltmConn, showmasktmMethodConn, enatmMaskConn, enatmMaskaftConn; + +public: + LocallabTone(); + ~LocallabTone(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void equiltmChanged(); + void showmasktmMethodChanged(); + void enatmMaskChanged(); + void enatmMaskaftChanged(); +}; + +/* ==== LocallabRetinex ==== */ +class LocallabRetinex: + public Gtk::VBox, + public LocallabTool +{ +private: + // Retinex specific widgets + Adjuster* const dehaz; + Adjuster* const depth; + Gtk::CheckButton* const lumonly; + Gtk::Frame* const retiFrame; + Adjuster* const str; + Gtk::CheckButton* const loglin; + Adjuster* const sensih; + Gtk::Frame* const retitoolFrame; + MyComboBoxText* const retinexMethod; + Gtk::CheckButton* const fftwreti; + Gtk::CheckButton* const equilret; + Adjuster* const neigh; + Adjuster* const vart; + Adjuster* const scalereti; + Adjuster* const limd; + Adjuster* const offs; + MyExpander* const expretitools; + Adjuster* const chrrt; + Adjuster* const darkness; + Adjuster* const lightnessreti; + Adjuster* const cliptm; + Adjuster* const softradiusret; + CurveEditorGroup* const LocalcurveEditortransT; + FlatCurveEditor* const cTtransshape; + Gtk::Label* const mMLabels; + Gtk::Label* const transLabels; + Gtk::Label* const transLabels2; + CurveEditorGroup* const LocalcurveEditorgainT; + FlatCurveEditor* const cTgainshape; + MyExpander* const expmaskreti; + MyComboBoxText* const showmaskretiMethod; + Gtk::CheckButton* const enaretiMask; + Gtk::CheckButton* const enaretiMasktmap; + CurveEditorGroup* const maskretiCurveEditorG; + FlatCurveEditor* const CCmaskretishape; + FlatCurveEditor* const LLmaskretishape; + FlatCurveEditor* const HHmaskretishape; + Adjuster* const blendmaskreti; + Adjuster* const radmaskreti; + Adjuster* const lapmaskreti; + Adjuster* const chromaskreti; + Adjuster* const gammaskreti; + Adjuster* const slomaskreti; + CurveEditorGroup* const mask2retiCurveEditorG; + DiagonalCurveEditor* const Lmaskretishape; + Gtk::CheckButton* const inversret; + + sigc::connection lumonlyConn, loglinConn, retinexMethodConn, fftwretiConn, equilretConn, showmaskretiMethodConn, enaretiMaskConn, enaretiMasktmapConn, inversretConn; + +public: + LocallabRetinex(); + ~LocallabRetinex(); + + void updateMinMax(const double cdma, const double cdmin, const double mini, const double maxi, const double Tmean, const double Tsigma, const double Tmin, const double Tmax); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void lumonlyChanged(); + void loglinChanged(); + void retinexMethodChanged(); + void fftwretiChanged(); + void equilretChanged(); + void showmaskretiMethodChanged(); + void enaretiMaskChanged(); + void enaretiMasktmapChanged(); + void inversretChanged(); + + void updateRetinexGUI1(); + void updateRetinexGUI2(); + void updateRetinexGUI3(); +}; + +/* ==== LocallabSharp ==== */ +class LocallabSharp: + public Gtk::VBox, + public LocallabTool +{ +private: + Adjuster* const sharcontrast; + Adjuster* const sharblur; + Adjuster* const sharamount; + Adjuster* const shardamping; + Adjuster* const shariter; + Adjuster* const sharradius; + Adjuster* const sensisha; + Gtk::CheckButton* const inverssha; + MyComboBoxText* const showmasksharMethod; + + sigc::connection inversshaConn, showmasksharMethodConn; + +public: + LocallabSharp(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void inversshaChanged(); + void showmasksharMethodChanged(); +}; + +/* ==== LocallabContrast ==== */ +class LocallabContrast: + public Gtk::VBox, + public LocallabTool, + public ThresholdAdjusterListener +{ +private: + MyComboBoxText* const localcontMethod; + Adjuster* const lcradius; + Adjuster* const lcamount; + Adjuster* const lcdarkness; + Adjuster* const lclightness; + Adjuster* const sigmalc; + CurveEditorGroup* const LocalcurveEditorwav; + FlatCurveEditor* const wavshape; + Adjuster* const levelwav; + ThresholdAdjuster* const csThreshold; + MyExpander* const expresidpyr; + Adjuster* const residcont; + Adjuster* const residchro; + Adjuster* const residsha; + Adjuster* const residshathr; + Adjuster* const residhi; + Adjuster* const residhithr; + Adjuster* const sensilc; + Gtk::Frame* const clariFrame; + Adjuster* const clarilres; + Adjuster* const claricres; + Adjuster* const clarisoft; + Gtk::CheckButton* const origlc; + MyExpander* const expcontrastpyr; + Gtk::CheckButton* const wavgradl; + Adjuster* const sigmalc2; + Adjuster* const strwav; + Adjuster* const angwav; + Gtk::CheckButton* const wavedg; + Adjuster* const strengthw; + Adjuster* const sigmaed; + CurveEditorGroup* const LocalcurveEditorwavedg; + FlatCurveEditor* const wavshapeedg; + Adjuster* const gradw; + Gtk::CheckButton* const waveshow; + ToolParamBlock* const edgsBoxshow; + Adjuster* const radiusw; + Adjuster* const detailw; + MyComboBoxText* const localedgMethod; + Adjuster* const tloww; + Adjuster* const thigw; + Adjuster* const edgw; + Adjuster* const basew; + MyComboBoxText* const localneiMethod; + Gtk::CheckButton* const wavblur; + Adjuster* const levelblur; + Adjuster* const sigmabl; + Adjuster* const chromablu; + CurveEditorGroup* const LocalcurveEditorwavlev; + FlatCurveEditor* const wavshapelev; + Adjuster* const residblur; + Gtk::CheckButton* const blurlc; + MyExpander* const expcontrastpyr2; + Gtk::CheckButton* const wavcont; + Adjuster* const sigma; + Adjuster* const offset; + Adjuster* const chromalev; + CurveEditorGroup* const LocalcurveEditorwavcon; + FlatCurveEditor* const wavshapecon; + Gtk::CheckButton* const wavcompre; + CurveEditorGroup* const LocalcurveEditorwavcompre; + FlatCurveEditor* const wavshapecompre; + Adjuster* const sigmadr; + Adjuster* const threswav; + Adjuster* const residcomp; + Gtk::CheckButton* const wavcomp; + Adjuster* const sigmadc; + Adjuster* const deltad; + CurveEditorGroup* const LocalcurveEditorwavcomp; + FlatCurveEditor* const wavshapecomp; + Adjuster* const fatres; + Gtk::CheckButton* const fftwlc; + MyExpander* const expmasklc; + MyComboBoxText* const showmasklcMethod; + Gtk::CheckButton* const enalcMask; + CurveEditorGroup* const masklcCurveEditorG; + FlatCurveEditor* const CCmasklcshape; + FlatCurveEditor* const LLmasklcshape; + FlatCurveEditor* const HHmasklcshape; + Adjuster* const blendmasklc; + Adjuster* const radmasklc; + Adjuster* const chromasklc; + CurveEditorGroup* const mask2lcCurveEditorG; + DiagonalCurveEditor* const Lmasklcshape; + + sigc::connection localcontMethodConn, origlcConn, wavgradlConn, wavedgConn, localedgMethodConn, waveshowConn, localneiMethodConn, wavblurConn, blurlcConn, wavcontConn, wavcompreConn, wavcompConn, fftwlcConn, showmasklcMethodConn, enalcMaskConn; + +public: + LocallabContrast(); + ~LocallabContrast(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void adjusterChanged(ThresholdAdjuster* a, double newBottom, double newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) override {}; // Not used + void adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) override {}; // Not used + void adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void localcontMethodChanged(); + void origlcChanged(); + void wavgradlChanged(); + void wavedgChanged(); + void localedgMethodChanged(); + void waveshowChanged(); + void localneiMethodChanged(); + void wavblurChanged(); + void blurlcChanged(); + void wavcontChanged(); + void wavcompreChanged(); + void wavcompChanged(); + void fftwlcChanged(); + void showmasklcMethodChanged(); + void enalcMaskChanged(); + + void updateContrastGUI1(); + void updateContrastGUI2(); + void updateContrastGUI3(); +}; + +/* ==== LocallabCBDL ==== */ +class LocallabCBDL: + public Gtk::VBox, + public LocallabTool +{ +private: + const std::array multiplier; + Adjuster* const chromacbdl; + Adjuster* const threshold; + Adjuster* const blurcbdl; + Adjuster* const clarityml; + Adjuster* const contresid; + Adjuster* const softradiuscb; + Adjuster* const sensicb; + MyExpander* const expmaskcb; + MyComboBoxText* const showmaskcbMethod; + Gtk::CheckButton* const enacbMask; + CurveEditorGroup* const maskcbCurveEditorG; + FlatCurveEditor* const CCmaskcbshape; + FlatCurveEditor* const LLmaskcbshape; + FlatCurveEditor* const HHmaskcbshape; + Adjuster* const blendmaskcb; + Adjuster* const radmaskcb; + Adjuster* const lapmaskcb; + Adjuster* const chromaskcb; + Adjuster* const gammaskcb; + Adjuster* const slomaskcb; + CurveEditorGroup* const mask2cbCurveEditorG; + DiagonalCurveEditor* const Lmaskcbshape; + + sigc::connection showmaskcbMethodConn, enacbMaskConn; + + Gtk::Button* const lumacontrastMinusButton; + Gtk::Button* const lumaneutralButton; + Gtk::Button* const lumacontrastPlusButton; + + sigc::connection lumacontrastMinusPressedConn, lumaneutralPressedConn, lumacontrastPlusPressedConn; + +public: + LocallabCBDL(); + ~LocallabCBDL(); + + bool isMaskViewActive() override; + void resetMaskView() override; + void getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) override; + + void updateAdviceTooltips(const bool showTooltips) override; + + void setDefaultExpanderVisibility() override; + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + void curveChanged(CurveEditor* ce) override; + +private: + void enabledChanged() override; + void convertParamToNormal() override; + void updateGUIToMode(const modeType new_type) override; + + void updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) override; + + void showmaskcbMethodChanged(); + void enacbMaskChanged(); + + void lumacontrastMinusPressed(); + void lumaneutralPressed(); + void lumacontrastPlusPressed(); +}; + +/* ==== LocallabLog ==== */ +class LocallabLog: + public Gtk::VBox, + public LocallabTool +{ +private: + Gtk::ToggleButton* const autocompute; + Gtk::Frame* const logPFrame; + Adjuster* const blackEv; + Adjuster* const whiteEv; + Gtk::CheckButton* const fullimage; + Gtk::CheckButton* const Autogray; + Adjuster* const sourceGray; + Adjuster* const targetGray; + Adjuster* const detail; + Adjuster* const baselog; + Adjuster* const sensilog; + Adjuster* const strlog; + Adjuster* const anglog; + + sigc::connection autoconn, fullimageConn, AutograyConn; + +public: + LocallabLog(); + void updateAdviceTooltips(const bool showTooltips) override; + + void disableListener() override; + void enableListener() override; + void read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited = nullptr) override; + void adjusterChanged(Adjuster* a, double newval) override; + + void updateAutocompute(const float blackev, const float whiteev, const float sourceg, const float targetg); + +private: + void enabledChanged() override; + + void autocomputeToggled(); + void fullimageChanged(); + void AutograyChanged(); + + void updateLogGUI(); +}; + +#endif diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc new file mode 100644 index 000000000..816bfcd30 --- /dev/null +++ b/rtgui/locallabtools2.cc @@ -0,0 +1,4725 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath frame + * + * + * 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 . + * 2019 Pierre Cabrera + */ +#include "locallabtools.h" + +#include "options.h" +#include "../rtengine/procparams.h" +#include "locallab.h" +#include "rtimage.h" + +#define MINNEIGH 0.1 +#define MAXNEIGH 1500 +#define CENTERNEIGH 200 + +using namespace rtengine; +using namespace procparams; + +extern Options options; + +static double retiSlider2neigh(double sval) +{ + // Slider range: 0 - 5000 + double neigh; + + if (sval <= 200) { + // Linear below center-temp + neigh = MINNEIGH + (sval / 200.0) * (CENTERNEIGH - MINNEIGH); + } else { + const double slope = (double)(CENTERNEIGH - MINNEIGH) / (MAXNEIGH - CENTERNEIGH); + const double x = (sval - 200) / 200; // x range: 0 - 1 + const double y = x * slope + (1.0 - slope) * pow(x, 4.0); + neigh = CENTERNEIGH + y * (MAXNEIGH - CENTERNEIGH); + } + + if (neigh < MINNEIGH) { + neigh = MINNEIGH; + } + + if (neigh > MAXNEIGH) { + neigh = MAXNEIGH; + } + + return neigh; +} + +static double retiNeigh2Slider(double neigh) +{ + double sval; + + if (neigh <= CENTERNEIGH) { + sval = ((neigh - MINNEIGH) / (CENTERNEIGH - MINNEIGH)) * 200.0; + } else { + const double slope = (double)(CENTERNEIGH - MINNEIGH) / (MAXNEIGH - CENTERNEIGH); + const double y = (neigh - CENTERNEIGH) / (MAXNEIGH - CENTERNEIGH); + double x = pow(y, 0.25); // Rough guess of x, will be a little lower + double k = 0.1; + bool add = true; + + // The y=f(x) function is a mess to invert, therefore we have this trial-refinement loop instead. + // From tests, worst case is about 20 iterations, i.e. no problem + for (;;) { + double y1 = x * slope + (1.0 - slope) * pow(x, 4.0); + + if (200 * fabs(y1 - y) < 0.1) { + break; + } + + if (y1 < y) { + if (!add) { + k /= 2; + } + + x += k; + add = true; + } else { + if (add) { + k /= 2; + } + + x -= k; + add = false; + } + } + + sval = 200.0 + x * 200.0; + } + + if (sval < 0.) { + sval = 0.; + } + + if (sval > 1500.) { + sval = 1500.; + } + + return sval; +} + +/* ==== LocallabTone ==== */ +LocallabTone::LocallabTone(): + LocallabTool(this, M("TP_LOCALLAB_TONE_TOOLNAME"), M("TP_LOCALLAB_TM"), true), + + // Tone mapping specific widgets + amount(Gtk::manage(new Adjuster(M("TP_LOCALLAB_AMOUNT"), 50., 100.0, 0.5, 95.))), + stren(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STREN"), -0.5, 2.0, 0.01, 0.5))), + equiltm(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EQUIL")))), + gamma(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAM"), 0.4, 4.0, 0.11, 1.0))), + satur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SATUR"), -100., 100., 0.1, 0.))), // By default satur = 0 ==> use Mantiuk value + estop(Gtk::manage(new Adjuster(M("TP_LOCALLAB_ESTOP"), 0.1, 4., 0.01, 1.4))), + scaltm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SCALTM"), 0.1, 10.0, 0.01, 1.0))), + rewei(Gtk::manage(new Adjuster(M("TP_LOCALLAB_REWEI"), 0, 3, 1, 0))), + softradiustm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.1, 0.))), + sensitm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 30))), + expmasktm(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWT")))), + showmasktmMethod(Gtk::manage(new MyComboBoxText())), + enatmMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enatmMaskaft(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_AFTER_MASK")))), + masktmCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + lapmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + radmasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + chromasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), + slomasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + mask2tmCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmasktmshape(static_cast(mask2tmCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter Tone Mapping specific widgets + amount->setAdjusterListener(this); + + stren->setAdjusterListener(this); + + equiltmConn = equiltm->signal_toggled().connect(sigc::mem_fun(*this, &LocallabTone::equiltmChanged)); + + gamma->setAdjusterListener(this); + + satur->setAdjusterListener(this); + + estop->setAdjusterListener(this); + + scaltm->setAdjusterListener(this); + + rewei->setAdjusterListener(this); + + softradiustm->setLogScale(10, 0); + softradiustm->setAdjusterListener(this); + + sensitm->setAdjusterListener(this); + + setExpandAlignProperties(expmasktm, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmasktmMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmasktmMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmasktmMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmasktmMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmasktmMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmasktmMethod->set_active(0); + showmasktmMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmasktmMethodConn = showmasktmMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabTone::showmasktmMethodChanged)); + + enatmMaskConn = enatmMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabTone::enatmMaskChanged)); + + enatmMaskaftConn = enatmMaskaft->signal_toggled().connect(sigc::mem_fun(*this, &LocallabTone::enatmMaskaftChanged)); + + masktmCurveEditorG->setCurveListener(this); + + CCmasktmshape->setIdentityValue(0.); + CCmasktmshape->setResetCurve(FlatCurveType(defSpot.CCmasktmcurve.at(0)), defSpot.CCmasktmcurve); + CCmasktmshape->setBottomBarColorProvider(this, 1); + + LLmasktmshape->setIdentityValue(0.); + LLmasktmshape->setResetCurve(FlatCurveType(defSpot.LLmasktmcurve.at(0)), defSpot.LLmasktmcurve); + LLmasktmshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmasktmshape->setIdentityValue(0.); + HHmasktmshape->setResetCurve(FlatCurveType(defSpot.HHmasktmcurve.at(0)), defSpot.HHmasktmcurve); + HHmasktmshape->setCurveColorProvider(this, 2); + HHmasktmshape->setBottomBarColorProvider(this, 2); + + masktmCurveEditorG->curveListComplete(); + + blendmasktm->setAdjusterListener(this); + + lapmasktm->setAdjusterListener(this); + + radmasktm->setLogScale(10, -10); + radmasktm->setAdjusterListener(this); + + chromasktm->setAdjusterListener(this); + + gammasktm->setAdjusterListener(this); + + slomasktm->setAdjusterListener(this); + + mask2tmCurveEditorG->setCurveListener(this); + Lmasktmshape->setResetCurve(DiagonalCurveType(defSpot.Lmasktmcurve.at(0)), defSpot.Lmasktmcurve); + Lmasktmshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmasktmshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2tmCurveEditorG->curveListComplete(); + + // Add Tone Mapping specific widgets to GUI + // pack_start(*amount); // To use if we change transit_shapedetect parameters + pack_start(*stren); + pack_start(*equiltm); + pack_start(*gamma); + pack_start(*satur); + pack_start(*estop); + pack_start(*scaltm); + pack_start(*rewei); + // pack_start(*softradiustm); // Always bad with TM ?? + pack_start(*sensitm); + ToolParamBlock* const masktmBox = Gtk::manage(new ToolParamBlock()); + masktmBox->pack_start(*showmasktmMethod, Gtk::PACK_SHRINK, 4); + masktmBox->pack_start(*enatmMask, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*enatmMaskaft, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*masktmCurveEditorG, Gtk::PACK_SHRINK, 4); + masktmBox->pack_start(*blendmasktm, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*lapmasktm, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*radmasktm, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*chromasktm, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*gammasktm, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*slomasktm, Gtk::PACK_SHRINK, 0); + masktmBox->pack_start(*mask2tmCurveEditorG, Gtk::PACK_SHRINK, 4); + expmasktm->add(*masktmBox, false); + pack_start(*expmasktm, false, false); +} + +LocallabTone::~LocallabTone() +{ + delete masktmCurveEditorG; + delete mask2tmCurveEditorG; +} + +bool LocallabTone::isMaskViewActive() +{ + return (showmasktmMethod->get_active_row_number() != 0); +} + +void LocallabTone::resetMaskView() +{ + showmasktmMethodConn.block(true); + showmasktmMethod->set_active(0); + showmasktmMethodConn.block(false); +} + +void LocallabTone::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + tmMask = showmasktmMethod->get_active_row_number(); +} + +void LocallabTone::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_TONEMAP_TOOLTIP")); + estop->set_tooltip_text(M("TP_LOCALLAB_TONEMAPESTOP_TOOLTIP")); + rewei->set_tooltip_text(M("TP_LOCALLAB_TONEMAPREWEI_TOOLTIP")); + scaltm->set_tooltip_text(M("TP_LOCALLAB_TONEMASCALE_TOOLTIP")); + gamma->set_tooltip_text(M("TP_LOCALLAB_TONEMAPGAM_TOOLTIP")); + equiltm->set_tooltip_text(M("TP_LOCALLAB_EQUILTM_TOOLTIP")); + sensitm->set_tooltip_text(M("TP_LOCALLAB_SENSI_TOOLTIP")); + expmasktm->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmasktmshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmasktmshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmasktmshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + lapmasktm->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + radmasktm->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + Lmasktmshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + blendmasktm->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2tmCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + estop->set_tooltip_text(""); + rewei->set_tooltip_text(""); + scaltm->set_tooltip_text(M("")); + gamma->set_tooltip_text(M("")); + equiltm->set_tooltip_text(M("")); + sensitm->set_tooltip_text(""); + expmasktm->set_tooltip_text(""); + CCmasktmshape->setTooltip(""); + LLmasktmshape->setTooltip(""); + HHmasktmshape->setTooltip(""); + lapmasktm->set_tooltip_text(""); + radmasktm->set_tooltip_text(""); + Lmasktmshape->setTooltip(""); + blendmasktm->set_tooltip_text(M("")); + mask2tmCurveEditorG->set_tooltip_text(M("")); + } +} + +void LocallabTone::setDefaultExpanderVisibility() +{ + expmasktm->set_expanded(false); +} + +void LocallabTone::disableListener() +{ + LocallabTool::disableListener(); + + equiltmConn.block(true); + showmasktmMethodConn.block(true); + enatmMaskConn.block(true); + enatmMaskaftConn.block(true); +} + +void LocallabTone::enableListener() +{ + LocallabTool::enableListener(); + + equiltmConn.block(false); + showmasktmMethodConn.block(false); + enatmMaskConn.block(false); + enatmMaskaftConn.block(false); +} + +void LocallabTone::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visitonemap); + exp->setEnabled(spot.exptonemap); + complexity->set_active(spot.complextonemap); + + amount->setValue(spot.amount); + stren->setValue(spot.stren); + equiltm->set_active(spot.equiltm); + gamma->setValue(spot.gamma); + satur->setValue(spot.satur); + estop->setValue(spot.estop); + scaltm->setValue(spot.scaltm); + rewei->setValue((double)spot.rewei); + softradiustm->setValue(spot.softradiustm); + sensitm->setValue((double)spot.sensitm); + enatmMask->set_active(spot.enatmMask); + enatmMaskaft->set_active(spot.enatmMaskaft); + CCmasktmshape->setCurve(spot.CCmasktmcurve); + LLmasktmshape->setCurve(spot.LLmasktmcurve); + HHmasktmshape->setCurve(spot.HHmasktmcurve); + blendmasktm->setValue((double)spot.blendmasktm); + lapmasktm->setValue(spot.lapmasktm); + radmasktm->setValue(spot.radmasktm); + chromasktm->setValue(spot.chromasktm); + gammasktm->setValue(spot.gammasktm); + slomasktm->setValue(spot.slomasktm); + Lmasktmshape->setCurve(spot.Lmasktmcurve); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabTone::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.exptonemap = exp->getEnabled(); + spot.visitonemap = exp->get_visible(); + spot.complextonemap = complexity->get_active_row_number(); + + spot.amount = amount->getValue(); + spot.stren = stren->getValue(); + spot.equiltm = equiltm->get_active(); + spot.gamma = gamma->getValue(); + spot.satur = satur->getValue(); + spot.estop = estop->getValue(); + spot.scaltm = scaltm->getValue(); + spot.rewei = rewei->getIntValue(); + spot.softradiustm = softradiustm->getValue(); + spot.sensitm = sensitm->getIntValue(); + spot.enatmMask = enatmMask->get_active(); + spot.enatmMaskaft = enatmMaskaft->get_active(); + spot.LLmasktmcurve = LLmasktmshape->getCurve(); + spot.CCmasktmcurve = CCmasktmshape->getCurve(); + spot.HHmasktmcurve = HHmasktmshape->getCurve(); + spot.blendmasktm = blendmasktm->getIntValue(); + spot.lapmasktm = lapmasktm->getValue(); + spot.radmasktm = radmasktm->getValue(); + spot.chromasktm = chromasktm->getValue(); + spot.gammasktm = gammasktm->getValue(); + spot.slomasktm = slomasktm->getValue(); + spot.Lmasktmcurve = Lmasktmshape->getCurve(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabTone::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + amount->setDefault(defSpot.amount); + stren->setDefault(defSpot.stren); + gamma->setDefault(defSpot.gamma); + satur->setDefault(defSpot.satur); + estop->setDefault(defSpot.estop); + scaltm->setDefault(defSpot.scaltm); + rewei->setDefault((double)defSpot.rewei); + softradiustm->setDefault(defSpot.softradiustm); + sensitm->setDefault((double)defSpot.sensitm); + blendmasktm->setDefault((double)defSpot.blendmasktm); + lapmasktm->setDefault(defSpot.lapmasktm); + radmasktm->setDefault(defSpot.radmasktm); + chromasktm->setDefault(defSpot.chromasktm); + gammasktm->setDefault(defSpot.gammasktm); + slomasktm->setDefault(defSpot.slomasktm); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabTone::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == amount) { + if (listener) { + listener->panelChanged(Evlocallabamount, + amount->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == stren) { + if (listener) { + listener->panelChanged(Evlocallabstren, + stren->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gamma) { + if (listener) { + listener->panelChanged(Evlocallabgamma, + gamma->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == satur) { + if (listener) { + listener->panelChanged(Evlocallabsatur, + satur->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == estop) { + if (listener) { + listener->panelChanged(Evlocallabestop, + estop->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == scaltm) { + if (listener) { + listener->panelChanged(Evlocallabscaltm, + scaltm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == rewei) { + if (listener) { + listener->panelChanged(Evlocallabrewei, + rewei->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == softradiustm) { + if (listener) { + listener->panelChanged(Evlocallabsoftradiustm, + softradiustm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensitm) { + if (listener) { + listener->panelChanged(Evlocallabsensitm, + sensitm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmasktm) { + if (listener) { + listener->panelChanged(Evlocallabblendmasktm, + blendmasktm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmasktm) { + if (listener) { + listener->panelChanged(Evlocallablapmasktm, + lapmasktm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmasktm) { + if (listener) { + listener->panelChanged(Evlocallabradmasktm, + radmasktm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromasktm) { + if (listener) { + listener->panelChanged(Evlocallabchromasktm, + chromasktm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammasktm) { + if (listener) { + listener->panelChanged(Evlocallabgammasktm, + gammasktm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomasktm) { + if (listener) { + listener->panelChanged(Evlocallabslomasktm, + slomasktm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabTone::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == CCmasktmshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmasktmshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmasktmshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmasktmshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmasktmshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmasktmshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmasktmshape) { + if (listener) { + listener->panelChanged(EvlocallabLmasktmshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabTone::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenatonemap, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenatonemap, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabTone::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + gamma->setValue(defSpot.gamma); + satur->setValue(defSpot.satur); + rewei->setValue((double)defSpot.rewei); + lapmasktm->setValue(defSpot.lapmasktm); + gammasktm->setValue(defSpot.gammasktm); + slomasktm->setValue(defSpot.slomasktm); + + // Enable all listeners + enableListener(); +} + +void LocallabTone::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + gamma->hide(); + satur->hide(); + rewei->hide(); + lapmasktm->hide(); + gammasktm->hide(); + slomasktm->hide(); + } else { + // Advanced widgets are shown in Expert mode + gamma->show(); + satur->show(); + rewei->show(); + lapmasktm->show(); + gammasktm->show(); + slomasktm->show(); + } +} + +void LocallabTone::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmasktmshape->updateLocallabBackground(normChromar); + LLmasktmshape->updateLocallabBackground(normLumar); + HHmasktmshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabTone::equiltmChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (equiltm->get_active()) { + listener->panelChanged(Evlocallabequiltm, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabequiltm, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabTone::showmasktmMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabTone::enatmMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enatmMask->get_active()) { + listener->panelChanged(EvLocallabEnatmMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnatmMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabTone::enatmMaskaftChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enatmMaskaft->get_active()) { + listener->panelChanged(EvLocallabEnatmMaskaft, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnatmMaskaft, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +/* ==== LocallabRetinex ==== */ +LocallabRetinex::LocallabRetinex(): + LocallabTool(this, M("TP_LOCALLAB_RET_TOOLNAME"), M("TP_LOCALLAB_RETI"), true), + + // Retinex specific widgets + dehaz(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DEHAZ"), -100, 100, 1, 0))), + depth(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DEPTH"), 0, 100, 1, 25))), + lumonly(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_LUMONLY")))), + retiFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_RETIFRA")))), + str(Gtk::manage(new Adjuster(M("TP_LOCALLAB_STR"), 0., 100., 0.2, 0.))), + loglin(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_LOGLIN")))), + sensih(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIH"), 0, 100, 1, 60))), + retitoolFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_RETITOOLFRA")))), + retinexMethod(Gtk::manage(new MyComboBoxText())), + fftwreti(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTW")))), + equilret(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EQUIL")))), + neigh(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NEIGH"), MINNEIGH, MAXNEIGH, 0.5, 50., nullptr, nullptr, &retiSlider2neigh, &retiNeigh2Slider))), + vart(Gtk::manage(new Adjuster(M("TP_LOCALLAB_VART"), 0.1, 500., 0.1, 150.))), + scalereti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SCALERETI"), 1.0, 10.0, 1., 2.))), + limd(Gtk::manage(new Adjuster(M("TP_LOCALLAB_THRESRETI"), 1.2, 100.0, 0.1, 8.))), + offs(Gtk::manage(new Adjuster(M("TP_LOCALLAB_OFFS"), -16386., 32768., 1., 0.))), + expretitools(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPRETITOOLS")))), + chrrt(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHRRT"), 0.0, 100.0, 0.1, 0.0))), + darkness(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DARKRETI"), 0.01, 6.0, 0.01, 2.0))), + lightnessreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LIGHTRETI"), 0.01, 4.0, 0.01, 1.))), + cliptm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLIPTM"), 0.02, 2.0, 0.01, 1.))), + softradiusret(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRETI"), 0.0, 1000.0, 0.5, 40.))), + LocalcurveEditortransT(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONMAP"))), + cTtransshape(static_cast(LocalcurveEditortransT->addCurve(CT_Flat, "", nullptr, false, false))), + mMLabels(Gtk::manage(new Gtk::Label("---"))), + transLabels(Gtk::manage(new Gtk::Label("---"))), + transLabels2(Gtk::manage(new Gtk::Label("---"))), + LocalcurveEditorgainT(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONGAIN"))), + cTgainshape(static_cast(LocalcurveEditorgainT->addCurve(CT_Flat, "", nullptr, false, false))), + expmaskreti(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWR")))), + showmaskretiMethod(Gtk::manage(new MyComboBoxText())), + enaretiMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enaretiMasktmap(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TM_MASK")))), + maskretiCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 10.))), + lapmaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), + slomaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + mask2retiCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskretishape(static_cast(mask2retiCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + inversret(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter Retinex specific widgets + dehaz->set_tooltip_text(M("TP_LOCALLAB_DEHAZ_TOOLTIP")); + dehaz->setAdjusterListener(this); + + depth->setAdjusterListener(this); + + lumonlyConn = lumonly->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::lumonlyChanged)); + + retiFrame->set_label_align(0.025, 0.5); + + str->setAdjusterListener(this); + + loglinConn = loglin->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::loglinChanged)); + + sensih->setAdjusterListener(this); + + retitoolFrame->set_label_align(0.025, 0.5); + + retinexMethod->append(M("TP_RETINEX_LOW")); + retinexMethod->append(M("TP_RETINEX_UNIFORM")); + retinexMethod->append(M("TP_RETINEX_HIGH")); + retinexMethod->set_active(0); + retinexMethod->set_tooltip_markup(M("TP_LOCRETI_METHOD_TOOLTIP")); + retinexMethodConn = retinexMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabRetinex::retinexMethodChanged)); + + fftwretiConn = fftwreti->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::fftwretiChanged)); + + equilretConn = equilret->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::equilretChanged)); + + neigh->setAdjusterListener(this); + + vart->setAdjusterListener(this); + + scalereti->setAdjusterListener(this); + + limd->setAdjusterListener(this); + + offs->setAdjusterListener(this); + + setExpandAlignProperties(expretitools, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + chrrt->setAdjusterListener(this); + + darkness->setAdjusterListener(this); + + lightnessreti->setAdjusterListener(this); + + cliptm->setAdjusterListener(this); + + softradiusret->setLogScale(10, 0); + softradiusret->setAdjusterListener(this); + + LocalcurveEditortransT->setCurveListener(this); + + cTtransshape->setIdentityValue(0.); + cTtransshape->setResetCurve(FlatCurveType(defSpot.localTtranscurve.at(0)), defSpot.localTtranscurve); + + LocalcurveEditortransT->curveListComplete(); + + setExpandAlignProperties(mMLabels, true, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START); + + setExpandAlignProperties(transLabels, true, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START); + + setExpandAlignProperties(transLabels2, true, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START); + + LocalcurveEditorgainT->setCurveListener(this); + + cTgainshape->setIdentityValue(0.); + cTgainshape->setResetCurve(FlatCurveType(defSpot.localTgaincurve.at(0)), defSpot.localTgaincurve); + + LocalcurveEditorgainT->curveListComplete(); + + setExpandAlignProperties(expmaskreti, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmaskretiMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskretiMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskretiMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskretiMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskretiMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskretiMethod->set_active(0); + showmaskretiMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskretiMethodConn = showmaskretiMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabRetinex::showmaskretiMethodChanged)); + + enaretiMaskConn = enaretiMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::enaretiMaskChanged)); + + enaretiMasktmapConn = enaretiMasktmap->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::enaretiMasktmapChanged)); + + maskretiCurveEditorG->setCurveListener(this); + + CCmaskretishape->setIdentityValue(0.); + CCmaskretishape->setResetCurve(FlatCurveType(defSpot.CCmaskreticurve.at(0)), defSpot.CCmaskreticurve); + CCmaskretishape->setBottomBarColorProvider(this, 1); + + LLmaskretishape->setIdentityValue(0.); + LLmaskretishape->setResetCurve(FlatCurveType(defSpot.LLmaskreticurve.at(0)), defSpot.LLmaskreticurve); + LLmaskretishape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmaskretishape->setIdentityValue(0.); + HHmaskretishape->setResetCurve(FlatCurveType(defSpot.HHmaskreticurve.at(0)), defSpot.HHmaskreticurve); + HHmaskretishape->setCurveColorProvider(this, 2); + HHmaskretishape->setBottomBarColorProvider(this, 2); + + maskretiCurveEditorG->curveListComplete(); + + blendmaskreti->setAdjusterListener(this); + + radmaskreti->setAdjusterListener(this); + + lapmaskreti->setAdjusterListener(this); + + chromaskreti->setAdjusterListener(this); + + gammaskreti->setAdjusterListener(this); + + slomaskreti->setAdjusterListener(this); + + mask2retiCurveEditorG->setCurveListener(this); + + Lmaskretishape->setResetCurve(DiagonalCurveType(defSpot.Lmaskreticurve.at(0)), defSpot.Lmaskreticurve); + Lmaskretishape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmaskretishape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2retiCurveEditorG->curveListComplete(); + + inversretConn = inversret->signal_toggled().connect(sigc::mem_fun(*this, &LocallabRetinex::inversretChanged)); + + // Add Retinex specific widgets to GUI + ToolParamBlock* const auxBox = Gtk::manage(new ToolParamBlock()); + Gtk::Frame* const dehaFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_DEHAFRA"))); + dehaFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const dehaBox = Gtk::manage(new ToolParamBlock()); + dehaBox->pack_start(*dehaz); + dehaBox->pack_start(*depth); + dehaBox->pack_start(*lumonly); + dehaFrame->add(*dehaBox); + auxBox->add(*dehaFrame); + ToolParamBlock* const deharetiBox = Gtk::manage(new ToolParamBlock()); + deharetiBox->pack_start(*str); + deharetiBox->pack_start(*loglin); + retiFrame->add(*deharetiBox); + auxBox->add(*retiFrame); + ToolParamBlock* const scopeBox = Gtk::manage(new ToolParamBlock()); + scopeBox->pack_start(*sensih); + auxBox->add(*scopeBox); + pack_start(*auxBox); + ToolParamBlock* const retiBox = Gtk::manage(new ToolParamBlock()); + retiBox->pack_start(*retinexMethod); + retiBox->pack_start(*fftwreti); + retiBox->pack_start(*equilret); + retiBox->pack_start(*neigh); + retiBox->pack_start(*vart); + retiBox->pack_start(*scalereti); + retiBox->pack_start(*limd); + retiBox->pack_start(*offs); + ToolParamBlock* const toolretiBox = Gtk::manage(new ToolParamBlock()); + toolretiBox->pack_start(*chrrt); + toolretiBox->pack_start(*darkness); + toolretiBox->pack_start(*lightnessreti); + toolretiBox->pack_start(*cliptm); + toolretiBox->pack_start(*softradiusret); + toolretiBox->pack_start(*LocalcurveEditortransT, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + toolretiBox->pack_start(*mMLabels); + toolretiBox->pack_start(*transLabels); + toolretiBox->pack_start(*transLabels2); + toolretiBox->pack_start(*LocalcurveEditorgainT, Gtk::PACK_SHRINK, 4); + expretitools->add(*toolretiBox, false); + retiBox->pack_start(*expretitools, false, false); + ToolParamBlock* const maskretiBox = Gtk::manage(new ToolParamBlock()); + maskretiBox->pack_start(*showmaskretiMethod, Gtk::PACK_SHRINK, 4); + maskretiBox->pack_start(*enaretiMask, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*enaretiMasktmap, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*maskretiCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskretiBox->pack_start(*blendmaskreti, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*radmaskreti, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*lapmaskreti, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*chromaskreti, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*gammaskreti, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*slomaskreti, Gtk::PACK_SHRINK, 0); + maskretiBox->pack_start(*mask2retiCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + expmaskreti->add(*maskretiBox, false); + retiBox->pack_start(*expmaskreti, false, false); + // retiBox->pack_start(*inversret); + retitoolFrame->add(*retiBox); + pack_start(*retitoolFrame); +} + +LocallabRetinex::~LocallabRetinex() +{ + delete LocalcurveEditortransT; + delete LocalcurveEditorgainT; + delete maskretiCurveEditorG; + delete mask2retiCurveEditorG; +} + +void LocallabRetinex::updateMinMax(const double cdma, const double cdmin, const double mini, const double maxi, const double Tmean, const double Tsigma, const double Tmin, const double Tmax) +{ + idle_register.add( + [this, cdma, cdmin, mini, maxi, Tmean, Tsigma, Tmin, Tmax]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + mMLabels->set_text( + Glib::ustring::compose(M("TP_LOCALLAB_MLABEL"), + Glib::ustring::format(std::fixed, std::setprecision(0), cdmin), + Glib::ustring::format(std::fixed, std::setprecision(0), cdma)) + ); + transLabels->set_text( + Glib::ustring::compose(M("TP_LOCALLAB_TLABEL"), + Glib::ustring::format(std::fixed, std::setprecision(1), mini), + Glib::ustring::format(std::fixed, std::setprecision(1), maxi), + Glib::ustring::format(std::fixed, std::setprecision(1), Tmean), + Glib::ustring::format(std::fixed, std::setprecision(1), Tsigma)) + ); + transLabels2->set_text( + Glib::ustring::compose(M("TP_RETINEX_TLABEL2"), + Glib::ustring::format(std::fixed, std::setprecision(1), Tmin), + Glib::ustring::format(std::fixed, std::setprecision(1), Tmax)) + ); + + return false; + } + ); +} + +bool LocallabRetinex::isMaskViewActive() +{ + return (showmaskretiMethod->get_active_row_number() != 0); +} + +void LocallabRetinex::resetMaskView() +{ + showmaskretiMethodConn.block(true); + showmaskretiMethod->set_active(0); + showmaskretiMethodConn.block(false); +} + +void LocallabRetinex::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + retiMask = showmaskretiMethod->get_active_row_number(); +} + +void LocallabRetinex::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + sensih->set_tooltip_text(M("TP_LOCALLAB_SENSIH_TOOLTIP")); + fftwreti->set_tooltip_text(M("TP_LOCALLAB_RETI_FFTW_TOOLTIP")); + loglin->set_tooltip_text(M("TP_LOCALLAB_RETI_LOGLIN_TOOLTIP")); + scalereti->set_tooltip_text(M("TP_LOCALLAB_RETI_SCALE_TOOLTIP")); + limd->set_tooltip_text(M("TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP")); + offs->set_tooltip_text(M("TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP")); + cliptm->set_tooltip_text(M("TP_LOCALLAB_RETI_LIMDOFFS_TOOLTIP")); + lightnessreti->set_tooltip_text(M("TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP")); + darkness->set_tooltip_text(M("TP_LOCALLAB_RETI_LIGHTDARK_TOOLTIP")); + neigh->set_tooltip_text(M("TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP")); + vart->set_tooltip_text(M("TP_LOCALLAB_RETI_NEIGH_VART_TOOLTIP")); + equilret->set_tooltip_text(M("TP_LOCALLAB_EQUILTM_TOOLTIP")); + softradiusret->set_tooltip_text(M("TP_LOCALLAB_GUIDFILTER_TOOLTIP")); + cTtransshape->setTooltip(M("TP_LOCALLAB_TRANSMISSION_TOOLTIP")); + mMLabels->set_tooltip_markup(M("TP_LOCALLAB_MLABEL_TOOLTIP")); + transLabels->set_tooltip_markup(M("TP_LOCALLAB_TLABEL_TOOLTIP")); + cTgainshape->setTooltip(M("TP_RETINEX_GAINTRANSMISSION_TOOLTIP")); + expmaskreti->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + enaretiMasktmap->set_tooltip_markup(M("TP_LOCALLAB_ENARETIMASKTMAP_TOOLTIP")); + CCmaskretishape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskretishape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskretishape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + radmaskreti->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmaskreti->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + Lmaskretishape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + blendmaskreti->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2retiCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + } else { + sensih->set_tooltip_text(""); + fftwreti->set_tooltip_text(""); + neigh->set_tooltip_text(""); + vart->set_tooltip_text(""); + loglin->set_tooltip_text(M("")); + scalereti->set_tooltip_text(M("")); + limd->set_tooltip_text(M("")); + offs->set_tooltip_text(M("")); + lightnessreti->set_tooltip_text(M("")); + darkness->set_tooltip_text(M("")); + equilret->set_tooltip_text(M("")); + softradiusret->set_tooltip_text(""); + cTtransshape->setTooltip(""); + mMLabels->set_tooltip_text(""); + transLabels->set_tooltip_text(""); + cTgainshape->setTooltip(""); + expmaskreti->set_tooltip_text(""); + enaretiMasktmap->set_tooltip_text(""); + CCmaskretishape->setTooltip(""); + LLmaskretishape->setTooltip(""); + HHmaskretishape->setTooltip(""); + radmaskreti->set_tooltip_text(""); + lapmaskreti->set_tooltip_text(""); + Lmaskretishape->setTooltip(""); + blendmaskreti->set_tooltip_text(M("")); + mask2retiCurveEditorG->set_tooltip_text(M("")); + } +} + +void LocallabRetinex::setDefaultExpanderVisibility() +{ + expretitools->set_expanded(false); + expmaskreti->set_expanded(false); +} + +void LocallabRetinex::disableListener() +{ + LocallabTool::disableListener(); + + lumonlyConn.block(true); + loglinConn.block(true); + retinexMethodConn.block(true); + fftwretiConn.block(true); + equilretConn.block(true); + showmaskretiMethodConn.block(true); + enaretiMaskConn.block(true); + enaretiMasktmapConn.block(true); + inversretConn.block(true); +} + +void LocallabRetinex::enableListener() +{ + LocallabTool::enableListener(); + + lumonlyConn.block(false); + loglinConn.block(false); + retinexMethodConn.block(false); + fftwretiConn.block(false); + equilretConn.block(false); + showmaskretiMethodConn.block(false); + enaretiMaskConn.block(false); + enaretiMasktmapConn.block(false); + inversretConn.block(false); +} + +void LocallabRetinex::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visireti); + exp->setEnabled(spot.expreti); + complexity->set_active(spot.complexreti); + + dehaz->setValue((double)spot.dehaz); + depth->setValue((double)spot.depth); + lumonly->set_active(spot.lumonly); + str->setValue(spot.str); + loglin->set_active(spot.loglin); + sensih->setValue((double)spot.sensih); + + if (spot.retinexMethod == "low") { + retinexMethod->set_active(0); + } else if (spot.retinexMethod == "uni") { + retinexMethod->set_active(1); + } else { + retinexMethod->set_active(2); + } + + fftwreti->set_active(spot.fftwreti); + equilret->set_active(spot.equilret); + neigh->setValue(spot.neigh); + vart->setValue(spot.vart); + scalereti->setValue(spot.scalereti); + limd->setValue(spot.limd); + offs->setValue(spot.offs); + chrrt->setValue(spot.chrrt); + darkness->setValue(spot.darkness); + lightnessreti->setValue(spot.lightnessreti); + cliptm->setValue(spot.cliptm); + softradiusret->setValue(spot.softradiusret); + cTtransshape->setCurve(spot.localTtranscurve); + cTgainshape->setCurve(spot.localTgaincurve); + enaretiMask->set_active(spot.enaretiMask); + enaretiMasktmap->set_active(spot.enaretiMasktmap); + CCmaskretishape->setCurve(spot.CCmaskreticurve); + LLmaskretishape->setCurve(spot.LLmaskreticurve); + HHmaskretishape->setCurve(spot.HHmaskreticurve); + blendmaskreti->setValue((double)spot.blendmaskreti); + radmaskreti->setValue(spot.radmaskreti); + lapmaskreti->setValue(spot.lapmaskreti); + chromaskreti->setValue(spot.chromaskreti); + gammaskreti->setValue(spot.gammaskreti); + slomaskreti->setValue(spot.slomaskreti); + Lmaskretishape->setCurve(spot.Lmaskreticurve); + inversret->set_active(spot.inversret); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update Retinex GUI according to scalereti adjuster value + updateRetinexGUI1(); + + // Update Retinex GUI according to inversret button state + updateRetinexGUI2(); + + // Update Retinex GUI according to str adjuster value + updateRetinexGUI3(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabRetinex::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expreti = exp->getEnabled(); + spot.visireti = exp->get_visible(); + spot.complexreti = complexity->get_active_row_number(); + + spot.dehaz = dehaz->getIntValue(); + spot.depth = depth->getIntValue(); + spot.lumonly = lumonly->get_active(); + spot.str = str->getValue(); + spot.loglin = loglin->get_active(); + spot.sensih = sensih->getIntValue(); + + if (retinexMethod->get_active_row_number() == 0) { + spot.retinexMethod = "low"; + } else if (retinexMethod->get_active_row_number() == 1) { + spot.retinexMethod = "uni"; + } else if (retinexMethod->get_active_row_number() == 2) { + spot.retinexMethod = "high"; + } + + spot.fftwreti = fftwreti->get_active(); + spot.equilret = equilret->get_active(); + spot.neigh = neigh->getValue(); + spot.vart = vart->getValue(); + spot.scalereti = scalereti->getValue(); + spot.limd = limd->getValue(); + spot.offs = offs->getValue(); + spot.chrrt = chrrt->getValue(); + spot.darkness = darkness->getValue(); + spot.lightnessreti = lightnessreti->getValue(); + spot.cliptm = cliptm->getValue(); + spot.softradiusret = softradiusret->getValue(); + spot.localTtranscurve = cTtransshape->getCurve(); + spot.localTgaincurve = cTgainshape->getCurve(); + spot.enaretiMask = enaretiMask->get_active(); + spot.enaretiMasktmap = enaretiMasktmap->get_active(); + spot.CCmaskreticurve = CCmaskretishape->getCurve(); + spot.LLmaskreticurve = LLmaskretishape->getCurve(); + spot.HHmaskreticurve = HHmaskretishape->getCurve(); + spot.blendmaskreti = blendmaskreti->getIntValue(); + spot.radmaskreti = radmaskreti->getValue(); + spot.lapmaskreti = lapmaskreti->getValue(); + spot.chromaskreti = chromaskreti->getValue(); + spot.gammaskreti = gammaskreti->getValue(); + spot.slomaskreti = slomaskreti->getValue(); + spot.Lmaskreticurve = Lmaskretishape->getCurve(); + spot.inversret = inversret->get_active(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabRetinex::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + dehaz->setDefault((double)defSpot.dehaz); + depth->setDefault((double)defSpot.depth); + str->setDefault(defSpot.str); + sensih->setDefault((double)defSpot.sensih); + neigh->setDefault(defSpot.neigh); + vart->setDefault(defSpot.vart); + scalereti->setDefault(defSpot.scalereti); + limd->setDefault(defSpot.limd); + offs->setDefault(defSpot.offs); + chrrt->setDefault(defSpot.chrrt); + darkness->setDefault(defSpot.darkness); + lightnessreti->setDefault(defSpot.lightnessreti); + cliptm->setDefault(defSpot.cliptm); + softradiusret->setDefault(defSpot.softradiusret); + blendmaskreti->setDefault((double)defSpot.blendmaskreti); + radmaskreti->setDefault(defSpot.radmaskreti); + lapmaskreti->setDefault(defSpot.lapmaskreti); + chromaskreti->setDefault(defSpot.chromaskreti); + gammaskreti->setDefault(defSpot.gammaskreti); + slomaskreti->setDefault(defSpot.slomaskreti); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabRetinex::adjusterChanged(Adjuster* a, double newval) +{ + // Update Retinex GUI according to scalereti adjuster value + if (a == scalereti) { + updateRetinexGUI1(); + } + + // Update Retinex GUI according to str adjuster value + if (a == str) { + updateRetinexGUI3(); + } + + if (isLocActivated && exp->getEnabled()) { + if (a == dehaz) { + if (listener) { + listener->panelChanged(Evlocallabdehaz, + dehaz->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == depth) { + if (listener) { + listener->panelChanged(Evlocallabdepth, + depth->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == str) { + if (listener) { + listener->panelChanged(Evlocallabstr, + str->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensih) { + if (listener) { + listener->panelChanged(Evlocallabsensih, + sensih->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == neigh) { + if (listener) { + listener->panelChanged(Evlocallabneigh, + neigh->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == vart) { + if (listener) { + listener->panelChanged(Evlocallabvart, + vart->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == scalereti) { + if (listener) { + listener->panelChanged(Evlocallabscalereti, + scalereti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == limd) { + if (listener) { + listener->panelChanged(Evlocallablimd, + limd->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == offs) { + if (listener) { + listener->panelChanged(Evlocallaboffs, + offs->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chrrt) { + if (listener) { + listener->panelChanged(Evlocallabchrrt, + chrrt->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == darkness) { + if (listener) { + listener->panelChanged(Evlocallabdarkness, + darkness->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lightnessreti) { + if (listener) { + listener->panelChanged(Evlocallablightnessreti, + lightnessreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == cliptm) { + if (listener) { + listener->panelChanged(Evlocallabcliptm, + cliptm->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == softradiusret) { + if (listener) { + listener->panelChanged(Evlocallabsoftradiusret, + softradiusret->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskreti) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskreti, + blendmaskreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskreti) { + if (listener) { + listener->panelChanged(Evlocallabradmaskreti, + radmaskreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskreti) { + if (listener) { + listener->panelChanged(Evlocallablapmaskreti, + lapmaskreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskreti) { + if (listener) { + listener->panelChanged(Evlocallabchromaskreti, + chromaskreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskreti) { + if (listener) { + listener->panelChanged(Evlocallabgammaskreti, + gammaskreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskreti) { + if (listener) { + listener->panelChanged(Evlocallabslomaskreti, + slomaskreti->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == cTtransshape) { + if (listener) { + listener->panelChanged(EvlocallabCTtransCurve, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == cTgainshape) { + if (listener) { + listener->panelChanged(EvlocallabCTgainCurve, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == CCmaskretishape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskretishape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskretishape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskretishape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskretishape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskretishape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmaskretishape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskretishape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenareti, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenareti, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + str->setValue(defSpot.str); + loglin->set_active(defSpot.loglin); + + if (defSpot.retinexMethod == "low") { + retinexMethod->set_active(0); + } else if (defSpot.retinexMethod == "uni") { + retinexMethod->set_active(1); + } else { + retinexMethod->set_active(2); + } + + fftwreti->set_active(defSpot.fftwreti); + equilret->set_active(defSpot.equilret); + neigh->setValue(defSpot.neigh); + vart->setValue(defSpot.vart); + scalereti->setValue(defSpot.scalereti); + limd->setValue(defSpot.limd); + offs->setValue(defSpot.offs); + chrrt->setValue(defSpot.chrrt); + darkness->setValue(defSpot.darkness); + lightnessreti->setValue(defSpot.lightnessreti); + cliptm->setValue(defSpot.cliptm); + softradiusret->setValue(defSpot.softradiusret); + cTtransshape->setCurve(defSpot.localTtranscurve); + cTgainshape->setCurve(defSpot.localTgaincurve); + enaretiMask->set_active(defSpot.enaretiMask); + enaretiMasktmap->set_active(defSpot.enaretiMasktmap); + CCmaskretishape->setCurve(defSpot.CCmaskreticurve); + LLmaskretishape->setCurve(defSpot.LLmaskreticurve); + HHmaskretishape->setCurve(defSpot.HHmaskreticurve); + blendmaskreti->setValue((double)defSpot.blendmaskreti); + radmaskreti->setValue(defSpot.radmaskreti); + lapmaskreti->setValue(defSpot.lapmaskreti); + chromaskreti->setValue(defSpot.chromaskreti); + gammaskreti->setValue(defSpot.gammaskreti); + slomaskreti->setValue(defSpot.slomaskreti); + Lmaskretishape->setCurve(defSpot.Lmaskreticurve); + inversret->set_active(defSpot.inversret); + + // Enable all listeners + enableListener(); + + // Update GUI based on converted widget parameters: + // - Update Retinex GUI according to scalereti adjuster value + updateRetinexGUI1(); + // - Update Retinex GUI according to inversret button state + updateRetinexGUI2(); + // - Update Retinex GUI according to str adjuster value + updateRetinexGUI3(); +} + +void LocallabRetinex::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + retiFrame->hide(); + retitoolFrame->hide(); + } else { + // Advanced widgets are shown in Expert mode + retiFrame->show(); + retitoolFrame->show(); + } +} + +void LocallabRetinex::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskretishape->updateLocallabBackground(normChromar); + LLmaskretishape->updateLocallabBackground(normLumar); + HHmaskretishape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabRetinex::lumonlyChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (lumonly->get_active()) { + listener->panelChanged(Evlocallablumonly, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallablumonly, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::loglinChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (loglin->get_active()) { + listener->panelChanged(Evlocallabloglin, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabloglin, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::retinexMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabretinexMethod, + retinexMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabRetinex::fftwretiChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fftwreti->get_active()) { + listener->panelChanged(Evlocallabfftwreti, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabfftwreti, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::equilretChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (inversret->get_active()) { + listener->panelChanged(Evlocallabequilret, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabequilret, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::showmaskretiMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabRetinex::enaretiMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enaretiMask->get_active()) { + listener->panelChanged(EvLocallabEnaretiMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaretiMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::enaretiMasktmapChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enaretiMasktmap->get_active()) { + listener->panelChanged(EvLocallabEnaretiMasktmap, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnaretiMasktmap, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::inversretChanged() +{ + // Update Retinex GUI according to inversret button state + updateRetinexGUI2(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (inversret->get_active()) { + listener->panelChanged(Evlocallabinversret, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabinversret, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabRetinex::updateRetinexGUI1() +{ + // Update Retinex GUI according to scalereti adjuster value + if (scalereti->getValue() == 1) { + retinexMethod->hide(); + softradiusret->hide(); + LocalcurveEditortransT->hide(); + LocalcurveEditorgainT->hide(); + } else { + retinexMethod->show(); + softradiusret->show(); + LocalcurveEditortransT->show(); + LocalcurveEditorgainT->show(); + } +} + +void LocallabRetinex::updateRetinexGUI2() +{ + // Update Retinex GUI according to inversret button state + if (inversret->get_active()) { + expmaskreti->hide(); + } else { + expmaskreti->show(); + } +} + +void LocallabRetinex::updateRetinexGUI3() +{ + if (str->getValue() >= 0.1f) { + retitoolFrame->show(); + } else { + retitoolFrame->hide(); + } +} + +/* ==== LocallabSharp ==== */ +LocallabSharp::LocallabSharp(): + LocallabTool(this, M("TP_LOCALLAB_SHARP_TOOLNAME"), M("TP_LOCALLAB_SHARP"), true), + + // Sharpening specific widgets + sharcontrast(Gtk::manage(new Adjuster(M("TP_SHARPENING_CONTRAST"), 0, 200, 1, 20))), + sharblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHARBLUR"), 0.2, 2.0, 0.05, 0.2))), + sharamount(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHARAMOUNT"), 0, 100, 1, 100))), + shardamping(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHARDAMPING"), 0, 100, 1, 0))), + shariter(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHARITER"), 5, 100, 1, 30))), + sharradius(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHARRADIUS"), 0.4, 2.5, 0.01, 0.75))), + sensisha(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIS"), 0, 100, 1, 40))), + inverssha(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))), + showmasksharMethod(Gtk::manage(new MyComboBoxText())) +{ + // Parameter Sharpening specific widgets + sharcontrast->setAdjusterListener(this); + + sharradius->setAdjusterListener(this); + + sharamount->setAdjusterListener(this); + + shardamping->setAdjusterListener(this); + + shariter->setAdjusterListener(this); + + sharblur->setAdjusterListener(this); + + sensisha->setAdjusterListener(this); + + inversshaConn = inverssha->signal_toggled().connect(sigc::mem_fun(*this, &LocallabSharp::inversshaChanged)); + + showmasksharMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmasksharMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmasksharMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmasksharMethod->set_active(0); + showmasksharMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmasksharMethodConn = showmasksharMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabSharp::showmasksharMethodChanged)); + + // Add Sharpening specific widgets to GUI + pack_start(*sharcontrast); + pack_start(*sharblur); + pack_start(*sharradius); + pack_start(*sharamount); + pack_start(*shardamping); + pack_start(*shariter); + pack_start(*sensisha); + pack_start(*inverssha); + Gtk::Frame* const sharFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_SHARFRAME"))); + sharFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const sharfBox = Gtk::manage(new ToolParamBlock()); + sharfBox->pack_start(*showmasksharMethod); + sharFrame->add(*sharfBox); + pack_start(*sharFrame); +} + +bool LocallabSharp::isMaskViewActive() +{ + return (showmasksharMethod->get_active_row_number() != 0); +} + +void LocallabSharp::resetMaskView() +{ + showmasksharMethodConn.block(true); + showmasksharMethod->set_active(0); + showmasksharMethodConn.block(false); +} + +void LocallabSharp::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + sharMask = showmasksharMethod->get_active_row_number(); +} + +void LocallabSharp::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_EXPSHARP_TOOLTIP")); + sensisha->set_tooltip_text(M("TP_LOCALLAB_SENSIS_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + sensisha->set_tooltip_text(""); + } +} + +void LocallabSharp::disableListener() +{ + LocallabTool::disableListener(); + + inversshaConn.block(true); + showmasksharMethodConn.block(true); +} + +void LocallabSharp::enableListener() +{ + LocallabTool::enableListener(); + + inversshaConn.block(false); + showmasksharMethodConn.block(false); +} + +void LocallabSharp::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visisharp); + exp->setEnabled(spot.expsharp); + complexity->set_active(spot.complexsharp); + + sharcontrast->setValue((double)spot.sharcontrast); + sharradius->setValue(spot.sharradius); + sharamount->setValue((double)spot.sharamount); + shardamping->setValue((double)spot.shardamping); + shariter->setValue((double)spot.shariter); + sharblur->setValue(spot.sharblur); + sensisha->setValue((double)spot.sensisha); + inverssha->set_active(spot.inverssha); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabSharp::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expsharp = exp->getEnabled(); + spot.visisharp = exp->get_visible(); + spot.complexsharp = complexity->get_active_row_number(); + + spot.sharcontrast = sharcontrast->getIntValue(); + spot.sharradius = sharradius->getValue(); + spot.sharamount = sharamount->getIntValue(); + spot.shardamping = shardamping->getIntValue(); + spot.shariter = shariter->getIntValue(); + spot.sharblur = sharblur->getValue(); + spot.sensisha = sensisha->getIntValue(); + spot.inverssha = inverssha->get_active(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabSharp::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + sharcontrast->setDefault((double)defSpot.sharcontrast); + sharradius->setDefault(defSpot.sharradius); + sharamount->setDefault((double)defSpot.sharamount); + shardamping->setDefault((double)defSpot.shardamping); + shariter->setDefault((double)defSpot.shariter); + sharblur->setDefault(defSpot.sharblur); + sensisha->setDefault((double)defSpot.sensisha); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabSharp::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == sharcontrast) { + if (listener) { + listener->panelChanged(Evlocallabsharcontrast, + sharcontrast->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sharradius) { + if (listener) { + listener->panelChanged(Evlocallabsharradius, + sharradius->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sharamount) { + if (listener) { + listener->panelChanged(Evlocallabsharamount, + sharamount->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shardamping) { + if (listener) { + listener->panelChanged(Evlocallabshardamping, + shardamping->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == shariter) { + if (listener) { + listener->panelChanged(Evlocallabshariter, + shariter->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sharblur) { + if (listener) { + listener->panelChanged(Evlocallabsharblur, + sharblur->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensisha) { + if (listener) { + listener->panelChanged(Evlocallabsensis, + sensisha->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabSharp::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenasharp, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenasharp, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabSharp::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + shardamping->setValue((double)defSpot.shardamping); + shariter->setValue((double)defSpot.shariter); + sharblur->setValue(defSpot.sharblur); + sharamount->setValue(defSpot.sharamount); + + // Enable all listeners + enableListener(); +} + +void LocallabSharp::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + shardamping->hide(); + shariter->hide(); + sharblur->hide(); + sharcontrast->hide(); + sharamount->hide(); + } else { + // Advanced widgets are shown in Expert mode + shardamping->show(); + shariter->show(); + sharblur->hide(); + sharcontrast->hide(); + sharamount->show(); + } +} + +void LocallabSharp::inversshaChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (inverssha->get_active()) { + listener->panelChanged(Evlocallabinverssha, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabinverssha, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabSharp::showmasksharMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +/* ==== LocallabContrast ==== */ +LocallabContrast::LocallabContrast(): + LocallabTool(this, M("TP_LOCALLAB_LC_TOOLNAME"), M("TP_LOCALLAB_LOC_CONTRAST"), true), + + // Local contrast specific widgets + localcontMethod(Gtk::manage(new MyComboBoxText())), + lcradius(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_RADIUS"), 10, 100, 1, 80))), + lcamount(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_AMOUNT"), 0, 1.0, 0.01, 0))), + lcdarkness(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_DARKNESS"), 0, 3.0, 0.01, 1.0))), + lclightness(Gtk::manage(new Adjuster(M("TP_LOCALCONTRAST_LIGHTNESS"), 0, 3.0, 0.01, 1.0))), + sigmalc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + LocalcurveEditorwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAV"))), + wavshape(static_cast(LocalcurveEditorwav->addCurve(CT_Flat, "", nullptr, false, false))), + levelwav(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LEVELWAV"), 1, 9, 1, 4))), + csThreshold(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLD"), 0, 9, 0, 0, 6, 6, 0, false))), + expresidpyr(Gtk::manage(new MyExpander(false, Gtk::manage(new Gtk::HBox())))), + residcont(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDCONT"), -100, 100, 1, 0))), + residchro(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDCHRO"), -100., 100., 1., 0.))), + residsha(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDSHA"), -100., 100., 1., 0.))), + residshathr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDSHATHR"), 0., 100., 1., 30.))), + residhi(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDHI"), -100., 100., 1., 0.))), + residhithr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDHITHR"), 0., 100., 1., 70.))), + sensilc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSIS"), 0, 100, 1, 60))), + clariFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CLARIFRA")))), + clarilres(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLARILRES"), -20., 100., 0.5, 0.))), + claricres(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLARICRES"), -20., 100., 0.5, 0.))), + clarisoft(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 1.))), + origlc(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ORIGLC")))), + expcontrastpyr(Gtk::manage(new MyExpander(false, Gtk::manage(new Gtk::HBox())))), + wavgradl(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_GRALWFRA")))), + sigmalc2(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + strwav(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -4.0, 4.0, 0.05, 0.))), + angwav(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))), + wavedg(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EDGFRA")))), + strengthw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDVAL"), 0., 100.0, 0.5, 0.))), + sigmaed(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + LocalcurveEditorwavedg(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVEDG"))), + wavshapeedg(static_cast(LocalcurveEditorwavedg->addCurve(CT_Flat, "", nullptr, false, false))), + gradw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECT"), 0., 100.0, 0.5, 90.))), + waveshow(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EDGSHOW")))), + edgsBoxshow(Gtk::manage(new ToolParamBlock())), + radiusw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDRAD"), 5., 100.0, 0.5, 15.))), + detailw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGTHRESH"), -50., 100.0, 1., 10.))), + localedgMethod(Gtk::manage(new MyComboBoxText())), + tloww(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECTTHR"), 0., 100.0, 1., 20.))), + thigw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECTTHR2"), -10., 100.0, 1., 0.))), + edgw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGESENSI"), 0., 100.0, 1., 60.))), + basew(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEAMPLI"), 0., 100.0, 1., 10.))), + localneiMethod(Gtk::manage(new MyComboBoxText())), + wavblur(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_BLURLEVELFRA")))), + levelblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LEVELBLUR"), 0., 100., 0.5, 0.))), + sigmabl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + chromablu(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMABLU"), 0.0, 5., 0.1, 0.))), + LocalcurveEditorwavlev(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVLEV"))), + wavshapelev(static_cast(LocalcurveEditorwavlev->addCurve(CT_Flat, "", nullptr, false, false))), + residblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDBLUR"), 0., 100., 0.5, 0.))), + blurlc(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_BLURLC")))), + expcontrastpyr2(Gtk::manage(new MyExpander(false, Gtk::manage(new Gtk::HBox())))), + wavcont(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_CONTFRA")))), + sigma(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + offset(Gtk::manage(new Adjuster(M("TP_LOCALLAB_OFFSETWAV"), 0.33, 1.66, 0.01, 1., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + chromalev(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMALEV"), 0.1, 5., 0.1, 1.))), + LocalcurveEditorwavcon(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVCON"))), + wavshapecon(static_cast(LocalcurveEditorwavcon->addCurve(CT_Flat, "", nullptr, false, false))), + wavcompre(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_COMPREFRA")))), + LocalcurveEditorwavcompre(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVCOMPRE"))), + wavshapecompre(static_cast(LocalcurveEditorwavcompre->addCurve(CT_Flat, "", nullptr, false, false))), + sigmadr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + threswav(Gtk::manage(new Adjuster(M("TP_LOCALLAB_THRESWAV"), 0.9, 2., 0.01, 1.4))), + residcomp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDCOMP"), -1., 1., 0.01, 0.))), + wavcomp(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_COMPFRA")))), + sigmadc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 3., 0.01, 1.))), + deltad(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DELTAD"), -3., 3., 0.1, 0.))),//, Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + LocalcurveEditorwavcomp(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVCOMP"))), + wavshapecomp(static_cast(LocalcurveEditorwavcomp->addCurve(CT_Flat, "", nullptr, false, false))), + fatres(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATRES"), 0., 100., 1., 0.))), + fftwlc(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTW")))), + expmasklc(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWLC")))), + showmasklcMethod(Gtk::manage(new MyComboBoxText())), + enalcMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + masklcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + chromasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + mask2lcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmasklcshape(static_cast(mask2lcCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter Local contrast specific widgets + localcontMethod->append(M("TP_LOCALLAB_LOCCONT")); + localcontMethod->append(M("TP_LOCALLAB_WAVE")); + localcontMethod->set_active(0); + localcontMethodConn = localcontMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabContrast::localcontMethodChanged)); + + lcradius->setAdjusterListener(this); + + lcamount->setAdjusterListener(this); + + lcdarkness->setAdjusterListener(this); + + lclightness->setAdjusterListener(this); + sigmalc->setAdjusterListener(this); + + LocalcurveEditorwav->setCurveListener(this); + + wavshape->setIdentityValue(0.); + wavshape->setResetCurve(FlatCurveType(defSpot.locwavcurve.at(0)), defSpot.locwavcurve); + wavshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + LocalcurveEditorwav->curveListComplete(); + + levelwav->setAdjusterListener(this); + + csThreshold->setAdjusterListener(this); + + Gtk::HBox* const LresTitleHBox = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const LresLabel = Gtk::manage(new Gtk::Label()); + LresLabel->set_markup(Glib::ustring("") + escapeHtmlChars(M("TP_LOCALLAB_LOC_RESIDPYR")) + Glib::ustring("")); + LresLabel->set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + LresTitleHBox->pack_start(*LresLabel, Gtk::PACK_EXPAND_WIDGET, 0); + expresidpyr->setLabel(LresTitleHBox); + setExpandAlignProperties(expresidpyr, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + residcont->setAdjusterListener(this); + + residchro->setAdjusterListener(this); + + residsha->setAdjusterListener(this); + + residshathr->setAdjusterListener(this); + + residhi->setAdjusterListener(this); + + residhithr->setAdjusterListener(this); + + sensilc->setAdjusterListener(this); + + clariFrame->set_label_align(0.025, 0.5); + + clarilres->setAdjusterListener(this); + + claricres->setAdjusterListener(this); + + clarisoft->setLogScale(10, 0); + clarisoft->setAdjusterListener(this); + + origlcConn = origlc->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::origlcChanged)); + + Gtk::HBox* const LCTitleHBox = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const LCLabel = Gtk::manage(new Gtk::Label()); + LCLabel->set_markup(Glib::ustring("") + escapeHtmlChars(M("TP_LOCALLAB_LOC_CONTRASTPYR")) + Glib::ustring("") + escapeHtmlChars(M("TP_LOCALLAB_LOC_CONTRASTPYRLAB"))); + LCLabel->set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + LCTitleHBox->pack_start(*LCLabel, Gtk::PACK_EXPAND_WIDGET, 0); + expcontrastpyr->setLabel(LCTitleHBox); + setExpandAlignProperties(expcontrastpyr, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + wavgradlConn = wavgradl->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::wavgradlChanged)); + + sigmalc2->setAdjusterListener(this); + + strwav->setAdjusterListener(this); + + angwav->setAdjusterListener(this); + + wavedgConn = wavedg->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::wavedgChanged)); + + strengthw->setAdjusterListener(this); + + sigmaed->setAdjusterListener(this); + + LocalcurveEditorwavedg->setCurveListener(this); + + wavshapeedg->setIdentityValue(0.); + wavshapeedg->setResetCurve(FlatCurveType(defSpot.locedgwavcurve.at(0)), defSpot.locedgwavcurve); + wavshapeedg->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + LocalcurveEditorwavedg->curveListComplete(); + + gradw->setAdjusterListener(this); + + waveshowConn = waveshow->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::waveshowChanged)); + + radiusw->setAdjusterListener(this); + + detailw->setAdjusterListener(this); + + localedgMethod->append(M("TP_WAVELET_RE1")); + localedgMethod->append(M("TP_WAVELET_RE2")); + localedgMethod->append(M("TP_WAVELET_RE3")); + localedgMethod->set_active(0); + localedgMethodConn = localedgMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabContrast::localedgMethodChanged)); + + tloww->setAdjusterListener(this); + + thigw->setAdjusterListener(this); + + edgw->setAdjusterListener(this); + + basew->setAdjusterListener(this); + + localneiMethod->append(M("TP_WAVELET_NPNONE")); + localneiMethod->append(M("TP_WAVELET_NPLOW")); + localneiMethod->append(M("TP_WAVELET_NPHIGH")); + localneiMethod->set_active(0); + localneiMethodConn = localneiMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabContrast::localneiMethodChanged)); + + wavblurConn = wavblur->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::wavblurChanged)); + + levelblur->setAdjusterListener(this); + + sigmabl->setAdjusterListener(this); + + chromablu->setAdjusterListener(this); + + LocalcurveEditorwavlev->setCurveListener(this); + + wavshapelev->setIdentityValue(0.); + wavshapelev->setResetCurve(FlatCurveType(defSpot.loclevwavcurve.at(0)), defSpot.loclevwavcurve); + + LocalcurveEditorwavlev->curveListComplete(); + + residblur->setAdjusterListener(this); + + blurlcConn = blurlc->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::blurlcChanged)); + + Gtk::HBox* const LCTitleHBox2 = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const LCLabel2 = Gtk::manage(new Gtk::Label()); + LCLabel2->set_markup(Glib::ustring("") + escapeHtmlChars(M("TP_LOCALLAB_LOC_CONTRASTPYR2")) + Glib::ustring("") + escapeHtmlChars(M("TP_LOCALLAB_LOC_CONTRASTPYR2LAB"))); + LCLabel2->set_alignment(Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + LCTitleHBox2->pack_start(*LCLabel2, Gtk::PACK_EXPAND_WIDGET, 0); + expcontrastpyr2->setLabel(LCTitleHBox2); + setExpandAlignProperties(expcontrastpyr2, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + wavcontConn = wavcont->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::wavcontChanged)); + + sigma->setAdjusterListener(this); + + offset->setAdjusterListener(this); + + chromalev->setAdjusterListener(this); + + LocalcurveEditorwavcon->setCurveListener(this); + + wavshapecon->setIdentityValue(0.); + wavshapecon->setResetCurve(FlatCurveType(defSpot.locconwavcurve.at(0)), defSpot.locconwavcurve); + + LocalcurveEditorwavcon->curveListComplete(); + + wavcompreConn = wavcompre->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::wavcompreChanged)); + + LocalcurveEditorwavcompre->setCurveListener(this); + + wavshapecompre->setIdentityValue(0.); + wavshapecompre->setResetCurve(FlatCurveType(defSpot.loccomprewavcurve.at(0)), defSpot.loccomprewavcurve); + wavshapecompre->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_LL_TOOLTIP")); + + LocalcurveEditorwavcompre->curveListComplete(); + + sigmadr->setAdjusterListener(this); + + threswav->setAdjusterListener(this); + + residcomp->setAdjusterListener(this); + + wavcompConn = wavcomp->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::wavcompChanged)); + + sigmadc->setAdjusterListener(this); + + deltad->setAdjusterListener(this); + + LocalcurveEditorwavcomp->setCurveListener(this); + + wavshapecomp->setIdentityValue(0.); + wavshapecomp->setResetCurve(FlatCurveType(defSpot.loccompwavcurve.at(0)), defSpot.loccompwavcurve); + wavshapecomp->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + LocalcurveEditorwavcomp->curveListComplete(); + + fatres->setAdjusterListener(this); + + fftwlcConn = fftwlc->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::fftwlcChanged)); + + setExpandAlignProperties(expmasklc, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + + showmasklcMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmasklcMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmasklcMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmasklcMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmasklcMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmasklcMethod->set_active(0); + showmasklcMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmasklcMethodConn = showmasklcMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabContrast::showmasklcMethodChanged)); + + enalcMaskConn = enalcMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabContrast::enalcMaskChanged)); + + masklcCurveEditorG->setCurveListener(this); + + CCmasklcshape->setIdentityValue(0.); + CCmasklcshape->setResetCurve(FlatCurveType(defSpot.CCmasklccurve.at(0)), defSpot.CCmasklccurve); + CCmasklcshape->setBottomBarColorProvider(this, 1); + + LLmasklcshape->setIdentityValue(0.); + LLmasklcshape->setResetCurve(FlatCurveType(defSpot.LLmasklccurve.at(0)), defSpot.LLmasklccurve); + LLmasklcshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmasklcshape->setIdentityValue(0.); + HHmasklcshape->setResetCurve(FlatCurveType(defSpot.HHmasklccurve.at(0)), defSpot.HHmasklccurve); + HHmasklcshape->setCurveColorProvider(this, 2); + HHmasklcshape->setBottomBarColorProvider(this, 2); + + masklcCurveEditorG->curveListComplete(); + + blendmasklc->setAdjusterListener(this); + + radmasklc->setLogScale(10, -10); + radmasklc->setAdjusterListener(this); + + chromasklc->setAdjusterListener(this); + + mask2lcCurveEditorG->setCurveListener(this); + + Lmasklcshape->setResetCurve(DiagonalCurveType(defSpot.Lmasklccurve.at(0)), defSpot.Lmasklccurve); + Lmasklcshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmasklcshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2lcCurveEditorG->curveListComplete(); + + // Add Local contrast specific widgets to GUI + pack_start(*localcontMethod); + pack_start(*lcradius); + pack_start(*lcamount); + pack_start(*lcdarkness); + pack_start(*lclightness); + pack_start(*sigmalc); + pack_start(*LocalcurveEditorwav, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + // pack_start(*levelwav); + pack_start(*csThreshold); + Gtk::Frame* const shresFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_SHRESFRA"))); + shresFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const resiBox = Gtk::manage(new ToolParamBlock()); + resiBox->pack_start(*residcont); + resiBox->pack_start(*residchro); + ToolParamBlock* const shresBox = Gtk::manage(new ToolParamBlock()); + shresBox->pack_start(*residsha); + shresBox->pack_start(*residshathr); + shresBox->pack_start(*residhi); + shresBox->pack_start(*residhithr); + shresFrame->add(*shresBox); + resiBox->pack_start(*shresFrame); + expresidpyr->add(*resiBox, false); + pack_start(*expresidpyr); + pack_start(*sensilc); + Gtk::HSeparator* const separatorcontr = Gtk::manage(new Gtk::HSeparator()); + pack_start(*separatorcontr); + ToolParamBlock* const clariBox = Gtk::manage(new ToolParamBlock()); + clariBox->pack_start(*clarilres); + clariBox->pack_start(*claricres); + clariBox->pack_start(*clarisoft); + clariBox->pack_start(*origlc); + clariFrame->add(*clariBox); + pack_start(*clariFrame); + ToolParamBlock* const blurcontBox = Gtk::manage(new ToolParamBlock()); + Gtk::Frame* const gradwavFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRADWAVFRA"))); + gradwavFrame->set_label_align(0.025, 0.5); + gradwavFrame->set_label_widget(*wavgradl); + ToolParamBlock* const gradwavBox = Gtk::manage(new ToolParamBlock()); + gradwavBox->pack_start(*sigmalc2); + gradwavBox->pack_start(*strwav); + gradwavBox->pack_start(*angwav); + gradwavFrame->add(*gradwavBox); + blurcontBox->pack_start(*gradwavFrame); + Gtk::Frame* const edgFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_EDGSHARPFRA"))); + edgFrame->set_label_align(0.025, 0.5); + edgFrame->set_label_widget(*wavedg); + ToolParamBlock* const edgsBox = Gtk::manage(new ToolParamBlock()); + edgsBox->pack_start(*strengthw); + edgsBox->pack_start(*sigmaed); + edgsBox->pack_start(*LocalcurveEditorwavedg, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + edgsBox->pack_start(*gradw); + edgsBox->pack_start(*waveshow); + edgsBoxshow->pack_start(*radiusw); + edgsBoxshow->pack_start(*detailw); + Gtk::HBox* const edbox = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labmedgr = Gtk::manage(new Gtk::Label(M("TP_WAVELET_MEDGREINF") + ":")); + edbox->pack_start(*labmedgr, Gtk::PACK_SHRINK, 1); + edbox->pack_start(*localedgMethod); + edgsBoxshow->pack_start(*edbox); + Gtk::HSeparator* const separatoredg2 = Gtk::manage(new Gtk::HSeparator()); + edgsBoxshow->pack_start(*separatoredg2); + edgsBoxshow->pack_start(*tloww); + edgsBoxshow->pack_start(*thigw); + Gtk::HSeparator* const separatoredg = Gtk::manage(new Gtk::HSeparator()); + edgsBoxshow->pack_start(*separatoredg); + edgsBoxshow->pack_start(*edgw); + edgsBoxshow->pack_start(*basew); + Gtk::HBox* const ctboxNP = Gtk::manage(new Gtk::HBox()); + Gtk::Label* const labmNP = Gtk::manage(new Gtk::Label(M("TP_WAVELET_NPTYPE") + ":")); + ctboxNP->pack_start(*labmNP, Gtk::PACK_SHRINK, 1); + ctboxNP->pack_start(*localneiMethod); + edgsBoxshow->pack_start(*ctboxNP); + edgsBox->pack_start(*edgsBoxshow); + edgFrame->add(*edgsBox); + blurcontBox->pack_start(*edgFrame); + Gtk::Frame* const blurlevelFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_BLURLEVELFRA"))); + blurlevelFrame->set_label_align(0.025, 0.5); + blurlevelFrame->set_label_widget(*wavblur); + Gtk::VBox* const blurlevcontBox = Gtk::manage(new Gtk::VBox()); + blurlevcontBox->set_spacing(2); + blurlevcontBox->pack_start(*levelblur); + blurlevcontBox->pack_start(*sigmabl); + blurlevcontBox->pack_start(*chromablu); + blurlevcontBox->pack_start(*LocalcurveEditorwavlev, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + Gtk::HSeparator* const separatorblu = Gtk::manage(new Gtk::HSeparator()); + blurlevcontBox->pack_start(*separatorblu); + blurlevcontBox->pack_start(*residblur); + blurlevcontBox->pack_start(*blurlc); + blurlevelFrame->add(*blurlevcontBox); + blurcontBox->pack_start(*blurlevelFrame); + expcontrastpyr->add(*blurcontBox, false); + pack_start(*expcontrastpyr); + ToolParamBlock* const blurcontBox2 = Gtk::manage(new ToolParamBlock()); + Gtk::Frame* const contFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CONTFRA"))); + contFrame->set_label_align(0.025, 0.5); + Gtk::VBox* const contlevBox = Gtk::manage(new Gtk::VBox()); + contlevBox->set_spacing(2); + contFrame->set_label_widget(*wavcont); + contlevBox->pack_start(*sigma); + contlevBox->pack_start(*offset); + contlevBox->pack_start(*chromalev); + contlevBox->pack_start(*LocalcurveEditorwavcon, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + contFrame->add(*contlevBox); + blurcontBox2->pack_start(*contFrame); + Gtk::Frame* const compreFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_COMPREFRA"))); + compreFrame->set_label_align(0.025, 0.5); + Gtk::VBox* const compreBox = Gtk::manage(new Gtk::VBox()); + compreBox->set_spacing(2); + compreFrame->set_label_widget(*wavcompre); + compreBox->pack_start(*LocalcurveEditorwavcompre, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + compreBox->pack_start(*sigmadr); + compreBox->pack_start(*threswav); + compreBox->pack_start(*residcomp); + compreFrame->add(*compreBox); + blurcontBox2->pack_start(*compreFrame); + Gtk::Frame* const compFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_COMPFRA"))); + compFrame->set_label_align(0.025, 0.5); + Gtk::VBox* const compBox = Gtk::manage(new Gtk::VBox()); + compBox->set_spacing(2); + compFrame->set_label_widget(*wavcomp); + compBox->pack_start(*sigmadc); + compBox->pack_start(*deltad); + compBox->pack_start(*LocalcurveEditorwavcomp, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + // Gtk::HSeparator* const separatorcomp = Gtk::manage(new Gtk::HSeparator()); + // compBox->pack_start(*separatorcomp); + // compBox->pack_start(*fatres); + compFrame->add(*compBox); + blurcontBox2->pack_start(*compFrame); + expcontrastpyr2->add(*blurcontBox2, false); + pack_start(*expcontrastpyr2); + pack_start(*fftwlc); + ToolParamBlock* const masklcBox = Gtk::manage(new ToolParamBlock()); + masklcBox->pack_start(*showmasklcMethod, Gtk::PACK_SHRINK, 4); + masklcBox->pack_start(*enalcMask, Gtk::PACK_SHRINK, 0); + masklcBox->pack_start(*masklcCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + masklcBox->pack_start(*blendmasklc, Gtk::PACK_SHRINK, 0); + masklcBox->pack_start(*radmasklc, Gtk::PACK_SHRINK, 0); + masklcBox->pack_start(*chromasklc, Gtk::PACK_SHRINK, 0); + masklcBox->pack_start(*mask2lcCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + expmasklc->add(*masklcBox, false); + pack_start(*expmasklc, false, false); +} + +LocallabContrast::~LocallabContrast() +{ + delete LocalcurveEditorwav; + delete LocalcurveEditorwavedg; + delete LocalcurveEditorwavlev; + delete LocalcurveEditorwavcon; + delete LocalcurveEditorwavcompre; + delete LocalcurveEditorwavcomp; + delete masklcCurveEditorG; + delete mask2lcCurveEditorG; +} + +bool LocallabContrast::isMaskViewActive() +{ + return (showmasklcMethod->get_active_row_number() != 0); +} + +void LocallabContrast::resetMaskView() +{ + showmasklcMethodConn.block(true); + showmasklcMethod->set_active(0); + showmasklcMethodConn.block(false); +} + +void LocallabContrast::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + lcMask = showmasklcMethod->get_active_row_number(); +} + +void LocallabContrast::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_EXPCONTRAST_TOOLTIP")); + levelwav->set_tooltip_markup(M("TP_LOCALLAB_LEVELWAV_TOOLTIP")); + LocalcurveEditorwav->set_tooltip_markup(M("TP_LOCALLAB_LEVELLOCCONTRAST_TOOLTIP")); + wavgradl->set_tooltip_text(M("TP_LOCALLAB_WAVGRAD_TOOLTIP")); + clariFrame->set_tooltip_markup(M("TP_LOCALLAB_CLARI_TOOLTIP")); + clarisoft->set_tooltip_markup(M("TP_LOCALLAB_CLARISOFT_TOOLTIP")); + wavedg->set_tooltip_text(M("TP_LOCALLAB_WAVEEDG_TOOLTIP")); + wavblur->set_tooltip_text(M("TP_LOCALLAB_WAVBLUR_TOOLTIP")); + wavcont->set_tooltip_text(M("TP_LOCALLAB_WAVCONTF_TOOLTIP")); + wavcompre->set_tooltip_text(M("TP_LOCALLAB_WAVCOMPRE_TOOLTIP")); + wavcomp->set_tooltip_text(M("TP_LOCALLAB_WAVCOMP_TOOLTIP")); + chromablu->set_tooltip_text(M("TP_LOCALLAB_CHROMABLU_TOOLTIP")); + chromalev->set_tooltip_text(M("TP_LOCALLAB_CHROMABLU_TOOLTIP")); + fftwlc->set_tooltip_text(M("TP_LOCALLAB_LC_FFTW_TOOLTIP")); + expmasklc->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmasklcshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmasklcshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmasklcshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + Lmasklcshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + expcontrastpyr->set_tooltip_text(M("TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP")); + expcontrastpyr2->set_tooltip_text(M("TP_LOCALLAB_EXPCONTRASTPYR_TOOLTIP")); + blendmasklc->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2lcCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + levelwav->set_tooltip_text(""); + LocalcurveEditorwav->set_tooltip_markup(M("")); + wavgradl->set_tooltip_text(""); + clariFrame->set_tooltip_text(""); + clarisoft->set_tooltip_text(""); + wavedg->set_tooltip_text(""); + wavblur->set_tooltip_text(M("")); + wavcont->set_tooltip_text(M("")); + wavcompre->set_tooltip_text(M("")); + wavcomp->set_tooltip_text(M("")); + chromablu->set_tooltip_text(""); + chromalev->set_tooltip_text(""); + fftwlc->set_tooltip_text(""); + expmasklc->set_tooltip_text(""); + CCmasklcshape->setTooltip(""); + LLmasklcshape->setTooltip(""); + HHmasklcshape->setTooltip(""); + Lmasklcshape->setTooltip(""); + expcontrastpyr->set_tooltip_text(M("")); + expcontrastpyr2->set_tooltip_text(M("")); + blendmasklc->set_tooltip_text(M("")); + mask2lcCurveEditorG->set_tooltip_text(M("")); + } +} + +void LocallabContrast::setDefaultExpanderVisibility() +{ + expresidpyr->set_expanded(false); + expcontrastpyr->set_expanded(false); + expcontrastpyr2->set_expanded(false); + expmasklc->set_expanded(false); +} + +void LocallabContrast::disableListener() +{ + LocallabTool::disableListener(); + + localcontMethodConn.block(true); + origlcConn.block(true); + wavgradlConn.block(true); + wavedgConn.block(true); + localedgMethodConn.block(true); + waveshowConn.block(true); + localneiMethodConn.block(true); + wavblurConn.block(true); + blurlcConn.block(true); + wavcontConn.block(true); + wavcompreConn.block(true); + wavcompConn.block(true); + fftwlcConn.block(true); + showmasklcMethodConn.block(true); + enalcMaskConn.block(true); +} + +void LocallabContrast::enableListener() +{ + LocallabTool::enableListener(); + + localcontMethodConn.block(false); + origlcConn.block(false); + wavgradlConn.block(false); + wavedgConn.block(false); + localedgMethodConn.block(false); + waveshowConn.block(false); + localneiMethodConn.block(false); + wavblurConn.block(false); + blurlcConn.block(false); + wavcontConn.block(false); + wavcompreConn.block(false); + wavcompConn.block(false); + fftwlcConn.block(false); + showmasklcMethodConn.block(false); + enalcMaskConn.block(false); +} + +void LocallabContrast::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visicontrast); + exp->setEnabled(spot.expcontrast); + complexity->set_active(spot.complexcontrast); + + if (spot.localcontMethod == "loc") { + localcontMethod->set_active(0); + } else if (spot.localcontMethod == "wav") { + localcontMethod->set_active(1); + } + + fftwlc->set_active(spot.fftwlc); + // Update Local contrast GUI according to fftwlc button state + // Note: Contrary to the others, shall be called before setting 'lcradius' value + updateContrastGUI3(); + lcradius->setValue((double)spot.lcradius); + lcamount->setValue(spot.lcamount); + lcdarkness->setValue(spot.lcdarkness); + lclightness->setValue(spot.lclightness); + sigmalc->setValue(spot.sigmalc); + wavshape->setCurve(spot.locwavcurve); + levelwav->setValue((double)spot.levelwav); + csThreshold->setValue(spot.csthreshold); + residcont->setValue(spot.residcont); + residchro->setValue(spot.residchro); + residsha->setValue(spot.residsha); + residshathr->setValue(spot.residshathr); + residhi->setValue(spot.residhi); + residhithr->setValue(spot.residhithr); + sensilc->setValue((double)spot.sensilc); + clarilres->setValue(spot.clarilres); + claricres->setValue(spot.claricres); + clarisoft->setValue(spot.clarisoft); + origlc->set_active(spot.origlc); + wavgradl->set_active(spot.wavgradl); + sigmalc2->setValue(spot.sigmalc2); + strwav->setValue(spot.strwav); + angwav->setValue(spot.angwav); + wavedg->set_active(spot.wavedg); + strengthw->setValue(spot.strengthw); + sigmaed->setValue(spot.sigmaed); + wavshapeedg->setCurve(spot.locedgwavcurve); + gradw->setValue(spot.gradw); + waveshow->set_active(spot.waveshow); + radiusw->setValue(spot.radiusw); + detailw->setValue(spot.detailw); + + if (spot.localedgMethod == "fir") { + localedgMethod->set_active(0); + } else if (spot.localedgMethod == "sec") { + localedgMethod->set_active(1); + } else if (spot.localedgMethod == "thr") { + localedgMethod->set_active(2); + } + + tloww->setValue(spot.tloww); + thigw->setValue(spot.thigw); + edgw->setValue(spot.edgw); + basew->setValue(spot.basew); + + if (spot.localneiMethod == "none") { + localneiMethod->set_active(0); + } else if (spot.localneiMethod == "low") { + localneiMethod->set_active(1); + } else if (spot.localneiMethod == "high") { + localneiMethod->set_active(2); + } + + wavblur->set_active(spot.wavblur); + levelblur->setValue(spot.levelblur); + sigmabl->setValue(spot.sigmabl); + chromablu->setValue(spot.chromablu); + wavshapelev->setCurve(spot.loclevwavcurve); + residblur->setValue(spot.residblur); + blurlc->set_active(spot.blurlc); + wavcont->set_active(spot.wavcont); + sigma->setValue(spot.sigma); + offset->setValue(spot.offset); + chromalev->setValue(spot.chromalev); + wavshapecon->setCurve(spot.locconwavcurve); + wavcompre->set_active(spot.wavcompre); + wavshapecompre->setCurve(spot.loccomprewavcurve); + sigmadr->setValue(spot.sigmadr); + threswav->setValue(spot.threswav); + residcomp->setValue(spot.residcomp); + wavcomp->set_active(spot.wavcomp); + sigmadc->setValue(spot.sigmadc); + deltad->setValue(spot.deltad); + wavshapecomp->setCurve(spot.loccompwavcurve); + fatres->setValue(spot.fatres); + enalcMask->set_active(spot.enalcMask); + CCmasklcshape->setCurve(spot.CCmasklccurve); + LLmasklcshape->setCurve(spot.LLmasklccurve); + HHmasklcshape->setCurve(spot.HHmasklccurve); + blendmasklc->setValue((double)spot.blendmasklc); + radmasklc->setValue(spot.radmasklc); + chromasklc->setValue(spot.chromasklc); + Lmasklcshape->setCurve(spot.Lmasklccurve); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Update Local contrast GUI according to localcontMethod combobox value + updateContrastGUI1(); + + // Update Local contrast GUI according to waveshow button state + updateContrastGUI2(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabContrast::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expcontrast = exp->getEnabled(); + spot.visicontrast = exp->get_visible(); + spot.complexcontrast = complexity->get_active_row_number(); + + if (localcontMethod->get_active_row_number() == 0) { + spot.localcontMethod = "loc"; + } else if (localcontMethod->get_active_row_number() == 1) { + spot.localcontMethod = "wav"; + } + + spot.lcradius = lcradius->getIntValue(); + spot.lcamount = lcamount->getValue(); + spot.lcdarkness = lcdarkness->getValue(); + spot.lclightness = lclightness->getValue(); + spot.sigmalc = sigmalc->getValue(); + spot.locwavcurve = wavshape->getCurve(); + spot.levelwav = levelwav->getIntValue(); + spot.csthreshold = csThreshold->getValue(); + spot.residcont = residcont->getValue(); + spot.residchro = residchro->getValue(); + spot.residsha = residsha->getValue(); + spot.residshathr = residshathr->getValue(); + spot.residhi = residhi->getValue(); + spot.residhithr = residhithr->getValue(); + spot.sensilc = sensilc->getIntValue(); + spot.clarilres = clarilres->getValue(); + spot.claricres = claricres->getValue(); + spot.clarisoft = clarisoft->getValue(); + spot.origlc = origlc->get_active(); + spot.wavgradl = wavgradl->get_active(); + spot.sigmalc2 = sigmalc2->getValue(); + spot.strwav = strwav->getValue(); + spot.angwav = angwav->getValue(); + spot.wavedg = wavedg->get_active(); + spot.strengthw = strengthw->getValue(); + spot.sigmaed = sigmaed->getValue(); + spot.locedgwavcurve = wavshapeedg->getCurve(); + spot.gradw = gradw->getValue(); + spot.waveshow = waveshow->get_active(); + spot.radiusw = radiusw->getValue(); + spot.detailw = detailw->getValue(); + + if (localedgMethod->get_active_row_number() == 0) { + spot.localedgMethod = "fir"; + } else if (localedgMethod->get_active_row_number() == 1) { + spot.localedgMethod = "sec"; + } else if (localedgMethod->get_active_row_number() == 2) { + spot.localedgMethod = "thr"; + } + + spot.tloww = tloww->getValue(); + spot.thigw = thigw->getValue(); + spot.edgw = edgw->getValue(); + spot.basew = basew->getValue(); + + if (localneiMethod->get_active_row_number() == 0) { + spot.localneiMethod = "none"; + } else if (localneiMethod->get_active_row_number() == 1) { + spot.localneiMethod = "low"; + } else if (localneiMethod->get_active_row_number() == 2) { + spot.localneiMethod = "high"; + } + + spot.wavblur = wavblur->get_active(); + spot.levelblur = levelblur->getValue(); + spot.sigmabl = sigmabl->getValue(); + spot.chromablu = chromablu->getValue(); + spot.loclevwavcurve = wavshapelev->getCurve(); + spot.residblur = residblur->getValue(); + spot.blurlc = blurlc->get_active(); + spot.wavcont = wavcont->get_active(); + spot.sigma = sigma->getValue(); + spot.offset = offset->getValue(); + spot.chromalev = chromalev->getValue(); + spot.locconwavcurve = wavshapecon->getCurve(); + spot.wavcompre = wavcompre->get_active(); + spot.loccomprewavcurve = wavshapecompre->getCurve(); + spot.sigmadr = sigmadr->getValue(); + spot.threswav = threswav->getValue(); + spot.residcomp = residcomp->getValue(); + spot.wavcomp = wavcomp->get_active(); + spot.sigmadc = sigmadc->getValue(); + spot.deltad = deltad->getValue(); + spot.loccompwavcurve = wavshapecomp->getCurve(); + spot.fatres = fatres->getValue(); + spot.fftwlc = fftwlc->get_active(); + spot.enalcMask = enalcMask->get_active(); + spot.CCmasklccurve = CCmasklcshape->getCurve(); + spot.LLmasklccurve = LLmasklcshape->getCurve(); + spot.HHmasklccurve = HHmasklcshape->getCurve(); + spot.blendmasklc = blendmasklc->getIntValue(); + spot.radmasklc = radmasklc->getValue(); + spot.chromasklc = chromasklc->getValue(); + spot.Lmasklccurve = Lmasklcshape->getCurve(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabContrast::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster and threshold adjuster widgets + lcradius->setDefault((double)defSpot.lcradius); + lcamount->setDefault(defSpot.lcamount); + lcdarkness->setDefault(defSpot.lcdarkness); + lclightness->setDefault(defSpot.lclightness); + sigmalc->setDefault(defSpot.sigmalc); + levelwav->setDefault((double)defSpot.levelwav); + csThreshold->setDefault(defSpot.csthreshold); + residcont->setDefault(defSpot.residcont); + residchro->setDefault(defSpot.residchro); + residsha->setDefault(defSpot.residsha); + residshathr->setDefault(defSpot.residshathr); + residhi->setDefault(defSpot.residhi); + residhithr->setDefault(defSpot.residhithr); + sensilc->setDefault((double)defSpot.sensilc); + clarilres->setDefault(defSpot.clarilres); + claricres->setDefault(defSpot.claricres); + clarisoft->setDefault(defSpot.clarisoft); + sigmalc2->setDefault(defSpot.sigmalc2); + strwav->setDefault(defSpot.strwav); + angwav->setDefault(defSpot.angwav); + strengthw->setDefault(defSpot.strengthw); + sigmaed->setDefault(defSpot.sigmaed); + gradw->setDefault(defSpot.gradw); + radiusw->setDefault(defSpot.radiusw); + detailw->setDefault(defSpot.detailw); + tloww->setDefault(defSpot.tloww); + thigw->setDefault(defSpot.thigw); + edgw->setDefault(defSpot.edgw); + basew->setDefault(defSpot.basew); + levelblur->setDefault(defSpot.levelblur); + sigmabl->setDefault(defSpot.sigmabl); + chromablu->setDefault(defSpot.chromablu); + residblur->setDefault(defSpot.residblur); + sigma->setDefault(defSpot.sigma); + offset->setDefault(defSpot.offset); + chromalev->setDefault(defSpot.chromalev); + sigmadr->setDefault(defSpot.sigmadr); + threswav->setDefault(defSpot.threswav); + residcomp->setDefault(defSpot.residcomp); + sigmadc->setDefault(defSpot.sigmadc); + deltad->setDefault(defSpot.deltad); + fatres->setDefault(defSpot.fatres); + blendmasklc->setDefault((double)defSpot.blendmasklc); + radmasklc->setDefault(defSpot.radmasklc); + chromasklc->setDefault(defSpot.chromasklc); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabContrast::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == lcradius) { + if (listener) { + listener->panelChanged(Evlocallablcradius, + lcradius->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lcamount) { + if (listener) { + listener->panelChanged(Evlocallablcamount, + lcamount->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lcdarkness) { + if (listener) { + listener->panelChanged(Evlocallablcdarkness, + lcdarkness->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lclightness) { + if (listener) { + listener->panelChanged(Evlocallablclightness, + lclightness->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigmalc) { + if (listener) { + listener->panelChanged(Evlocallabsigmalc, + sigmalc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == levelwav) { + if (listener) { + listener->panelChanged(Evlocallablevelwav, + levelwav->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residcont) { + if (listener) { + listener->panelChanged(Evlocallabresidcont, + residcont->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residchro) { + if (listener) { + listener->panelChanged(Evlocallabresidchro, + residchro->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residsha) { + if (listener) { + listener->panelChanged(Evlocallabresidsha, + residsha->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residshathr) { + if (listener) { + listener->panelChanged(Evlocallabresidshathr, + residshathr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residhi) { + if (listener) { + listener->panelChanged(Evlocallabresidhi, + residhi->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residhithr) { + if (listener) { + listener->panelChanged(Evlocallabresidhithr, + residhithr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensilc) { + if (listener) { + listener->panelChanged(Evlocallabsensilc, + sensilc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == clarilres) { + if (listener) { + listener->panelChanged(Evlocallabclarilres, + clarilres->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == claricres) { + if (listener) { + listener->panelChanged(Evlocallabclaricres, + claricres->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == clarisoft) { + if (listener) { + listener->panelChanged(Evlocallabclarisoft, + clarisoft->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigmalc2) { + if (listener) { + listener->panelChanged(Evlocallabsigmalc2, + sigmalc2->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strwav) { + if (listener) { + listener->panelChanged(Evlocallabstrwav, + strwav->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == angwav) { + if (listener) { + listener->panelChanged(Evlocallabangwav, + angwav->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strengthw) { + if (listener) { + listener->panelChanged(Evlocallabstrengthw, + strengthw->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigmaed) { + if (listener) { + listener->panelChanged(Evlocallabsigmaed, + sigmaed->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gradw) { + if (listener) { + listener->panelChanged(Evlocallabgradw, + gradw->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radiusw) { + if (listener) { + listener->panelChanged(Evlocallabradiusw, + radiusw->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == detailw) { + if (listener) { + listener->panelChanged(Evlocallabdetailw, + detailw->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == tloww) { + if (listener) { + listener->panelChanged(Evlocallabtloww, + tloww->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == thigw) { + if (listener) { + listener->panelChanged(Evlocallabthigw, + thigw->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == edgw) { + if (listener) { + listener->panelChanged(Evlocallabedgw, + edgw->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == basew) { + if (listener) { + listener->panelChanged(Evlocallabbasew, + basew->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == levelblur) { + if (listener) { + listener->panelChanged(Evlocallablevelblur, + levelblur->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigmabl) { + if (listener) { + listener->panelChanged(Evlocallabsigmabl, + sigmabl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromablu) { + if (listener) { + listener->panelChanged(Evlocallabchromablu, + chromablu->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residblur) { + if (listener) { + listener->panelChanged(Evlocallabresidblur, + residblur->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigma) { + if (listener) { + listener->panelChanged(Evlocallabsigma, + sigma->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == offset) { + if (listener) { + listener->panelChanged(Evlocallaboffset, + offset->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromalev) { + if (listener) { + listener->panelChanged(Evlocallabchromalev, + chromalev->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigmadr) { + if (listener) { + listener->panelChanged(Evlocallabsigmadr, + sigmadr->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + + if (a == threswav) { + if (listener) { + listener->panelChanged(Evlocallabthreswav, + threswav->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == residcomp) { + if (listener) { + listener->panelChanged(Evlocallabresidcomp, + residcomp->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sigmadc) { + if (listener) { + listener->panelChanged(Evlocallabsigmadc, + sigmadc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == deltad) { + if (listener) { + listener->panelChanged(Evlocallabdeltad, + deltad->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == fatres) { + if (listener) { + listener->panelChanged(Evlocallabfatres, + fatres->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmasklc) { + if (listener) { + listener->panelChanged(Evlocallabblendmasklc, + blendmasklc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmasklc) { + if (listener) { + listener->panelChanged(Evlocallabradmasklc, + radmasklc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromasklc) { + if (listener) { + listener->panelChanged(Evlocallabchromasklc, + chromasklc->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallabcsThreshold, + csThreshold->getHistoryString() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabContrast::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == wavshape) { + if (listener) { + listener->panelChanged(EvlocallabwavCurve, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == wavshapeedg) { + if (listener) { + listener->panelChanged(EvlocallabwavCurveedg, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == wavshapelev) { + if (listener) { + listener->panelChanged(EvlocallabwavCurvelev, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == wavshapecon) { + if (listener) { + listener->panelChanged(EvlocallabwavCurvecon, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == wavshapecompre) { + if (listener) { + listener->panelChanged(EvlocallabwavCurvecompre, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == wavshapecomp) { + if (listener) { + listener->panelChanged(EvlocallabwavCurvecomp, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == CCmasklcshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmasklcshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmasklcshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmasklcshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmasklcshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmasklcshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmasklcshape) { + if (listener) { + listener->panelChanged(EvlocallabLmasklcshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenacontrast, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenacontrast, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + if (defSpot.localcontMethod == "loc") { + localcontMethod->set_active(0); + } else if (defSpot.localcontMethod == "wav") { + localcontMethod->set_active(1); + } + + fftwlc->set_active(defSpot.fftwlc); + + // Enable all listeners + enableListener(); + + // Update GUI based on converted widget parameters: + // - Update Local contrast GUI according to localcontMethod combobox value + updateContrastGUI1(); + // - Update Local contrast GUI according to fftwlc button state + updateContrastGUI3(); +} + +void LocallabContrast::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + localcontMethod->hide(); + fftwlc->hide(); + } else { + // Advanced widgets are shown in Expert mode + localcontMethod->show(); + fftwlc->show(); + } +} + +void LocallabContrast::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmasklcshape->updateLocallabBackground(normChromar); + LLmasklcshape->updateLocallabBackground(normLumar); + HHmasklcshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabContrast::localcontMethodChanged() +{ + // Update Local contrast GUI according to localcontMethod combobox value + updateContrastGUI1(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallablocalcontMethod, + localcontMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabContrast::origlcChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (origlc->get_active()) { + listener->panelChanged(Evlocallaboriglc, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallaboriglc, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::wavgradlChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (wavgradl->get_active()) { + listener->panelChanged(Evlocallabwavgradl, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwavgradl, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::wavedgChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (wavedg->get_active()) { + listener->panelChanged(Evlocallabwavedg, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwavedg, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::localedgMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallablocaledgMethod, + localedgMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabContrast::waveshowChanged() +{ + // Update Local contrast GUI according to waveshow button state + updateContrastGUI2(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (waveshow->get_active()) { + listener->panelChanged(Evlocallabwaveshow, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwaveshow, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::localneiMethodChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + listener->panelChanged(EvlocallablocalneiMethod, + localneiMethod->get_active_text() + " (" + escapeHtmlChars(spotName) + ")"); + } + } +} + +void LocallabContrast::wavblurChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (wavblur->get_active()) { + listener->panelChanged(Evlocallabwavblur, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwavblur, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::blurlcChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (blurlc->get_active()) { + listener->panelChanged(Evlocallabblurlc, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabblurlc, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::wavcontChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (wavcont->get_active()) { + listener->panelChanged(Evlocallabwavcont, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwavcont, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::wavcompreChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (wavcompre->get_active()) { + listener->panelChanged(Evlocallabwavcompre, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwavcompre, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::wavcompChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (wavcomp->get_active()) { + listener->panelChanged(Evlocallabwavcomp, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabwavcomp, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::fftwlcChanged() +{ + // Update Local contrast GUI according to fftwlc button state + updateContrastGUI3(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fftwlc->get_active()) { + listener->panelChanged(Evlocallabfftwlc, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabfftwlc, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::showmasklcMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabContrast::enalcMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enalcMask->get_active()) { + listener->panelChanged(EvLocallabEnalcMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnalcMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabContrast::updateContrastGUI1() +{ + // Update Local contrast GUI according to localcontMethod combobox value + if (localcontMethod->get_active_row_number() == 0) { + lcradius->show(); + lcamount->show(); + lcdarkness->show(); + lclightness->show(); + sigmalc->hide(); + LocalcurveEditorwav->hide(); + levelwav->hide(); + csThreshold->hide(); + expresidpyr->hide(); + clariFrame->hide(); + expcontrastpyr->hide(); + expcontrastpyr2->hide(); + fftwlc->show(); + } else if (localcontMethod->get_active_row_number() == 1) { + lcradius->hide(); + lcamount->hide(); + lcdarkness->hide(); + lclightness->hide(); + sigmalc->show(); + LocalcurveEditorwav->show(); + levelwav->show(); + csThreshold->show(); + expresidpyr->show(); + clariFrame->show(); + expcontrastpyr->show(); + expcontrastpyr2->show(); + fftwlc->hide(); + } +} + +void LocallabContrast::updateContrastGUI2() +{ + // Update Local contrast GUI according to waveshow button state + if (waveshow->get_active()) { + edgsBoxshow->show(); + } else { + edgsBoxshow->hide(); + } +} + +void LocallabContrast::updateContrastGUI3() +{ + // Update Local contrast GUI according to fftwlc button state + const double temp = lcradius->getValue(); + + if (fftwlc->get_active()) { + lcradius->setLimits(20, 1000, 1, 80); + } else { + lcradius->setLimits(20, 100, 1, 80); + } + + lcradius->setValue(temp); +} + +/* ==== LocallabCBDL ==== */ +LocallabCBDL::LocallabCBDL(): + LocallabTool(this, M("TP_LOCALLAB_CBDL_TOOLNAME"), M("TP_LOCALLAB_CBDL"), true), + + // CBDL specific widgets + multiplier([]() -> std::array + { + std::array res = {}; + + for (unsigned int i = 0; i < res.size(); ++i) { + Glib::ustring ss = Glib::ustring::format(i); + + if (i == 0) { + ss += Glib::ustring::compose(" (%1)", M("TP_DIRPYREQUALIZER_LUMAFINEST")); + } else if (i == 5) { + ss += Glib::ustring::compose(" (%1)", M("TP_DIRPYREQUALIZER_LUMACOARSEST")); + } + + res[i] = Gtk::manage(new Adjuster(std::move(ss), 0.0, 4.0, 0.01, 1.0)); + } + + return res; + } + ()), + chromacbdl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMACBDL"), 0., 1.5, 0.01, 0.))), + threshold(Gtk::manage(new Adjuster(M("TP_DIRPYREQUALIZER_THRESHOLD"), 0, 1., 0.01, 0.2))), + blurcbdl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLURCBDL"), 0., 100., 0.1, 0.))), + clarityml(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLARITYML"), 0.1, 100., 0.1, 0.1))), + contresid(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CONTRESID"), -100, 100, 1, 0))), + softradiuscb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 1000.0, 0.5, 0.))), + sensicb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSICB"), 0, 100, 1, 60))), + expmaskcb(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWCB")))), + showmaskcbMethod(Gtk::manage(new MyComboBoxText())), + enacbMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + maskcbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + CCmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "C(C)", nullptr, false, false))), + LLmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + HHmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "LC(H)", nullptr, false, true))), + blendmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), -10.0, 1000.0, 0.1, 0.))), + lapmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + mask2cbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskcbshape(static_cast(mask2cbCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + + lumacontrastMinusButton(Gtk::manage(new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMACONTRAST_MINUS")))), + lumaneutralButton(Gtk::manage(new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMANEUTRAL")))), + lumacontrastPlusButton(Gtk::manage(new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMACONTRAST_PLUS")))) +{ + const LocallabParams::LocallabSpot defSpot; + + // Parameter CBDL specific widgets + for (const auto adj : multiplier) { + adj->setAdjusterListener(this); + } + + chromacbdl->setAdjusterListener(this); + + threshold->setAdjusterListener(this); + + blurcbdl->setAdjusterListener(this); + + clarityml->setAdjusterListener(this); + + contresid->setAdjusterListener(this); + + softradiuscb->setLogScale(10, 0); + softradiuscb->setAdjusterListener(this); + + sensicb->setAdjusterListener(this); + + showmaskcbMethod->append(M("TP_LOCALLAB_SHOWMNONE")); + showmaskcbMethod->append(M("TP_LOCALLAB_SHOWMODIF")); + showmaskcbMethod->append(M("TP_LOCALLAB_SHOWMODIFMASK")); + showmaskcbMethod->append(M("TP_LOCALLAB_SHOWMASK")); + showmaskcbMethod->append(M("TP_LOCALLAB_SHOWREF")); + showmaskcbMethod->set_active(0); + showmaskcbMethod->set_tooltip_markup(M("TP_LOCALLAB_SHOWMASKCOL_TOOLTIP")); + showmaskcbMethodConn = showmaskcbMethod->signal_changed().connect(sigc::mem_fun(*this, &LocallabCBDL::showmaskcbMethodChanged)); + + enacbMaskConn = enacbMask->signal_toggled().connect(sigc::mem_fun(*this, &LocallabCBDL::enacbMaskChanged)); + + maskcbCurveEditorG->setCurveListener(this); + + CCmaskcbshape->setIdentityValue(0.); + CCmaskcbshape->setResetCurve(FlatCurveType(defSpot.CCmaskcbcurve.at(0)), defSpot.CCmaskcbcurve); + CCmaskcbshape->setBottomBarColorProvider(this, 1); + + LLmaskcbshape->setIdentityValue(0.); + LLmaskcbshape->setResetCurve(FlatCurveType(defSpot.LLmaskcbcurve.at(0)), defSpot.LLmaskcbcurve); + LLmaskcbshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + HHmaskcbshape->setIdentityValue(0.); + HHmaskcbshape->setResetCurve(FlatCurveType(defSpot.HHmaskcbcurve.at(0)), defSpot.HHmaskcbcurve); + HHmaskcbshape->setCurveColorProvider(this, 2); + HHmaskcbshape->setBottomBarColorProvider(this, 2); + + maskcbCurveEditorG->curveListComplete(); + + blendmaskcb->setAdjusterListener(this); + + radmaskcb->setLogScale(10, -10); + radmaskcb->setAdjusterListener(this); + + lapmaskcb->setAdjusterListener(this); + + chromaskcb->setAdjusterListener(this); + + gammaskcb->setAdjusterListener(this); + + slomaskcb->setAdjusterListener(this); + + mask2cbCurveEditorG->setCurveListener(this); + + Lmaskcbshape->setResetCurve(DiagonalCurveType(defSpot.Lmaskcbcurve.at(0)), defSpot.Lmaskcbcurve); + Lmaskcbshape->setBottomBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + Lmaskcbshape->setLeftBarBgGradient({{0., 0., 0., 0.}, {1., 1., 1., 1.}}); + + mask2cbCurveEditorG->curveListComplete(); + + lumacontrastMinusPressedConn = lumacontrastMinusButton->signal_pressed().connect(sigc::mem_fun(*this, &LocallabCBDL::lumacontrastMinusPressed)); + + lumaneutralPressedConn = lumaneutralButton->signal_pressed().connect(sigc::mem_fun(*this, &LocallabCBDL::lumaneutralPressed)); + + lumacontrastPlusPressedConn = lumacontrastPlusButton->signal_pressed().connect(sigc::mem_fun(*this, &LocallabCBDL::lumacontrastPlusPressed)); + + // Add CBDL specific widgets to GUI + Gtk::HBox* buttonBox = Gtk::manage(new Gtk::HBox(true, 10)); + buttonBox->pack_start(*lumacontrastMinusButton); + buttonBox->pack_start(*lumaneutralButton); + buttonBox->pack_start(*lumacontrastPlusButton); + pack_start(*buttonBox); + + for (const auto adj : multiplier) { + pack_start(*adj); + } + + Gtk::HSeparator* const separator = Gtk::manage(new Gtk::HSeparator()); + pack_start(*separator, Gtk::PACK_SHRINK, 2); + pack_start(*chromacbdl); + pack_start(*threshold); + // pack_start(*blurcbdl); + Gtk::Frame* const residFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_RESID"))); + residFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const residBox = Gtk::manage(new ToolParamBlock()); + residBox->pack_start(*clarityml); + residBox->pack_start(*contresid); + residFrame->add(*residBox); + pack_start(*residFrame); + pack_start(*softradiuscb); + pack_start(*sensicb); + ToolParamBlock* const maskcbBox = Gtk::manage(new ToolParamBlock()); + maskcbBox->pack_start(*showmaskcbMethod, Gtk::PACK_SHRINK, 4); + maskcbBox->pack_start(*enacbMask, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*maskcbCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + maskcbBox->pack_start(*blendmaskcb, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*radmaskcb, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*lapmaskcb, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*chromaskcb, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*gammaskcb, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*slomaskcb, Gtk::PACK_SHRINK, 0); + maskcbBox->pack_start(*mask2cbCurveEditorG, Gtk::PACK_SHRINK, 4); // Padding is mandatory to correct behavior of curve editor + expmaskcb->add(*maskcbBox, false); + pack_start(*expmaskcb, false, false); +} + +LocallabCBDL::~LocallabCBDL() +{ + delete maskcbCurveEditorG; + delete mask2cbCurveEditorG; +} + +bool LocallabCBDL::isMaskViewActive() +{ + return (showmaskcbMethod->get_active_row_number() != 0); +} + + +void LocallabCBDL::resetMaskView() +{ + showmaskcbMethodConn.block(true); + showmaskcbMethod->set_active(0); + showmaskcbMethodConn.block(false); +} + +void LocallabCBDL::getMaskView(int &colorMask, int &colorMaskinv, int &expMask, int &expMaskinv, int &shMask, int &shMaskinv, int &vibMask, int &softMask, int &blMask, int &tmMask, int &retiMask, int &sharMask, int &lcMask, int &cbMask) +{ + cbMask = showmaskcbMethod->get_active_row_number(); +} + +void LocallabCBDL::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_EXPCBDL_TOOLTIP")); + for (const auto adj : multiplier) { + adj->set_tooltip_text(M("TP_LOCALLAB_CBDL_ADJ_TOOLTIP")); + } + threshold->set_tooltip_text(M("TP_LOCALLAB_CBDL_THRES_TOOLTIP")); + chromacbdl->set_tooltip_text(M("TP_LOCALLAB_CHROMACB_TOOLTIP")); + clarityml->set_tooltip_text(M("TP_LOCALLAB_CBDLCLARI_TOOLTIP")); + sensicb->set_tooltip_text(M("TP_LOCALLAB_SENSIH_TOOLTIP")); + expmaskcb->set_tooltip_markup(M("TP_LOCALLAB_MASK_TOOLTIP")); + CCmaskcbshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + LLmaskcbshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + HHmaskcbshape->setTooltip(M("TP_LOCALLAB_CURVEEDITOR_CC_TOOLTIP")); + radmaskcb->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + lapmaskcb->set_tooltip_text(M("TP_LOCALLAB_LAPRAD_TOOLTIP")); + Lmaskcbshape->setTooltip(M("TP_LOCALLAB_LMASK_LL_TOOLTIP")); + blendmaskcb->set_tooltip_text(M("TP_LOCALLAB_BLENDMASK_TOOLTIP")); + mask2cbCurveEditorG->set_tooltip_text(M("TP_LOCALLAB_CONTRASTCURVMASK_TOOLTIP")); + } else { + exp->set_tooltip_text(""); + for (const auto adj : multiplier) { + adj->set_tooltip_text(M("")); + } + threshold->set_tooltip_text(M("")); + chromacbdl->set_tooltip_text(""); + clarityml->set_tooltip_text(M("")); + sensicb->set_tooltip_text(""); + expmaskcb->set_tooltip_text(""); + CCmaskcbshape->setTooltip(""); + LLmaskcbshape->setTooltip(""); + HHmaskcbshape->setTooltip(""); + radmaskcb->set_tooltip_text(""); + lapmaskcb->set_tooltip_text(""); + Lmaskcbshape->setTooltip(""); + blendmaskcb->set_tooltip_text(M("")); + mask2cbCurveEditorG->set_tooltip_text(M("")); + } +} + +void LocallabCBDL::setDefaultExpanderVisibility() +{ + expmaskcb->set_expanded(false); +} + +void LocallabCBDL::disableListener() +{ + LocallabTool::disableListener(); + + showmaskcbMethodConn.block(true); + enacbMaskConn.block(true); + + lumacontrastMinusPressedConn.block(true); + lumaneutralPressedConn.block(true); + lumacontrastPlusPressedConn.block(true); +} + +void LocallabCBDL::enableListener() +{ + LocallabTool::enableListener(); + + showmaskcbMethodConn.block(false); + enacbMaskConn.block(false); + + lumacontrastMinusPressedConn.block(false); + lumaneutralPressedConn.block(false); + lumacontrastPlusPressedConn.block(false); +} + +void LocallabCBDL::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visicbdl); + exp->setEnabled(spot.expcbdl); + complexity->set_active(spot.complexcbdl); + + for (int i = 0; i < 6; i++) { + multiplier[i]->setValue(spot.mult[i]); + } + + chromacbdl->setValue(spot.chromacbdl); + threshold->setValue(spot.threshold); + blurcbdl->setValue(spot.blurcbdl); + clarityml->setValue(spot.clarityml); + contresid->setValue((double)spot.contresid); + softradiuscb->setValue(spot.softradiuscb); + sensicb->setValue((double)spot.sensicb); + enacbMask->set_active(spot.enacbMask); + CCmaskcbshape->setCurve(spot.CCmaskcbcurve); + LLmaskcbshape->setCurve(spot.LLmaskcbcurve); + HHmaskcbshape->setCurve(spot.HHmaskcbcurve); + blendmaskcb->setValue((double)spot.blendmaskcb); + radmaskcb->setValue(spot.radmaskcb); + lapmaskcb->setValue(spot.lapmaskcb); + chromaskcb->setValue(spot.chromaskcb); + gammaskcb->setValue(spot.gammaskcb); + slomaskcb->setValue(spot.slomaskcb); + Lmaskcbshape->setCurve(spot.Lmaskcbcurve); + } + + // Enable all listeners + enableListener(); + + // Update GUI according to complexity mode + updateGUIToMode(static_cast(complexity->get_active_row_number())); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabCBDL::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.expcbdl = exp->getEnabled(); + spot.visicbdl = exp->get_visible(); + spot.complexcbdl = complexity->get_active_row_number(); + + for (int i = 0; i < 6; i++) { + spot.mult[i] = multiplier[i]->getValue(); + } + + spot.chromacbdl = chromacbdl->getValue(); + spot.threshold = threshold->getValue(); + spot.blurcbdl = blurcbdl->getValue(); + spot.clarityml = clarityml->getValue(); + spot.contresid = contresid->getIntValue(); + spot.softradiuscb = softradiuscb->getValue(); + spot.sensicb = sensicb->getIntValue(); + spot.enacbMask = enacbMask->get_active(); + spot.LLmaskcbcurve = LLmaskcbshape->getCurve(); + spot.CCmaskcbcurve = CCmaskcbshape->getCurve(); + spot.HHmaskcbcurve = HHmaskcbshape->getCurve(); + spot.blendmaskcb = blendmaskcb->getIntValue(); + spot.radmaskcb = radmaskcb->getValue(); + spot.lapmaskcb = lapmaskcb->getValue(); + spot.chromaskcb = chromaskcb->getValue(); + spot.gammaskcb = gammaskcb->getValue(); + spot.slomaskcb = slomaskcb->getValue(); + spot.Lmaskcbcurve = Lmaskcbshape->getCurve(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabCBDL::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default values for adjuster widgets + for (int i = 0; i < 6; i++) { + multiplier[i]->setDefault(defSpot.mult[i]); + } + + chromacbdl->setDefault(defSpot.chromacbdl); + threshold->setDefault(defSpot.threshold); + blurcbdl->setDefault(defSpot.blurcbdl); + clarityml->setDefault(defSpot.clarityml); + contresid->setDefault((double)defSpot.contresid); + softradiuscb->setDefault(defSpot.softradiuscb); + sensicb->setDefault((double)defSpot.sensicb); + blendmaskcb->setDefault((double)defSpot.blendmaskcb); + radmaskcb->setDefault(defSpot.radmaskcb); + lapmaskcb->setDefault(defSpot.lapmaskcb); + chromaskcb->setDefault(defSpot.chromaskcb); + gammaskcb->setDefault(defSpot.gammaskcb); + slomaskcb->setDefault(defSpot.slomaskcb); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabCBDL::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == multiplier[0] || a == multiplier[1] || a == multiplier[2] || a == multiplier[3] || a == multiplier[4] || a == multiplier[5]) { + if (listener) { + listener->panelChanged(EvlocallabEqualizer, + Glib::ustring::compose("%1, %2, %3, %4, %5, %6", + Glib::ustring::format(std::fixed, std::setprecision(2), multiplier[0]->getValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multiplier[1]->getValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multiplier[2]->getValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multiplier[3]->getValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multiplier[4]->getValue()), + Glib::ustring::format(std::fixed, std::setprecision(2), multiplier[5]->getValue())) + + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromacbdl) { + if (listener) { + listener->panelChanged(Evlocallabchromacbdl, + chromacbdl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == threshold) { + if (listener) { + listener->panelChanged(EvlocallabThresho, + threshold->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blurcbdl) { + if (listener) { + listener->panelChanged(EvLocallabblurcbdl, + blurcbdl->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == clarityml) { + if (listener) { + listener->panelChanged(EvLocallabclarityml, + clarityml->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == contresid) { + if (listener) { + listener->panelChanged(EvLocallabcontresid, + contresid->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == softradiuscb) { + if (listener) { + listener->panelChanged(Evlocallabsoftradiuscb, + softradiuscb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensicb) { + if (listener) { + listener->panelChanged(Evlocallabsensicb, + sensicb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == blendmaskcb) { + if (listener) { + listener->panelChanged(Evlocallabblendmaskcb, + blendmaskcb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == radmaskcb) { + if (listener) { + listener->panelChanged(Evlocallabradmaskcb, + radmaskcb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == lapmaskcb) { + if (listener) { + listener->panelChanged(Evlocallablapmaskcb, + lapmaskcb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == chromaskcb) { + if (listener) { + listener->panelChanged(Evlocallabchromaskcb, + chromaskcb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == gammaskcb) { + if (listener) { + listener->panelChanged(Evlocallabgammaskcb, + gammaskcb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == slomaskcb) { + if (listener) { + listener->panelChanged(Evlocallabslomaskcb, + slomaskcb->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabCBDL::curveChanged(CurveEditor* ce) +{ + if (isLocActivated && exp->getEnabled()) { + if (ce == CCmaskcbshape) { + if (listener) { + listener->panelChanged(EvlocallabCCmaskcbshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == LLmaskcbshape) { + if (listener) { + listener->panelChanged(EvlocallabLLmaskcbshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == HHmaskcbshape) { + if (listener) { + listener->panelChanged(EvlocallabHHmaskcbshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (ce == Lmaskcbshape) { + if (listener) { + listener->panelChanged(EvlocallabLmaskcbshape, + M("HISTORY_CUSTOMCURVE") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabCBDL::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenacbdl, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenacbdl, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabCBDL::convertParamToNormal() +{ + const LocallabParams::LocallabSpot defSpot; + + // Disable all listeners + disableListener(); + + // Set hidden GUI widgets in Normal mode to default spot values + lapmaskcb->setValue(defSpot.lapmaskcb); + + // Enable all listeners + enableListener(); +} + +void LocallabCBDL::updateGUIToMode(const modeType new_type) +{ + if (new_type == Normal) { + // Advanced widgets are hidden in Normal mode + lapmaskcb->hide(); + } else { + // Advanced widgets are shown in Expert mode + lapmaskcb->show(); + } +} + +void LocallabCBDL::updateMaskBackground(const double normChromar, const double normLumar, const double normHuer) +{ + idle_register.add( + [this, normHuer, normLumar, normChromar]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update mask background + CCmaskcbshape->updateLocallabBackground(normChromar); + LLmaskcbshape->updateLocallabBackground(normLumar); + HHmaskcbshape->updateLocallabBackground(normHuer); + + return false; + } + ); +} + +void LocallabCBDL::showmaskcbMethodChanged() +{ + // If mask preview is activated, deactivate all other tool mask preview + if (locToolListener) { + locToolListener->resetOtherMaskView(this); + } + + if (listener) { + listener->panelChanged(EvlocallabshowmaskMethod, ""); + } +} + +void LocallabCBDL::enacbMaskChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (enacbMask->get_active()) { + listener->panelChanged(EvLocallabEnacbMask, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabEnacbMask, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabCBDL::lumacontrastMinusPressed() +{ + for (int i = 0; i < 6; i++) { + float inc = - (5 - i); + multiplier[i]->setValue(multiplier[i]->getValue() + 0.01f * inc); + } + + // Raise event (only for first multiplier because associated event concerns all multipliers) + adjusterChanged(multiplier[0], multiplier[0]->getValue()); // Value isn't used +} + +void LocallabCBDL::lumaneutralPressed() +{ + for (int i = 0; i < 6; i++) { + multiplier[i]->setValue(1.0); + } + + // Raise event (only for first multiplier because associated event concerns all multipliers) + adjusterChanged(multiplier[0], multiplier[0]->getValue()); // Value isn't used +} + +void LocallabCBDL::lumacontrastPlusPressed() +{ + for (int i = 0; i < 6; i++) { + float inc = (5 - i); + multiplier[i]->setValue(multiplier[i]->getValue() + 0.01f * inc); + } + + // Raise event (only for first multiplier because associated event concerns all multipliers) + adjusterChanged(multiplier[0], multiplier[0]->getValue()); // Value isn't used +} + +/* ==== LocallabLog ==== */ +LocallabLog::LocallabLog(): + LocallabTool(this, M("TP_LOCALLAB_LOG_TOOLNAME"), M("TP_LOCALLAB_LOG"), false, false), + + // Log encoding specific widgets + autocompute(Gtk::manage(new Gtk::ToggleButton(M("TP_LOCALLAB_LOGAUTO")))), + logPFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LOGPFRA")))), + blackEv(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLACK_EV"), -16.0, 0.0, 0.1, -5.0))), + whiteEv(Gtk::manage(new Adjuster(M("TP_LOCALLAB_WHITE_EV"), 0.0, 32.0, 0.1, 10.0))), + fullimage(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FULLIMAGE")))), + Autogray(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_AUTOGRAY")))), + sourceGray(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOURCE_GRAY"), 1.0, 100.0, 0.1, 10.0))), + targetGray(Gtk::manage(new Adjuster(M("TP_LOCALLAB_TARGET_GRAY"), 5.0, 80.0, 0.1, 18.0))), + detail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DETAIL"), 0., 1., 0.01, 0.6))), + baselog(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BASELOG"), 1.3, 8., 0.05, 2., Gtk::manage(new RTImage("circle-black-small.png")), Gtk::manage(new RTImage("circle-white-small.png"))))), + sensilog(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSILOG"), 0, 100, 1, 60))), + strlog(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -2.0, 2.0, 0.05, 0.))), + anglog(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180, 180, 0.1, 0.))) +{ + // Parameter Log encoding specific widgets + autoconn = autocompute->signal_toggled().connect(sigc::mem_fun(*this, &LocallabLog::autocomputeToggled)); + + blackEv->setLogScale(2, -8); + blackEv->setAdjusterListener(this); + + whiteEv->setLogScale(16, 0); + whiteEv->setAdjusterListener(this); + + fullimageConn = fullimage->signal_toggled().connect(sigc::mem_fun(*this, &LocallabLog::fullimageChanged)); + + AutograyConn = Autogray->signal_toggled().connect(sigc::mem_fun(*this, &LocallabLog::AutograyChanged)); + + sourceGray->setAdjusterListener(this); + + targetGray->setAdjusterListener(this); + + detail->setAdjusterListener(this); + + baselog->setAdjusterListener(this); + + sensilog->setAdjusterListener(this); + + strlog->setAdjusterListener(this); + + anglog->setAdjusterListener(this); + + // Add Log encoding specific widgets to GUI + logPFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const logPBox = Gtk::manage(new ToolParamBlock()); + logPBox->pack_start(*autocompute); + logPBox->pack_start(*blackEv); + logPBox->pack_start(*whiteEv); + logPBox->pack_start(*fullimage); + logPFrame->add(*logPBox); + pack_start(*logPFrame); + Gtk::Frame* const logFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_LOGFRA"))); + logFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const logFBox = Gtk::manage(new ToolParamBlock()); + logFBox->pack_start(*Autogray); + logFBox->pack_start(*sourceGray); + logFrame->add(*logFBox); + pack_start(*logFrame); + pack_start(*targetGray); + pack_start(*detail); + pack_start(*baselog); + pack_start(*sensilog); + Gtk::Frame* const gradlogFrame = Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRADLOGFRA"))); + gradlogFrame->set_label_align(0.025, 0.5); + ToolParamBlock* const gradlogBox = Gtk::manage(new ToolParamBlock()); + gradlogBox->pack_start(*strlog); + gradlogBox->pack_start(*anglog); + gradlogFrame->add(*gradlogBox); + pack_start(*gradlogFrame); +} + +void LocallabLog::updateAdviceTooltips(const bool showTooltips) +{ + if (showTooltips) { + exp->set_tooltip_text(M("TP_LOCALLAB_LOGENCOD_TOOLTIP")); + logPFrame->set_tooltip_text(M("TP_LOCALLAB_LOGFRAME_TOOLTIP")); + autocompute->set_tooltip_text(M("TP_LOCALLAB_LOGAUTO_TOOLTIP")); + blackEv->set_tooltip_text(M("TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP")); + whiteEv->set_tooltip_text(M("TP_LOCALLAB_LOGBLACKWHEV_TOOLTIP")); + // Autogray->set_tooltip_text(M("TP_LOCALLAB_LOGAUTOGREY_TOOLTIP")); + Autogray->set_tooltip_text(M("")); + sourceGray->set_tooltip_text(M("TP_LOCALLAB_LOGSRCGREY_TOOLTIP")); + targetGray->set_tooltip_text(M("TP_LOCALLAB_LOGTARGGREY_TOOLTIP")); + // detail->set_tooltip_text(M("TP_LOCALLAB_LOGDET_TOOLTIP")); + detail->set_tooltip_text(M("")); + baselog->set_tooltip_text(M("TP_LOCALLAB_LOGBASE_TOOLTIP")); + strlog->set_tooltip_text(M("TP_LOCALLAB_GRADGEN_TOOLTIP")); + anglog->set_tooltip_text(M("TP_LOCALLAB_GRADANG_TOOLTIP")); + } else { + exp->set_tooltip_text(M("")); + logPFrame->set_tooltip_text(""); + autocompute->set_tooltip_text(M("")); + blackEv->set_tooltip_text(M("")); + whiteEv->set_tooltip_text(M("")); + Autogray->set_tooltip_text(M("")); + sourceGray->set_tooltip_text(M("")); + targetGray->set_tooltip_text(M("")); + detail->set_tooltip_text(M("")); + baselog->set_tooltip_text(M("")); + strlog->set_tooltip_text(M("")); + anglog->set_tooltip_text(M("")); + } +} + +void LocallabLog::disableListener() +{ + LocallabTool::disableListener(); + + autoconn.block(true); + fullimageConn.block(true); + AutograyConn.block(true); +} + +void LocallabLog::enableListener() +{ + LocallabTool::enableListener(); + + autoconn.block(false); + fullimageConn.block(false); + AutograyConn.block(false); +} + +void LocallabLog::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ + // Disable all listeners + disableListener(); + + // Update GUI to selected spot value + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + const LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spotName = spot.name; // Update spot name according to selected spot + + exp->set_visible(spot.visilog); + exp->setEnabled(spot.explog); + + autocompute->set_active(spot.autocompute); + blackEv->setValue(spot.blackEv); + whiteEv->setValue(spot.whiteEv); + fullimage->set_active(spot.fullimage); + Autogray->set_active(spot.Autogray); + sourceGray->setValue(spot.sourceGray); + targetGray->setValue(spot.targetGray); + detail->setValue(spot.detail); + baselog->setValue(spot.baselog); + sensilog->setValue((double)spot.sensilog); + strlog->setValue(spot.strlog); + anglog->setValue(spot.anglog); + } + + // Enable all listeners + enableListener(); + + // Update Log Encoding GUI according to autocompute button state + updateLogGUI(); + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabLog::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ + const int index = pp->locallab.selspot; + + if (index < (int)pp->locallab.spots.size()) { + LocallabParams::LocallabSpot& spot = pp->locallab.spots.at(index); + + spot.explog = exp->getEnabled(); + spot.visilog = exp->get_visible(); + + spot.autocompute = autocompute->get_active(); + spot.blackEv = blackEv->getValue(); + spot.whiteEv = whiteEv->getValue(); + spot.fullimage = fullimage->get_active(); + spot.Autogray = Autogray->get_active(); + spot.sourceGray = sourceGray->getValue(); + spot.targetGray = targetGray->getValue(); + spot.detail = detail->getValue(); + spot.baselog = baselog->getValue(); + spot.sensilog = sensilog->getIntValue(); + spot.strlog = strlog->getValue(); + spot.anglog = anglog->getValue(); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabLog::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ + const int index = defParams->locallab.selspot; + + if (index < (int)defParams->locallab.spots.size()) { + const LocallabParams::LocallabSpot& defSpot = defParams->locallab.spots.at(index); + + // Set default value for adjuster widgets + blackEv->setDefault(defSpot.blackEv); + whiteEv->setDefault(defSpot.whiteEv); + sourceGray->setDefault(defSpot.sourceGray); + targetGray->setDefault(defSpot.targetGray); + detail->setDefault(defSpot.detail); + baselog->setDefault(defSpot.baselog); + sensilog->setDefault((double)defSpot.sensilog); + strlog->setDefault(defSpot.strlog); + anglog->setDefault(defSpot.anglog); + } + + // Note: No need to manage pedited as batch mode is deactivated for Locallab +} + +void LocallabLog::adjusterChanged(Adjuster* a, double newval) +{ + if (isLocActivated && exp->getEnabled()) { + if (a == blackEv) { + if (listener) { + listener->panelChanged(EvlocallabblackEv, + blackEv->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == whiteEv) { + if (listener) { + listener->panelChanged(EvlocallabwhiteEv, + whiteEv->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sourceGray) { + if (listener) { + listener->panelChanged(EvlocallabsourceGray, + sourceGray->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == targetGray) { + if (listener) { + listener->panelChanged(EvlocallabtargetGray, + targetGray->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == detail) { + if (listener) { + listener->panelChanged(Evlocallabdetail, + detail->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == baselog) { + if (listener) { + listener->panelChanged(Evlocallabbaselog, + baselog->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == sensilog) { + if (listener) { + listener->panelChanged(Evlocallabsensilog, + sensilog->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == strlog) { + if (listener) { + listener->panelChanged(Evlocallabstrlog, + strlog->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + + if (a == anglog) { + if (listener) { + listener->panelChanged(Evlocallabanglog, + anglog->getTextValue() + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabLog::updateAutocompute(const float blackev, const float whiteev, const float sourceg, const float targetg) +{ + idle_register.add( + [this, blackev, whiteev, sourceg, targetg]() -> bool { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + + // Update adjuster values according to autocomputed ones + disableListener(); + + blackEv->setValue(blackev); + whiteEv->setValue(whiteev); + sourceGray->setValue(sourceg); + targetGray->setValue(targetg); + + enableListener(); + + return false; + } + ); +} + +void LocallabLog::enabledChanged() +{ + if (isLocActivated) { + if (listener) { + if (exp->getEnabled()) { + listener->panelChanged(EvLocenalog, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocenalog, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabLog::autocomputeToggled() +{ + // Update Log Encoding GUI according to autocompute button state + updateLogGUI(); + + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (autocompute->get_active()) { + listener->panelChanged(EvLocallabAuto, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvLocallabAuto, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabLog::fullimageChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (fullimage->get_active()) { + listener->panelChanged(Evlocallabfullimage, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(Evlocallabfullimage, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabLog::AutograyChanged() +{ + if (isLocActivated && exp->getEnabled()) { + if (listener) { + if (Autogray->get_active()) { + listener->panelChanged(EvlocallabAutogray, + M("GENERAL_ENABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } else { + listener->panelChanged(EvlocallabAutogray, + M("GENERAL_DISABLED") + " (" + escapeHtmlChars(spotName) + ")"); + } + } + } +} + +void LocallabLog::updateLogGUI() +{ + if (autocompute->get_active()) { + blackEv->set_sensitive(false); + whiteEv->set_sensitive(false); + sourceGray->set_sensitive(false); + // targetGray->set_sensitive(true); + } else { + blackEv->set_sensitive(true); + whiteEv->set_sensitive(true); + sourceGray->set_sensitive(true); + // targetGray->set_sensitive(true); + } +} diff --git a/rtgui/main.cc b/rtgui/main.cc index f669bcf4a..7bb4afdc9 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -121,6 +121,7 @@ static void myGdkLockLeave() * -1 if there is an error in parameters * -2 if an error occurred during processing * -3 if at least one required procparam file was not found */ +//int processLineParams ( int argc, char **argv ); int processLineParams ( int argc, char **argv ) { int ret = 1; @@ -255,6 +256,7 @@ RTWindow *create_rt_window() //gdk_threads_enter (); RTWindow *rtWindow = new RTWindow(); + rtWindow->setWindowSize(); // Need to be called after RTWindow creation to work with all OS Windows Manager return rtWindow; } @@ -439,7 +441,7 @@ int main (int argc, char **argv) if (argc > 1) { if (!remote && !Glib::file_test (argv1, Glib::FILE_TEST_EXISTS ) && !Glib::file_test (argv1, Glib::FILE_TEST_IS_DIR)) { - bool stdoutRedirecttoConsole = (GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)) == 0x0000); + const bool stdoutRedirecttoConsole = (GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)) == 0x0000); // open console, if stdout is invalid if (stdoutRedirecttoConsole) { // check if parameter -w was passed. @@ -471,10 +473,9 @@ int main (int argc, char **argv) cursorInfo.bVisible = false; SetConsoleCursorInfo ( GetStdHandle ( STD_OUTPUT_HANDLE ), &cursorInfo ); - if (stdoutRedirecttoConsole) { // if stdout is Redirect to console, we also redirect stderr to console - freopen ( "CON", "w", stdout ) ; - freopen ( "CON", "w", stderr ) ; - } + // we also redirect stderr to console + freopen ( "CON", "w", stdout ) ; + freopen ( "CON", "w", stderr ) ; freopen ( "CON", "r", stdin ) ; diff --git a/rtgui/md5helper.h b/rtgui/md5helper.h new file mode 100644 index 000000000..17ee70f3a --- /dev/null +++ b/rtgui/md5helper.h @@ -0,0 +1,69 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-201 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 . + */ + +#include +#include + +#include +#include + +#ifdef WIN32 +#include +#endif + +namespace { +std::string getMD5 (const Glib::ustring& fname) +{ + + auto file = Gio::File::create_for_path (fname); + + if (file && file->query_exists ()) { + +#ifdef WIN32 + + std::unique_ptr wfname (reinterpret_cast (g_utf8_to_utf16 (fname.c_str (), -1, NULL, NULL, NULL)), g_free); + + WIN32_FILE_ATTRIBUTE_DATA fileAttr; + + if (GetFileAttributesExW (wfname.get (), GetFileExInfoStandard, &fileAttr)) { + // We use name, size and creation time to identify a file. + const auto identifier = Glib::ustring::compose ("%1-%2-%3-%4", fileAttr.nFileSizeLow, fileAttr.ftCreationTime.dwHighDateTime, fileAttr.ftCreationTime.dwLowDateTime, fname); + return Glib::Checksum::compute_checksum (Glib::Checksum::CHECKSUM_MD5, identifier); + } + +#else + + try { + + if (auto info = file->query_info ()) { + // We only use name and size to identify a file. + const auto identifier = Glib::ustring::compose ("%1%2", fname, info->get_size ()); + return Glib::Checksum::compute_checksum (Glib::Checksum::CHECKSUM_MD5, identifier); + } + + } catch (Gio::Error&) {} + +#endif + + } + + return {}; +} + +} diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc index da993987c..11d89ebd8 100644 --- a/rtgui/myflatcurve.cc +++ b/rtgui/myflatcurve.cc @@ -46,7 +46,8 @@ MyFlatCurve::MyFlatCurve () : deletedPointX(0.0), leftTanHandle({0.0, 0.0}), rightTanHandle({0.0, 0.0}), - draggingElement(false) + draggingElement(false), + locallabRef(0.0) { lit_point = -1; @@ -169,6 +170,20 @@ void MyFlatCurve::draw () cr->set_line_width (1.0 * s); + // Draw Locallab reference value in the background + if (locallabRef > 0.0) { + cr->set_line_width(1.0); + cr->move_to(double(graphX + 1), double(graphY - 1)); + c = style->get_color(state); + cr->set_source_rgba(c.get_red(), c.get_green(), c.get_blue(), 0.2); + cr->line_to(double(graphX + 1), double(graphY - 1) - double(graphH - 2)); + cr->line_to(double(graphX) + 1.5 + locallabRef*double(graphW -2), double(graphY - 1) - double(graphH - 2)); + cr->line_to(double(graphX) + 1.5 + locallabRef*double(graphW -2), double(graphY - 1)); + cr->close_path(); + cr->fill(); + cr->stroke(); + } + // draw the left colored bar if (leftBar) { // first the background @@ -1870,6 +1885,35 @@ void MyFlatCurve::stopNumericalAdjustment() } } +void MyFlatCurve::updateLocallabBackground(double ref) +{ + locallabRef = ref; + + mcih->pending++; + + idle_register.add( + [this]() -> bool + { + if (mcih->destroyed) { + if (mcih->pending == 1) { + delete mcih; + } else { + --mcih->pending; + } + + return false; + } + + mcih->clearPixmap(); + mcih->myCurve->queue_draw(); + + --mcih->pending; + + return false; + } + ); +} + void MyFlatCurve::setType (FlatCurveType t) { diff --git a/rtgui/myflatcurve.h b/rtgui/myflatcurve.h index 5da1d09ad..f51586567 100644 --- a/rtgui/myflatcurve.h +++ b/rtgui/myflatcurve.h @@ -68,6 +68,8 @@ public: class MyFlatCurve final : public MyCurve { +private: + IdleRegister idle_register; protected: FlatCurveDescr curve; @@ -95,6 +97,7 @@ protected: enum EditedHandle editedHandle; bool draggingElement; enum MouseOverAreas area; + double locallabRef; // Locallab reference value to display in the background void draw (); void movePoint(bool moveX, bool moveY, bool pipetteDrag = false); @@ -129,4 +132,6 @@ public: void setPos(double pos, int chanIdx) override; void stopNumericalAdjustment() override; + + void updateLocallabBackground(double ref); }; diff --git a/rtgui/options.cc b/rtgui/options.cc index 7ae6c24aa..cc49f1fcd 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -177,6 +177,10 @@ void Options::updatePaths() lastWaveletCurvesDir = preferredPath; } + if (lastlocalCurvesDir.empty() || !Glib::file_test(lastlocalCurvesDir, Glib::FILE_TEST_EXISTS) || !Glib::file_test(lastlocalCurvesDir, Glib::FILE_TEST_IS_DIR)) { + lastlocalCurvesDir = preferredPath; + } + if (lastPFCurvesDir.empty() || !Glib::file_test(lastPFCurvesDir, Glib::FILE_TEST_EXISTS) || !Glib::file_test(lastPFCurvesDir, Glib::FILE_TEST_IS_DIR)) { lastPFCurvesDir = preferredPath; } @@ -447,7 +451,9 @@ void Options::setDefaults() histogramHeight = 200; histogramDrawMode = 0; curvebboxpos = 1; + complexity = 1; prevdemo = PD_Sidecar; + rgbDenoiseThreadLimit = 0; #if defined( _OPENMP ) && defined( __x86_64__ ) clutCacheSize = omp_get_num_procs(); @@ -473,6 +479,7 @@ void Options::setDefaults() menuGroupFileOperations = true; menuGroupProfileOperations = true; menuGroupExtProg = true; + showtooltip = true; ICCPC_primariesPreset = "sRGB", ICCPC_redPrimaryX = 0.6400; @@ -590,6 +597,15 @@ void Options::setDefaults() rtSettings.amchroma = 40;//between 20 and 140 low values increase effect..and also artifacts, high values reduces rtSettings.level0_cbdl = 0; rtSettings.level123_cbdl = 30; +//locallab + rtSettings.cropsleep = 50;//generate a pause of 50 µs for dcrop (100%)to avoid crash when moving window, between 0 to ?? + rtSettings.reduchigh = 0.85;//transition for luminance in scope + rtSettings.reduclow = 0.85;//transition for luminance out scope + rtSettings.detectshape = true;//experimental new detection shape + rtSettings.previewselection = 5;//between 1 to 40 + rtSettings.cbdlsensi = 1.0;//between 0.001 to 1 + rtSettings.fftwsigma = true; //choice between sigma^2 or empirical formula + rtSettings.itcwb_thres = 34;//between 10 to 55 rtSettings.itcwb_sort = false; rtSettings.itcwb_greenrange = 0;//between 0 to 2 @@ -599,6 +615,7 @@ void Options::setDefaults() rtSettings.itcwb_delta = 1;//between 0 and 5 rtSettings.itcwb_stdobserver10 = true; rtSettings.itcwb_precis = 5;//3 or 5 or 9 +// end locallab rtSettings.protectred = 60; rtSettings.protectredh = 0.3; @@ -638,6 +655,7 @@ void Options::setDefaults() lastRetinexDir = ""; lastDenoiseCurvesDir = ""; lastWaveletCurvesDir = ""; + lastlocalCurvesDir = ""; lastPFCurvesDir = ""; lastHsvCurvesDir = ""; lastToneCurvesDir = ""; @@ -757,6 +775,26 @@ void Options::readFromFile(Glib::ustring fname) if (keyFile.has_key("General", "Verbose")) { rtSettings.verbose = keyFile.get_boolean("General", "Verbose"); } + + if (keyFile.has_key("General", "Detectshape")) { + rtSettings.detectshape = keyFile.get_boolean("General", "Detectshape"); + } + + if (keyFile.has_key("General", "Fftwsigma")) { + rtSettings.fftwsigma = keyFile.get_boolean("General", "Fftwsigma"); + } + + if (keyFile.has_key("General", "Cropsleep")) { + rtSettings.cropsleep = keyFile.get_integer("General", "Cropsleep"); + } + + if (keyFile.has_key("General", "Reduchigh")) { + rtSettings.reduchigh = keyFile.get_double("General", "Reduchigh"); + } + + if (keyFile.has_key("General", "Reduclow")) { + rtSettings.reduclow = keyFile.get_double("General", "Reduclow"); + } } if (keyFile.has_group("External Editor")) { @@ -798,8 +836,8 @@ void Options::readFromFile(Glib::ustring fname) saveFormat.tiffBits = keyFile.get_integer("Output", "TiffBps"); } - if (keyFile.has_key ("Output", "TiffFloat")) { - saveFormat.tiffFloat = keyFile.get_boolean ("Output", "TiffFloat"); + if (keyFile.has_key("Output", "TiffFloat")) { + saveFormat.tiffFloat = keyFile.get_boolean("Output", "TiffFloat"); } if (keyFile.has_key("Output", "TiffUncompressed")) { @@ -831,8 +869,8 @@ void Options::readFromFile(Glib::ustring fname) saveFormatBatch.tiffBits = keyFile.get_integer("Output", "TiffBpsBatch"); } - if (keyFile.has_key ("Output", "TiffFloatBatch")) { - saveFormatBatch.tiffFloat = keyFile.get_boolean ("Output", "TiffFloatBatch"); + if (keyFile.has_key("Output", "TiffFloatBatch")) { + saveFormatBatch.tiffFloat = keyFile.get_boolean("Output", "TiffFloatBatch"); } if (keyFile.has_key("Output", "TiffUncompressedBatch")) { @@ -1380,12 +1418,12 @@ void Options::readFromFile(Glib::ustring fname) histogramBar = keyFile.get_boolean("GUI", "HistogramBar"); } - if (keyFile.has_key ("GUI", "HistogramHeight")) { - histogramHeight = keyFile.get_integer ("GUI", "HistogramHeight"); + if (keyFile.has_key("GUI", "HistogramHeight")) { + histogramHeight = keyFile.get_integer("GUI", "HistogramHeight"); } - if (keyFile.has_key ("GUI", "HistogramDrawMode")) { - histogramDrawMode = keyFile.get_integer ("GUI", "HistogramDrawMode"); + if (keyFile.has_key("GUI", "HistogramDrawMode")) { + histogramDrawMode = keyFile.get_integer("GUI", "HistogramDrawMode"); } if (keyFile.has_key("GUI", "NavigatorRGBUnit")) { @@ -1396,10 +1434,15 @@ void Options::readFromFile(Glib::ustring fname) navHSVUnit = (NavigatorUnit)keyFile.get_integer("GUI", "NavigatorHSVUnit"); } + if (keyFile.has_key("GUI", "ShowFilmStripToolBar")) { showFilmStripToolBar = keyFile.get_boolean("GUI", "ShowFilmStripToolBar"); } + if (keyFile.has_key("GUI", "Showtooltip")) {//show tooltip in locallab + showtooltip = keyFile.get_boolean("GUI", "Showtooltip"); + } + if (keyFile.has_key("GUI", "FileBrowserToolbarSingleRow")) { FileBrowserToolbarSingleRow = keyFile.get_boolean("GUI", "FileBrowserToolbarSingleRow"); } @@ -1415,6 +1458,11 @@ void Options::readFromFile(Glib::ustring fname) if (keyFile.has_key("GUI", "CurveBBoxPosition")) { curvebboxpos = keyFile.get_integer("GUI", "CurveBBoxPosition"); } + + if (keyFile.has_key("GUI", "Complexity")) { + complexity = keyFile.get_integer("GUI", "Complexity"); + } + } if (keyFile.has_group("Crop Settings")) { @@ -1644,51 +1692,75 @@ void Options::readFromFile(Glib::ustring fname) //if( keyFile.has_key ("Color Management", "Ciebadpixgauss")) rtSettings.ciebadpixgauss = keyFile.get_boolean("Color Management", "Ciebadpixgauss"); + if (keyFile.has_key("Color Management", "Previewselection")) {//Intensity of preview selection deltaE + rtSettings.previewselection = keyFile.get_integer("Color Management", "Previewselection"); + } + + + if (keyFile.has_key("Color Management", "Cbdlsensi")) {//sensibility to crash for cbdl + rtSettings.cbdlsensi = keyFile.get_double("Color Management", "Cbdlsensi"); + } + + } if (keyFile.has_group("ICC Profile Creator")) { if (keyFile.has_key("ICC Profile Creator", "PimariesPreset")) { ICCPC_primariesPreset = keyFile.get_string("ICC Profile Creator", "PimariesPreset"); } + if (keyFile.has_key("ICC Profile Creator", "RedPrimaryX")) { ICCPC_redPrimaryX = keyFile.get_double("ICC Profile Creator", "RedPrimaryX"); } + if (keyFile.has_key("ICC Profile Creator", "RedPrimaryY")) { ICCPC_redPrimaryY = keyFile.get_double("ICC Profile Creator", "RedPrimaryY"); } + if (keyFile.has_key("ICC Profile Creator", "GreenPrimaryX")) { ICCPC_greenPrimaryX = keyFile.get_double("ICC Profile Creator", "GreenPrimaryX"); } + if (keyFile.has_key("ICC Profile Creator", "GreenPrimaryY")) { ICCPC_greenPrimaryY = keyFile.get_double("ICC Profile Creator", "GreenPrimaryY"); } + if (keyFile.has_key("ICC Profile Creator", "BluePrimaryX")) { ICCPC_bluePrimaryX = keyFile.get_double("ICC Profile Creator", "BluePrimaryX"); } + if (keyFile.has_key("ICC Profile Creator", "BluePrimaryY")) { ICCPC_bluePrimaryY = keyFile.get_double("ICC Profile Creator", "BluePrimaryY"); } + if (keyFile.has_key("ICC Profile Creator", "GammaPreset")) { ICCPC_gammaPreset = keyFile.get_string("ICC Profile Creator", "GammaPreset"); } + if (keyFile.has_key("ICC Profile Creator", "Gamma")) { ICCPC_gamma = keyFile.get_double("ICC Profile Creator", "Gamma"); } + if (keyFile.has_key("ICC Profile Creator", "Slope")) { ICCPC_slope = keyFile.get_double("ICC Profile Creator", "Slope"); } + if (keyFile.has_key("ICC Profile Creator", "ProfileVersion")) { ICCPC_profileVersion = keyFile.get_string("ICC Profile Creator", "ProfileVersion"); } + if (keyFile.has_key("ICC Profile Creator", "Illuminant")) { ICCPC_illuminant = keyFile.get_string("ICC Profile Creator", "Illuminant"); } + if (keyFile.has_key("ICC Profile Creator", "Description")) { ICCPC_description = keyFile.get_string("ICC Profile Creator", "Description"); } + if (keyFile.has_key("ICC Profile Creator", "Copyright")) { ICCPC_copyright = keyFile.get_string("ICC Profile Creator", "Copyright"); } + if (keyFile.has_key("ICC Profile Creator", "AppendParamsToDesc")) { ICCPC_appendParamsToDesc = keyFile.get_boolean("ICC Profile Creator", "AppendParamsToDesc"); } @@ -1881,6 +1953,8 @@ void Options::readFromFile(Glib::ustring fname) safeDirGet(keyFile, "Dialogs", "LastRetinexDir", lastRetinexDir); safeDirGet(keyFile, "Dialogs", "LastDenoiseCurvesDir", lastDenoiseCurvesDir); safeDirGet(keyFile, "Dialogs", "LastWaveletCurvesDir", lastWaveletCurvesDir); + safeDirGet(keyFile, "Dialogs", "LastlocalCurvesDir", lastlocalCurvesDir); + safeDirGet(keyFile, "Dialogs", "LastPFCurvesDir", lastPFCurvesDir); safeDirGet(keyFile, "Dialogs", "LastHsvCurvesDir", lastHsvCurvesDir); safeDirGet(keyFile, "Dialogs", "LastBWCurvesDir", lastBWCurvesDir); @@ -1978,6 +2052,12 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_string("General", "DarkFramesPath", rtSettings.darkFramesPath); keyFile.set_string("General", "FlatFieldsPath", rtSettings.flatFieldsPath); keyFile.set_boolean("General", "Verbose", rtSettings.verbose); + keyFile.set_integer("General", "Cropsleep", rtSettings.cropsleep); + keyFile.set_double("General", "Reduchigh", rtSettings.reduchigh); + keyFile.set_double("General", "Reduclow", rtSettings.reduclow); + keyFile.set_boolean("General", "Detectshape", rtSettings.detectshape); + keyFile.set_boolean("General", "Fftwsigma", rtSettings.fftwsigma); + keyFile.set_integer("External Editor", "EditorKind", editorToSendTo); keyFile.set_string("External Editor", "GimpDir", gimpDir); keyFile.set_string("External Editor", "PhotoshopDir", psDir); @@ -2048,6 +2128,7 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_integer("Performance", "ChunkSizeCA", chunkSizeCA); keyFile.set_integer("Performance", "ThumbnailInspectorMode", int(rtSettings.thumbnail_inspector_mode)); + keyFile.set_string("Output", "Format", saveFormat.format); keyFile.set_integer("Output", "JpegQuality", saveFormat.jpegQuality); keyFile.set_integer("Output", "JpegSubSamp", saveFormat.jpegSubSamp); @@ -2137,28 +2218,30 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_integer("GUI", "FrameColor", bgcolor); keyFile.set_boolean("GUI", "ProcessingQueueEnbled", procQueueEnabled); Glib::ArrayHandle tpopen = tpOpen; - keyFile.set_integer_list ("GUI", "ToolPanelsExpanded", tpopen); - keyFile.set_boolean ("GUI", "ToolPanelsExpandedAutoSave", autoSaveTpOpen); - keyFile.set_integer ("GUI", "MultiDisplayMode", multiDisplayMode); - keyFile.set_double_list ("GUI", "CutOverlayBrush", cutOverlayBrush); - keyFile.set_double_list ("GUI", "NavGuideBrush", navGuideBrush); - keyFile.set_integer ("GUI", "HistogramPosition", histogramPosition); - keyFile.set_boolean ("GUI", "HistogramRed", histogramRed); - keyFile.set_boolean ("GUI", "HistogramGreen", histogramGreen); - keyFile.set_boolean ("GUI", "HistogramBlue", histogramBlue); - keyFile.set_boolean ("GUI", "HistogramLuma", histogramLuma); - keyFile.set_boolean ("GUI", "HistogramChroma", histogramChroma); - keyFile.set_boolean ("GUI", "HistogramRAW", histogramRAW); - keyFile.set_boolean ("GUI", "HistogramBar", histogramBar); - keyFile.set_integer ("GUI", "HistogramHeight", histogramHeight); - keyFile.set_integer ("GUI", "HistogramDrawMode", histogramDrawMode); - keyFile.set_integer ("GUI", "NavigatorRGBUnit", (int)navRGBUnit); - keyFile.set_integer ("GUI", "NavigatorHSVUnit", (int)navHSVUnit); - keyFile.set_boolean ("GUI", "ShowFilmStripToolBar", showFilmStripToolBar); - keyFile.set_boolean ("GUI", "FileBrowserToolbarSingleRow", FileBrowserToolbarSingleRow); - keyFile.set_boolean ("GUI", "HideTPVScrollbar", hideTPVScrollbar); - keyFile.set_boolean ("GUI", "HistogramWorking", rtSettings.HistogramWorking); - keyFile.set_integer ("GUI", "CurveBBoxPosition", curvebboxpos); + keyFile.set_integer_list("GUI", "ToolPanelsExpanded", tpopen); + keyFile.set_boolean("GUI", "ToolPanelsExpandedAutoSave", autoSaveTpOpen); + keyFile.set_integer("GUI", "MultiDisplayMode", multiDisplayMode); + keyFile.set_double_list("GUI", "CutOverlayBrush", cutOverlayBrush); + keyFile.set_double_list("GUI", "NavGuideBrush", navGuideBrush); + keyFile.set_integer("GUI", "HistogramPosition", histogramPosition); + keyFile.set_boolean("GUI", "HistogramRed", histogramRed); + keyFile.set_boolean("GUI", "HistogramGreen", histogramGreen); + keyFile.set_boolean("GUI", "HistogramBlue", histogramBlue); + keyFile.set_boolean("GUI", "HistogramLuma", histogramLuma); + keyFile.set_boolean("GUI", "HistogramChroma", histogramChroma); + keyFile.set_boolean("GUI", "HistogramRAW", histogramRAW); + keyFile.set_boolean("GUI", "HistogramBar", histogramBar); + keyFile.set_integer("GUI", "HistogramHeight", histogramHeight); + keyFile.set_integer("GUI", "HistogramDrawMode", histogramDrawMode); + keyFile.set_integer("GUI", "NavigatorRGBUnit", (int)navRGBUnit); + keyFile.set_integer("GUI", "NavigatorHSVUnit", (int)navHSVUnit); + keyFile.set_boolean("GUI", "ShowFilmStripToolBar", showFilmStripToolBar); + keyFile.set_boolean("GUI", "FileBrowserToolbarSingleRow", FileBrowserToolbarSingleRow); + keyFile.set_boolean("GUI", "HideTPVScrollbar", hideTPVScrollbar); + keyFile.set_boolean("GUI", "HistogramWorking", rtSettings.HistogramWorking); + keyFile.set_integer("GUI", "CurveBBoxPosition", curvebboxpos); + keyFile.set_boolean("GUI", "Showtooltip", showtooltip); + keyFile.set_integer("GUI", "Complexity", complexity); //Glib::ArrayHandle crvopen = crvOpen; //keyFile.set_integer_list ("GUI", "CurvePanelsExpanded", crvopen); @@ -2216,6 +2299,8 @@ void Options::saveToFile(Glib::ustring fname) //keyFile.set_double ("Color Management", "Colortoningab", rtSettings.colortoningab); //keyFile.set_double ("Color Management", "Decaction", rtSettings.decaction); keyFile.set_string("Color Management", "ClutsDirectory", clutsDir); + keyFile.set_integer("Color Management", "Previewselection", rtSettings.previewselection); + keyFile.set_double("Color Management", "Cbdlsensi", rtSettings.cbdlsensi); keyFile.set_string("ICC Profile Creator", "PimariesPreset", ICCPC_primariesPreset); keyFile.set_double("ICC Profile Creator", "RedPrimaryX", ICCPC_redPrimaryX); @@ -2286,6 +2371,7 @@ void Options::saveToFile(Glib::ustring fname) keyFile.set_string("Dialogs", "LastRetinexDir", lastRetinexDir); keyFile.set_string("Dialogs", "LastDenoiseCurvesDir", lastDenoiseCurvesDir); keyFile.set_string("Dialogs", "LastWaveletCurvesDir", lastWaveletCurvesDir); + keyFile.set_string("Dialogs", "LastlocalCurvesDir", lastlocalCurvesDir); keyFile.set_string("Dialogs", "LastPFCurvesDir", lastPFCurvesDir); keyFile.set_string("Dialogs", "LastHsvCurvesDir", lastHsvCurvesDir); keyFile.set_string("Dialogs", "LastBWCurvesDir", lastBWCurvesDir); @@ -2325,18 +2411,8 @@ void Options::load(bool lightweight) const gchar* path; Glib::ustring dPath; -#ifdef __APPLE__ - // Build Application Support directory path for macOS. - const char* homedir = g_getenv("HOME"); // This returns the current container data dir in ~/Library - std::string homebuf{homedir}; - int homelength = strlen(homebuf.c_str()); - homebuf[homelength-44] = '\0'; // Terminate string after ${HOME}/Library - std::string homeconfig{homebuf}; - std::strcat(&homeconfig[0], "/Application Support/RawTherapee/config"); - path = homeconfig.c_str(); -#else path = g_getenv("RT_SETTINGS"); -#endif + if (path != nullptr) { rtdir = Glib::ustring(path); @@ -2356,7 +2432,11 @@ void Options::load(bool lightweight) } #else + #ifdef __APPLE__ + rtdir = Glib::build_filename(Glib::ustring(g_get_home_dir()), "/Library/Application Support/", Glib::ustring(CACHEFOLDERNAME), "/config/"); + #else rtdir = Glib::build_filename(Glib::ustring(g_get_user_config_dir()), Glib::ustring(CACHEFOLDERNAME)); + #endif #endif } @@ -2378,14 +2458,9 @@ void Options::load(bool lightweight) rtdir = Glib::build_filename(argv0, "mysettings"); } - // Modify the path of the cache folder to the one provided in RT_CACHE environment variable. Build the cache folder name in macOS. -#ifdef __APPLE__ - std::string homecache{homebuf}; - std::strcat(&homecache[0], "/Application Support/RawTherapee/cache"); - path = homecache.c_str(); -#else + // Modify the path of the cache folder to the one provided in RT_CACHE environment variable. path = g_getenv("RT_CACHE"); -#endif + if (path != nullptr) { cacheBaseDir = Glib::ustring(path); @@ -2400,7 +2475,11 @@ void Options::load(bool lightweight) #ifdef WIN32 cacheBaseDir = Glib::build_filename(rtdir, "cache"); #else + #ifdef __APPLE__ + cacheBaseDir = Glib::build_filename(Glib::ustring(g_get_home_dir()), "/Library/Application Support/", Glib::ustring(CACHEFOLDERNAME), "/cache/"); + #else cacheBaseDir = Glib::build_filename(Glib::ustring(g_get_user_cache_dir()), Glib::ustring(CACHEFOLDERNAME)); + #endif #endif } diff --git a/rtgui/options.h b/rtgui/options.h index b8a71c9cb..02d62292c 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -282,12 +282,13 @@ public: //std::vector crvOpen; std::vector baBehav; rtengine::Settings rtSettings; - + bool showtooltip; std::vector favoriteDirs; std::vector renameTemplates; bool renameUseTemplates; bool internalThumbIfUntouched; bool overwriteOutputFile; + int complexity; std::vector thumbnailZoomRatios; bool overlayedFileNames; @@ -409,6 +410,7 @@ public: Glib::ustring lastRetinexDir; Glib::ustring lastDenoiseCurvesDir; Glib::ustring lastWaveletCurvesDir; + Glib::ustring lastlocalCurvesDir; Glib::ustring lastPFCurvesDir; Glib::ustring lastHsvCurvesDir; Glib::ustring lastToneCurvesDir; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 511aefaa7..bbfb7cc4e 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -338,14 +338,35 @@ void ParamsEdited::set(bool v) lensProf.lfCameraMake = v; lensProf.lfCameraModel = v; lensProf.lfLens = v; + perspective.method = v; perspective.horizontal = v; perspective.vertical = v; + perspective.camera_crop_factor = v; + perspective.camera_focal_length = v; + perspective.camera_pitch = v; + perspective.camera_roll = v; + perspective.camera_shift_horiz = v; + perspective.camera_shift_vert = v; + perspective.camera_yaw = v; + perspective.projection_pitch = v; + perspective.projection_rotate = v; + perspective.projection_shift_horiz = v; + perspective.projection_shift_vert = v; + perspective.projection_yaw = v; gradient.enabled = v; gradient.degree = v; gradient.feather = v; gradient.strength = v; gradient.centerX = v; gradient.centerY = v; + + locallab.enabled = v; + locallab.selspot = v; + + for (size_t i = 0; i < locallab.spots.size(); i++) { + locallab.spots.at(i).set(v); + } + pcvignette.enabled = v; pcvignette.strength = v; pcvignette.feather = v; @@ -520,9 +541,9 @@ void ParamsEdited::set(bool v) wavelet.HSmethod = v; wavelet.Dirmethod = v; wavelet.sigma = v; - wavelet.sigma = v; wavelet.offset = v; wavelet.lowthr = v; + wavelet.rescon = v; wavelet.resconH = v; wavelet.reschro = v; wavelet.resblur = v; @@ -655,6 +676,13 @@ void ParamsEdited::initFrom(const std::vector& const ProcParams& p = src[0]; + // Resize LocallabSpotEdited according to src[0] + locallab.spots.clear(); + locallab.spots.resize(p.locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); + + // Variable used to determined if Locallab spots number is equal and so spots can be combined + bool isSpotNumberEqual = true; + for (size_t i = 1; i < src.size(); i++) { const ProcParams& other = src[i]; toneCurve.curve = toneCurve.curve && p.toneCurve.curve == other.toneCurve.curve; @@ -963,14 +991,501 @@ void ParamsEdited::initFrom(const std::vector& lensProf.lfCameraMake = lensProf.lfCameraMake && p.lensProf.lfCameraMake == other.lensProf.lfCameraMake; lensProf.lfCameraModel = lensProf.lfCameraModel && p.lensProf.lfCameraModel == other.lensProf.lfCameraModel; lensProf.lfLens = lensProf.lfLens && p.lensProf.lfLens == other.lensProf.lfLens; + perspective.method = perspective.method && p.perspective.method == other.perspective.method; perspective.horizontal = perspective.horizontal && p.perspective.horizontal == other.perspective.horizontal; perspective.vertical = perspective.vertical && p.perspective.vertical == other.perspective.vertical; + perspective.camera_crop_factor = perspective.camera_crop_factor && p.perspective.camera_crop_factor == other.perspective.camera_crop_factor; + perspective.camera_focal_length = perspective.camera_focal_length && p.perspective.camera_focal_length == other.perspective.camera_focal_length; + perspective.camera_pitch = perspective.camera_pitch && p.perspective.camera_pitch == other.perspective.camera_pitch; + perspective.camera_roll = perspective.camera_roll && p.perspective.camera_roll == other.perspective.camera_roll; + perspective.camera_shift_horiz = perspective.camera_shift_horiz && p.perspective.camera_shift_horiz == other.perspective.camera_shift_horiz; + perspective.camera_shift_vert = perspective.camera_shift_vert && p.perspective.camera_shift_vert == other.perspective.camera_shift_vert; + perspective.projection_pitch = perspective.projection_pitch && p.perspective.projection_pitch == other.perspective.projection_pitch; + perspective.camera_yaw = perspective.camera_yaw && p.perspective.camera_yaw == other.perspective.camera_yaw; + perspective.projection_rotate = perspective.projection_rotate && p.perspective.projection_rotate == other.perspective.projection_rotate; + perspective.projection_shift_horiz = perspective.projection_shift_horiz && p.perspective.projection_shift_horiz == other.perspective.projection_shift_horiz; + perspective.projection_shift_vert = perspective.projection_shift_vert && p.perspective.projection_shift_vert == other.perspective.projection_shift_vert; + perspective.projection_yaw = perspective.projection_yaw && p.perspective.projection_yaw == other.perspective.projection_yaw; gradient.enabled = gradient.enabled && p.gradient.enabled == other.gradient.enabled; gradient.degree = gradient.degree && p.gradient.degree == other.gradient.degree; gradient.feather = gradient.feather && p.gradient.feather == other.gradient.feather; gradient.strength = gradient.strength && p.gradient.strength == other.gradient.strength; gradient.centerX = gradient.centerX && p.gradient.centerX == other.gradient.centerX; gradient.centerY = gradient.centerY && p.gradient.centerY == other.gradient.centerY; + + locallab.enabled = locallab.enabled && p.locallab.enabled == other.locallab.enabled; + isSpotNumberEqual = isSpotNumberEqual && p.locallab.spots.size() == other.locallab.spots.size(); + locallab.selspot = locallab.selspot && p.locallab.selspot == other.locallab.selspot; + + if (isSpotNumberEqual) { + for (size_t j = 0; j < locallab.spots.size() && j < p.locallab.spots.size() && j < other.locallab.spots.size(); j++) { + const LocallabParams::LocallabSpot& pSpot = p.locallab.spots.at(j); + const LocallabParams::LocallabSpot& otherSpot = other.locallab.spots.at(j); + // Control spot settings + locallab.spots.at(j).name = locallab.spots.at(j).name && pSpot.name == otherSpot.name; + locallab.spots.at(j).isvisible = locallab.spots.at(j).isvisible && pSpot.isvisible == otherSpot.isvisible; + locallab.spots.at(j).shape = locallab.spots.at(j).shape && pSpot.shape == otherSpot.shape; + locallab.spots.at(j).spotMethod = locallab.spots.at(j).spotMethod && pSpot.spotMethod == otherSpot.spotMethod; + locallab.spots.at(j).wavMethod = locallab.spots.at(j).wavMethod && pSpot.wavMethod == otherSpot.wavMethod; + locallab.spots.at(j).sensiexclu = locallab.spots.at(j).sensiexclu && pSpot.sensiexclu == otherSpot.sensiexclu; + locallab.spots.at(j).structexclu = locallab.spots.at(j).structexclu && pSpot.structexclu == otherSpot.structexclu; + locallab.spots.at(j).struc = locallab.spots.at(j).struc && pSpot.struc == otherSpot.struc; + locallab.spots.at(j).shapeMethod = locallab.spots.at(j).shapeMethod && pSpot.shapeMethod == otherSpot.shapeMethod; + locallab.spots.at(j).loc = locallab.spots.at(j).loc && pSpot.loc == otherSpot.loc; + locallab.spots.at(j).centerX = locallab.spots.at(j).centerX && pSpot.centerX == otherSpot.centerX; + locallab.spots.at(j).centerY = locallab.spots.at(j).centerY && pSpot.centerY == otherSpot.centerY; + locallab.spots.at(j).circrad = locallab.spots.at(j).circrad && pSpot.circrad == otherSpot.circrad; + locallab.spots.at(j).qualityMethod = locallab.spots.at(j).qualityMethod && pSpot.qualityMethod == otherSpot.qualityMethod; + locallab.spots.at(j).complexMethod = locallab.spots.at(j).complexMethod && pSpot.complexMethod == otherSpot.complexMethod; + locallab.spots.at(j).transit = locallab.spots.at(j).transit && pSpot.transit == otherSpot.transit; + locallab.spots.at(j).feather = locallab.spots.at(j).feather && pSpot.feather == otherSpot.feather; + locallab.spots.at(j).thresh = locallab.spots.at(j).thresh && pSpot.thresh == otherSpot.thresh; + locallab.spots.at(j).iter = locallab.spots.at(j).iter && pSpot.iter == otherSpot.iter; + locallab.spots.at(j).balan = locallab.spots.at(j).balan && pSpot.balan == otherSpot.balan; + locallab.spots.at(j).balanh = locallab.spots.at(j).balanh && pSpot.balanh == otherSpot.balanh; + locallab.spots.at(j).colorde = locallab.spots.at(j).colorde && pSpot.colorde == otherSpot.colorde; + locallab.spots.at(j).colorscope = locallab.spots.at(j).colorscope && pSpot.colorscope == otherSpot.colorscope; + locallab.spots.at(j).transitweak = locallab.spots.at(j).transitweak && pSpot.transitweak == otherSpot.transitweak; + locallab.spots.at(j).transitgrad = locallab.spots.at(j).transitgrad && pSpot.transitgrad == otherSpot.transitgrad; + locallab.spots.at(j).avoid = locallab.spots.at(j).avoid && pSpot.avoid == otherSpot.avoid; + locallab.spots.at(j).blwh = locallab.spots.at(j).blwh && pSpot.blwh == otherSpot.blwh; + locallab.spots.at(j).recurs = locallab.spots.at(j).recurs && pSpot.recurs == otherSpot.recurs; + locallab.spots.at(j).laplac = locallab.spots.at(j).laplac && pSpot.laplac == otherSpot.laplac; + locallab.spots.at(j).deltae = locallab.spots.at(j).deltae && pSpot.deltae == otherSpot.deltae; + locallab.spots.at(j).shortc = locallab.spots.at(j).shortc && pSpot.shortc == otherSpot.shortc; + locallab.spots.at(j).savrest = locallab.spots.at(j).savrest && pSpot.savrest == otherSpot.savrest; + locallab.spots.at(j).scopemask = locallab.spots.at(j).scopemask && pSpot.scopemask == otherSpot.scopemask; + locallab.spots.at(j).lumask = locallab.spots.at(j).lumask && pSpot.lumask == otherSpot.lumask; + // Color & Light + locallab.spots.at(j).visicolor = locallab.spots.at(j).visicolor && pSpot.visicolor == otherSpot.visicolor; + locallab.spots.at(j).expcolor = locallab.spots.at(j).expcolor && pSpot.expcolor == otherSpot.expcolor; + locallab.spots.at(j).complexcolor = locallab.spots.at(j).complexcolor && pSpot.complexcolor == otherSpot.complexcolor; + locallab.spots.at(j).curvactiv = locallab.spots.at(j).curvactiv && pSpot.curvactiv == otherSpot.curvactiv; + locallab.spots.at(j).lightness = locallab.spots.at(j).lightness && pSpot.lightness == otherSpot.lightness; + locallab.spots.at(j).contrast = locallab.spots.at(j).contrast && pSpot.contrast == otherSpot.contrast; + locallab.spots.at(j).chroma = locallab.spots.at(j).chroma && pSpot.chroma == otherSpot.chroma; + locallab.spots.at(j).labgridALow = locallab.spots.at(j).labgridALow && pSpot.labgridALow == otherSpot.labgridALow; + locallab.spots.at(j).labgridBLow = locallab.spots.at(j).labgridBLow && pSpot.labgridBLow == otherSpot.labgridBLow; + locallab.spots.at(j).labgridAHigh = locallab.spots.at(j).labgridAHigh && pSpot.labgridAHigh == otherSpot.labgridAHigh; + locallab.spots.at(j).labgridBHigh = locallab.spots.at(j).labgridBHigh && pSpot.labgridBHigh == otherSpot.labgridBHigh; + locallab.spots.at(j).labgridALowmerg = locallab.spots.at(j).labgridALowmerg && pSpot.labgridALowmerg == otherSpot.labgridALowmerg; + locallab.spots.at(j).labgridBLowmerg = locallab.spots.at(j).labgridBLowmerg && pSpot.labgridBLowmerg == otherSpot.labgridBLowmerg; + locallab.spots.at(j).labgridAHighmerg = locallab.spots.at(j).labgridAHighmerg && pSpot.labgridAHighmerg == otherSpot.labgridAHighmerg; + locallab.spots.at(j).labgridBHighmerg = locallab.spots.at(j).labgridBHighmerg && pSpot.labgridBHighmerg == otherSpot.labgridBHighmerg; + locallab.spots.at(j).strengthgrid = locallab.spots.at(j).strengthgrid && pSpot.strengthgrid == otherSpot.strengthgrid; + locallab.spots.at(j).sensi = locallab.spots.at(j).sensi && pSpot.sensi == otherSpot.sensi; + locallab.spots.at(j).structcol = locallab.spots.at(j).structcol && pSpot.structcol == otherSpot.structcol; + locallab.spots.at(j).strcol = locallab.spots.at(j).strcol && pSpot.strcol == otherSpot.strcol; + locallab.spots.at(j).strcolab = locallab.spots.at(j).strcolab && pSpot.strcolab == otherSpot.strcolab; + locallab.spots.at(j).strcolh = locallab.spots.at(j).strcolh && pSpot.strcolh == otherSpot.strcolh; + locallab.spots.at(j).angcol = locallab.spots.at(j).angcol && pSpot.angcol == otherSpot.angcol; + locallab.spots.at(j).blurcolde = locallab.spots.at(j).blurcolde && pSpot.blurcolde == otherSpot.blurcolde; + locallab.spots.at(j).blurcol = locallab.spots.at(j).blurcol && pSpot.blurcol == otherSpot.blurcol; + locallab.spots.at(j).contcol = locallab.spots.at(j).contcol && pSpot.contcol == otherSpot.contcol; + locallab.spots.at(j).blendmaskcol = locallab.spots.at(j).blendmaskcol && pSpot.blendmaskcol == otherSpot.blendmaskcol; + locallab.spots.at(j).radmaskcol = locallab.spots.at(j).radmaskcol && pSpot.radmaskcol == otherSpot.radmaskcol; + locallab.spots.at(j).chromaskcol = locallab.spots.at(j).chromaskcol && pSpot.chromaskcol == otherSpot.chromaskcol; + locallab.spots.at(j).gammaskcol = locallab.spots.at(j).gammaskcol && pSpot.gammaskcol == otherSpot.gammaskcol; + locallab.spots.at(j).slomaskcol = locallab.spots.at(j).slomaskcol && pSpot.slomaskcol == otherSpot.slomaskcol; + locallab.spots.at(j).shadmaskcol = locallab.spots.at(j).shadmaskcol && pSpot.shadmaskcol == otherSpot.shadmaskcol; + locallab.spots.at(j).strumaskcol = locallab.spots.at(j).strumaskcol && pSpot.strumaskcol == otherSpot.strumaskcol; + locallab.spots.at(j).lapmaskcol = locallab.spots.at(j).lapmaskcol && pSpot.lapmaskcol == otherSpot.lapmaskcol; + locallab.spots.at(j).qualitycurveMethod = locallab.spots.at(j).qualitycurveMethod && pSpot.qualitycurveMethod == otherSpot.qualitycurveMethod; + locallab.spots.at(j).gridMethod = locallab.spots.at(j).gridMethod && pSpot.gridMethod == otherSpot.gridMethod; + locallab.spots.at(j).merMethod = locallab.spots.at(j).merMethod && pSpot.merMethod == otherSpot.merMethod; + locallab.spots.at(j).toneMethod = locallab.spots.at(j).toneMethod && pSpot.toneMethod == otherSpot.toneMethod; + locallab.spots.at(j).mergecolMethod = locallab.spots.at(j).mergecolMethod && pSpot.mergecolMethod == otherSpot.mergecolMethod; + locallab.spots.at(j).llcurve = locallab.spots.at(j).llcurve && pSpot.llcurve == otherSpot.llcurve; + locallab.spots.at(j).lccurve = locallab.spots.at(j).lccurve && pSpot.lccurve == otherSpot.lccurve; + locallab.spots.at(j).cccurve = locallab.spots.at(j).cccurve && pSpot.cccurve == otherSpot.cccurve; + locallab.spots.at(j).clcurve = locallab.spots.at(j).clcurve && pSpot.cccurve == otherSpot.clcurve; + locallab.spots.at(j).rgbcurve = locallab.spots.at(j).rgbcurve && pSpot.rgbcurve == otherSpot.rgbcurve; + locallab.spots.at(j).LHcurve = locallab.spots.at(j).LHcurve && pSpot.LHcurve == otherSpot.LHcurve; + locallab.spots.at(j).HHcurve = locallab.spots.at(j).HHcurve && pSpot.HHcurve == otherSpot.HHcurve; + locallab.spots.at(j).invers = locallab.spots.at(j).invers && pSpot.invers == otherSpot.invers; + locallab.spots.at(j).special = locallab.spots.at(j).special && pSpot.special == otherSpot.special; + locallab.spots.at(j).toolcol = locallab.spots.at(j).toolcol && pSpot.toolcol == otherSpot.toolcol; + locallab.spots.at(j).enaColorMask = locallab.spots.at(j).enaColorMask && pSpot.enaColorMask == otherSpot.enaColorMask; + locallab.spots.at(j).fftColorMask = locallab.spots.at(j).fftColorMask && pSpot.fftColorMask == otherSpot.fftColorMask; + locallab.spots.at(j).CCmaskcurve = locallab.spots.at(j).CCmaskcurve && pSpot.CCmaskcurve == otherSpot.CCmaskcurve; + locallab.spots.at(j).LLmaskcurve = locallab.spots.at(j).LLmaskcurve && pSpot.LLmaskcurve == otherSpot.LLmaskcurve; + locallab.spots.at(j).HHmaskcurve = locallab.spots.at(j).HHmaskcurve && pSpot.HHmaskcurve == otherSpot.HHmaskcurve; + locallab.spots.at(j).HHhmaskcurve = locallab.spots.at(j).HHhmaskcurve && pSpot.HHhmaskcurve == otherSpot.HHhmaskcurve; + locallab.spots.at(j).softradiuscol = locallab.spots.at(j).softradiuscol && pSpot.softradiuscol == otherSpot.softradiuscol; + locallab.spots.at(j).opacol = locallab.spots.at(j).opacol && pSpot.opacol == otherSpot.opacol; + locallab.spots.at(j).mercol = locallab.spots.at(j).mercol && pSpot.mercol == otherSpot.mercol; + locallab.spots.at(j).merlucol = locallab.spots.at(j).merlucol && pSpot.merlucol == otherSpot.merlucol; + locallab.spots.at(j).conthrcol = locallab.spots.at(j).conthrcol && pSpot.conthrcol == otherSpot.conthrcol; + locallab.spots.at(j).Lmaskcurve = locallab.spots.at(j).Lmaskcurve && pSpot.Lmaskcurve == otherSpot.Lmaskcurve; + locallab.spots.at(j).LLmaskcolcurvewav = locallab.spots.at(j).LLmaskcolcurvewav && pSpot.LLmaskcolcurvewav == otherSpot.LLmaskcolcurvewav; + locallab.spots.at(j).csthresholdcol = locallab.spots.at(j).csthresholdcol && pSpot.csthresholdcol == otherSpot.csthresholdcol; + // Exposure + locallab.spots.at(j).visiexpose = locallab.spots.at(j).visiexpose && pSpot.visiexpose == otherSpot.visiexpose; + locallab.spots.at(j).expexpose = locallab.spots.at(j).expexpose && pSpot.expexpose == otherSpot.expexpose; + locallab.spots.at(j).complexexpose = locallab.spots.at(j).complexexpose && pSpot.complexexpose == otherSpot.complexexpose; + locallab.spots.at(j).expcomp = locallab.spots.at(j).expcomp && pSpot.expcomp == otherSpot.expcomp; + locallab.spots.at(j).hlcompr = locallab.spots.at(j).hlcompr && pSpot.hlcompr == otherSpot.hlcompr; + locallab.spots.at(j).hlcomprthresh = locallab.spots.at(j).hlcomprthresh && pSpot.hlcomprthresh == otherSpot.hlcomprthresh; + locallab.spots.at(j).black = locallab.spots.at(j).black && pSpot.black == otherSpot.black; + locallab.spots.at(j).shadex = locallab.spots.at(j).shadex && pSpot.shadex == otherSpot.shadex; + locallab.spots.at(j).shcompr = locallab.spots.at(j).shcompr && pSpot.shcompr == otherSpot.shcompr; + locallab.spots.at(j).expchroma = locallab.spots.at(j).expchroma && pSpot.expchroma == otherSpot.expchroma; + locallab.spots.at(j).sensiex = locallab.spots.at(j).sensiex && pSpot.sensiex == otherSpot.sensiex; + locallab.spots.at(j).structexp = locallab.spots.at(j).structexp && pSpot.structexp == otherSpot.structexp; + locallab.spots.at(j).blurexpde = locallab.spots.at(j).blurexpde && pSpot.blurexpde == otherSpot.blurexpde; + locallab.spots.at(j).strexp = locallab.spots.at(j).strexp && pSpot.strexp == otherSpot.strexp; + locallab.spots.at(j).angexp = locallab.spots.at(j).angexp && pSpot.angexp == otherSpot.angexp; + locallab.spots.at(j).excurve = locallab.spots.at(j).excurve && pSpot.excurve == otherSpot.excurve; + locallab.spots.at(j).inversex = locallab.spots.at(j).inversex && pSpot.inversex == otherSpot.inversex; + locallab.spots.at(j).enaExpMask = locallab.spots.at(j).enaExpMask && pSpot.enaExpMask == otherSpot.enaExpMask; + locallab.spots.at(j).enaExpMaskaft = locallab.spots.at(j).enaExpMaskaft && pSpot.enaExpMaskaft == otherSpot.enaExpMaskaft; + locallab.spots.at(j).CCmaskexpcurve = locallab.spots.at(j).CCmaskexpcurve && pSpot.CCmaskexpcurve == otherSpot.CCmaskexpcurve; + locallab.spots.at(j).LLmaskexpcurve = locallab.spots.at(j).LLmaskexpcurve && pSpot.LLmaskexpcurve == otherSpot.LLmaskexpcurve; + locallab.spots.at(j).HHmaskexpcurve = locallab.spots.at(j).HHmaskexpcurve && pSpot.HHmaskexpcurve == otherSpot.HHmaskexpcurve; + locallab.spots.at(j).blendmaskexp = locallab.spots.at(j).blendmaskexp && pSpot.blendmaskexp == otherSpot.blendmaskexp; + locallab.spots.at(j).radmaskexp = locallab.spots.at(j).radmaskexp && pSpot.radmaskexp == otherSpot.radmaskexp; + locallab.spots.at(j).chromaskexp = locallab.spots.at(j).chromaskexp && pSpot.chromaskexp == otherSpot.chromaskexp; + locallab.spots.at(j).gammaskexp = locallab.spots.at(j).gammaskexp && pSpot.gammaskexp == otherSpot.gammaskexp; + locallab.spots.at(j).slomaskexp = locallab.spots.at(j).slomaskexp && pSpot.slomaskexp == otherSpot.slomaskexp; + locallab.spots.at(j).lapmaskexp = locallab.spots.at(j).lapmaskexp && pSpot.lapmaskexp == otherSpot.lapmaskexp; + locallab.spots.at(j).strmaskexp = locallab.spots.at(j).strmaskexp && pSpot.strmaskexp == otherSpot.strmaskexp; + locallab.spots.at(j).angmaskexp = locallab.spots.at(j).angmaskexp && pSpot.angmaskexp == otherSpot.angmaskexp; + locallab.spots.at(j).softradiusexp = locallab.spots.at(j).softradiusexp && pSpot.softradiusexp == otherSpot.softradiusexp; + locallab.spots.at(j).Lmaskexpcurve = locallab.spots.at(j).Lmaskexpcurve && pSpot.Lmaskexpcurve == otherSpot.Lmaskexpcurve; + locallab.spots.at(j).expMethod = locallab.spots.at(j).expMethod && pSpot.expMethod == otherSpot.expMethod; + locallab.spots.at(j).exnoiseMethod = locallab.spots.at(j).exnoiseMethod && pSpot.exnoiseMethod == otherSpot.exnoiseMethod; + locallab.spots.at(j).laplacexp = locallab.spots.at(j).laplacexp && pSpot.laplacexp == otherSpot.laplacexp; + locallab.spots.at(j).balanexp = locallab.spots.at(j).balanexp && pSpot.balanexp == otherSpot.balanexp; + locallab.spots.at(j).linear = locallab.spots.at(j).linear && pSpot.linear == otherSpot.linear; + locallab.spots.at(j).gamm = locallab.spots.at(j).gamm && pSpot.gamm == otherSpot.gamm; + locallab.spots.at(j).fatamount = locallab.spots.at(j).fatamount && pSpot.fatamount == otherSpot.fatamount; + locallab.spots.at(j).fatdetail = locallab.spots.at(j).fatdetail && pSpot.fatdetail == otherSpot.fatdetail; + locallab.spots.at(j).fatanchor = locallab.spots.at(j).fatanchor && pSpot.fatanchor == otherSpot.fatanchor; + locallab.spots.at(j).fatlevel = locallab.spots.at(j).fatlevel && pSpot.fatlevel == otherSpot.fatlevel; + // Shadow highlight + locallab.spots.at(j).visishadhigh = locallab.spots.at(j).visishadhigh && pSpot.visishadhigh == otherSpot.visishadhigh; + locallab.spots.at(j).expshadhigh = locallab.spots.at(j).expshadhigh && pSpot.expshadhigh == otherSpot.expshadhigh; + locallab.spots.at(j).complexshadhigh = locallab.spots.at(j).complexshadhigh && pSpot.complexshadhigh == otherSpot.complexshadhigh; + locallab.spots.at(j).shMethod = locallab.spots.at(j).shMethod && pSpot.shMethod == otherSpot.shMethod; + + for (int k = 0; k < 5; k++) { + locallab.spots.at(j).multsh[k] = locallab.spots.at(j).multsh[k] && pSpot.multsh[k] == otherSpot.multsh[k]; + } + + locallab.spots.at(j).highlights = locallab.spots.at(j).highlights && pSpot.highlights == otherSpot.highlights; + locallab.spots.at(j).h_tonalwidth = locallab.spots.at(j).h_tonalwidth && pSpot.h_tonalwidth == otherSpot.h_tonalwidth; + locallab.spots.at(j).shadows = locallab.spots.at(j).shadows && pSpot.shadows == otherSpot.shadows; + locallab.spots.at(j).s_tonalwidth = locallab.spots.at(j).s_tonalwidth && pSpot.s_tonalwidth == otherSpot.s_tonalwidth; + locallab.spots.at(j).sh_radius = locallab.spots.at(j).sh_radius && pSpot.sh_radius == otherSpot.sh_radius; + locallab.spots.at(j).sensihs = locallab.spots.at(j).sensihs && pSpot.sensihs == otherSpot.sensihs; + locallab.spots.at(j).enaSHMask = locallab.spots.at(j).enaSHMask && pSpot.enaSHMask == otherSpot.enaSHMask; + locallab.spots.at(j).CCmaskSHcurve = locallab.spots.at(j).CCmaskSHcurve && pSpot.CCmaskSHcurve == otherSpot.CCmaskSHcurve; + locallab.spots.at(j).LLmaskSHcurve = locallab.spots.at(j).LLmaskSHcurve && pSpot.LLmaskSHcurve == otherSpot.LLmaskSHcurve; + locallab.spots.at(j).HHmaskSHcurve = locallab.spots.at(j).HHmaskSHcurve && pSpot.HHmaskSHcurve == otherSpot.HHmaskSHcurve; + locallab.spots.at(j).blendmaskSH = locallab.spots.at(j).blendmaskSH && pSpot.blendmaskSH == otherSpot.blendmaskSH; + locallab.spots.at(j).radmaskSH = locallab.spots.at(j).radmaskSH && pSpot.radmaskSH == otherSpot.radmaskSH; + locallab.spots.at(j).blurSHde = locallab.spots.at(j).blurSHde && pSpot.blurSHde == otherSpot.blurSHde; + locallab.spots.at(j).strSH = locallab.spots.at(j).strSH && pSpot.strSH == otherSpot.strSH; + locallab.spots.at(j).angSH = locallab.spots.at(j).angSH && pSpot.angSH == otherSpot.angSH; + locallab.spots.at(j).inverssh = locallab.spots.at(j).inverssh && pSpot.inverssh == otherSpot.inverssh; + locallab.spots.at(j).chromaskSH = locallab.spots.at(j).chromaskSH && pSpot.chromaskSH == otherSpot.chromaskSH; + locallab.spots.at(j).gammaskSH = locallab.spots.at(j).gammaskSH && pSpot.gammaskSH == otherSpot.gammaskSH; + locallab.spots.at(j).slomaskSH = locallab.spots.at(j).slomaskSH && pSpot.slomaskSH == otherSpot.slomaskSH; + locallab.spots.at(j).lapmaskSH = locallab.spots.at(j).lapmaskSH && pSpot.lapmaskSH == otherSpot.lapmaskSH; + locallab.spots.at(j).detailSH = locallab.spots.at(j).detailSH && pSpot.detailSH == otherSpot.detailSH; + locallab.spots.at(j).LmaskSHcurve = locallab.spots.at(j).LmaskSHcurve && pSpot.LmaskSHcurve == otherSpot.LmaskSHcurve; + locallab.spots.at(j).fatamountSH = locallab.spots.at(j).fatamountSH && pSpot.fatamountSH == otherSpot.fatamountSH; + locallab.spots.at(j).fatanchorSH = locallab.spots.at(j).fatanchorSH && pSpot.fatanchorSH == otherSpot.fatanchorSH; + locallab.spots.at(j).gamSH = locallab.spots.at(j).gamSH && pSpot.gamSH == otherSpot.gamSH; + locallab.spots.at(j).sloSH = locallab.spots.at(j).sloSH && pSpot.sloSH == otherSpot.sloSH; + // Vibrance + locallab.spots.at(j).visivibrance = locallab.spots.at(j).visivibrance && pSpot.visivibrance == otherSpot.visivibrance; + locallab.spots.at(j).expvibrance = locallab.spots.at(j).expvibrance && pSpot.expvibrance == otherSpot.expvibrance; + locallab.spots.at(j).complexvibrance = locallab.spots.at(j).complexvibrance && pSpot.complexvibrance == otherSpot.complexvibrance; + locallab.spots.at(j).saturated = locallab.spots.at(j).saturated && pSpot.saturated == otherSpot.saturated; + locallab.spots.at(j).pastels = locallab.spots.at(j).pastels && pSpot.pastels == otherSpot.pastels; + locallab.spots.at(j).warm = locallab.spots.at(j).warm && pSpot.warm == otherSpot.warm; + locallab.spots.at(j).psthreshold = locallab.spots.at(j).psthreshold && pSpot.psthreshold == otherSpot.psthreshold; + locallab.spots.at(j).protectskins = locallab.spots.at(j).protectskins && pSpot.protectskins == otherSpot.protectskins; + locallab.spots.at(j).avoidcolorshift = locallab.spots.at(j).avoidcolorshift && pSpot.avoidcolorshift == otherSpot.avoidcolorshift; + locallab.spots.at(j).pastsattog = locallab.spots.at(j).pastsattog && pSpot.pastsattog == otherSpot.pastsattog; + locallab.spots.at(j).sensiv = locallab.spots.at(j).sensiv && pSpot.sensiv == otherSpot.sensiv; + locallab.spots.at(j).skintonescurve = locallab.spots.at(j).skintonescurve && pSpot.skintonescurve == otherSpot.skintonescurve; + locallab.spots.at(j).CCmaskvibcurve = locallab.spots.at(j).CCmaskvibcurve && pSpot.CCmaskvibcurve == otherSpot.CCmaskvibcurve; + locallab.spots.at(j).LLmaskvibcurve = locallab.spots.at(j).LLmaskvibcurve && pSpot.LLmaskvibcurve == otherSpot.LLmaskvibcurve; + locallab.spots.at(j).HHmaskvibcurve = locallab.spots.at(j).HHmaskvibcurve && pSpot.HHmaskvibcurve == otherSpot.HHmaskvibcurve; + locallab.spots.at(j).enavibMask = locallab.spots.at(j).enavibMask && pSpot.enavibMask == otherSpot.enavibMask; + locallab.spots.at(j).blendmaskvib = locallab.spots.at(j).blendmaskvib && pSpot.blendmaskvib == otherSpot.blendmaskvib; + locallab.spots.at(j).radmaskvib = locallab.spots.at(j).radmaskvib && pSpot.radmaskvib == otherSpot.radmaskvib; + locallab.spots.at(j).chromaskvib = locallab.spots.at(j).chromaskvib && pSpot.chromaskvib == otherSpot.chromaskvib; + locallab.spots.at(j).gammaskvib = locallab.spots.at(j).gammaskvib && pSpot.gammaskvib == otherSpot.gammaskvib; + locallab.spots.at(j).slomaskvib = locallab.spots.at(j).slomaskvib && pSpot.slomaskvib == otherSpot.slomaskvib; + locallab.spots.at(j).lapmaskvib = locallab.spots.at(j).lapmaskvib && pSpot.lapmaskvib == otherSpot.lapmaskvib; + locallab.spots.at(j).strvib = locallab.spots.at(j).strvib && pSpot.strvib == otherSpot.strvib; + locallab.spots.at(j).strvibab = locallab.spots.at(j).strvibab && pSpot.strvibab == otherSpot.strvibab; + locallab.spots.at(j).strvibh = locallab.spots.at(j).strvibh && pSpot.strvibh == otherSpot.strvibh; + locallab.spots.at(j).angvib = locallab.spots.at(j).angvib && pSpot.angvib == otherSpot.angvib; + locallab.spots.at(j).Lmaskvibcurve = locallab.spots.at(j).Lmaskvibcurve && pSpot.Lmaskvibcurve == otherSpot.Lmaskvibcurve; + // Soft Light + locallab.spots.at(j).visisoft = locallab.spots.at(j).visisoft && pSpot.visisoft == otherSpot.visisoft; + locallab.spots.at(j).expsoft = locallab.spots.at(j).expsoft && pSpot.expsoft == otherSpot.expsoft; + locallab.spots.at(j).complexsoft = locallab.spots.at(j).complexsoft && pSpot.complexsoft == otherSpot.complexsoft; + locallab.spots.at(j).streng = locallab.spots.at(j).streng && pSpot.streng == otherSpot.streng; + locallab.spots.at(j).sensisf = locallab.spots.at(j).sensisf && pSpot.sensisf == otherSpot.sensisf; + locallab.spots.at(j).laplace = locallab.spots.at(j).laplace && pSpot.laplace == otherSpot.laplace; + locallab.spots.at(j).softMethod = locallab.spots.at(j).softMethod && pSpot.softMethod == otherSpot.softMethod; + // Blur & Noise + locallab.spots.at(j).visiblur = locallab.spots.at(j).visiblur && pSpot.visiblur == otherSpot.visiblur; + locallab.spots.at(j).expblur = locallab.spots.at(j).expblur && pSpot.expblur == otherSpot.expblur; + locallab.spots.at(j).complexblur = locallab.spots.at(j).complexblur && pSpot.complexblur == otherSpot.complexblur; + locallab.spots.at(j).radius = locallab.spots.at(j).radius && pSpot.radius == otherSpot.radius; + locallab.spots.at(j).strength = locallab.spots.at(j).strength && pSpot.strength == otherSpot.strength; + locallab.spots.at(j).sensibn = locallab.spots.at(j).sensibn && pSpot.sensibn == otherSpot.sensibn; + locallab.spots.at(j).itera = locallab.spots.at(j).itera && pSpot.itera == otherSpot.itera; + locallab.spots.at(j).guidbl = locallab.spots.at(j).guidbl && pSpot.guidbl == otherSpot.guidbl; + locallab.spots.at(j).strbl = locallab.spots.at(j).strbl && pSpot.strbl == otherSpot.strbl; + locallab.spots.at(j).isogr = locallab.spots.at(j).isogr && pSpot.isogr == otherSpot.isogr; + locallab.spots.at(j).strengr = locallab.spots.at(j).strengr && pSpot.strengr == otherSpot.strengr; + locallab.spots.at(j).scalegr = locallab.spots.at(j).scalegr && pSpot.scalegr == otherSpot.scalegr; + locallab.spots.at(j).epsbl = locallab.spots.at(j).epsbl && pSpot.epsbl == otherSpot.epsbl; + locallab.spots.at(j).blMethod = locallab.spots.at(j).blMethod && pSpot.blMethod == otherSpot.blMethod; + locallab.spots.at(j).chroMethod = locallab.spots.at(j).chroMethod && pSpot.chroMethod == otherSpot.chroMethod; + locallab.spots.at(j).blurMethod = locallab.spots.at(j).blurMethod && pSpot.blurMethod == otherSpot.blurMethod; + locallab.spots.at(j).medMethod = locallab.spots.at(j).medMethod && pSpot.medMethod == otherSpot.medMethod; + locallab.spots.at(j).activlum = locallab.spots.at(j).activlum && pSpot.activlum == otherSpot.activlum; + locallab.spots.at(j).noiselumf = locallab.spots.at(j).noiselumf && pSpot.noiselumf == otherSpot.noiselumf; + locallab.spots.at(j).noiselumf0 = locallab.spots.at(j).noiselumf0 && pSpot.noiselumf0 == otherSpot.noiselumf0; + locallab.spots.at(j).noiselumf2 = locallab.spots.at(j).noiselumf2 && pSpot.noiselumf2 == otherSpot.noiselumf2; + locallab.spots.at(j).noiselumc = locallab.spots.at(j).noiselumc && pSpot.noiselumc == otherSpot.noiselumc; + locallab.spots.at(j).noiselumdetail = locallab.spots.at(j).noiselumdetail && pSpot.noiselumdetail == otherSpot.noiselumdetail; + locallab.spots.at(j).noiselequal = locallab.spots.at(j).noiselequal && pSpot.noiselequal == otherSpot.noiselequal; + locallab.spots.at(j).noisechrof = locallab.spots.at(j).noisechrof && pSpot.noisechrof == otherSpot.noisechrof; + locallab.spots.at(j).noisechroc = locallab.spots.at(j).noisechroc && pSpot.noisechroc == otherSpot.noisechroc; + locallab.spots.at(j).noisechrodetail = locallab.spots.at(j).noisechrodetail && pSpot.noisechrodetail == otherSpot.noisechrodetail; + locallab.spots.at(j).adjblur = locallab.spots.at(j).adjblur && pSpot.adjblur == otherSpot.adjblur; + locallab.spots.at(j).bilateral = locallab.spots.at(j).bilateral && pSpot.bilateral == otherSpot.bilateral; + locallab.spots.at(j).sensiden = locallab.spots.at(j).sensiden && pSpot.sensiden == otherSpot.sensiden; + locallab.spots.at(j).detailthr = locallab.spots.at(j).detailthr && pSpot.detailthr == otherSpot.detailthr; + locallab.spots.at(j).locwavcurveden = locallab.spots.at(j).locwavcurveden && pSpot.locwavcurveden == otherSpot.locwavcurveden; + locallab.spots.at(j).showmaskblMethodtyp = locallab.spots.at(j).showmaskblMethodtyp && pSpot.showmaskblMethodtyp == otherSpot.showmaskblMethodtyp; + locallab.spots.at(j).CCmaskblcurve = locallab.spots.at(j).CCmaskblcurve && pSpot.CCmaskblcurve == otherSpot.CCmaskblcurve; + locallab.spots.at(j).LLmaskblcurve = locallab.spots.at(j).LLmaskblcurve && pSpot.LLmaskblcurve == otherSpot.LLmaskblcurve; + locallab.spots.at(j).HHmaskblcurve = locallab.spots.at(j).HHmaskblcurve && pSpot.HHmaskblcurve == otherSpot.HHmaskblcurve; + locallab.spots.at(j).enablMask = locallab.spots.at(j).enablMask && pSpot.enablMask == otherSpot.enablMask; + locallab.spots.at(j).fftwbl = locallab.spots.at(j).fftwbl && pSpot.fftwbl == otherSpot.fftwbl; + locallab.spots.at(j).toolbl = locallab.spots.at(j).toolbl && pSpot.toolbl == otherSpot.toolbl; + locallab.spots.at(j).blendmaskbl = locallab.spots.at(j).blendmaskbl && pSpot.blendmaskbl == otherSpot.blendmaskbl; + locallab.spots.at(j).radmaskbl = locallab.spots.at(j).radmaskbl && pSpot.radmaskbl == otherSpot.radmaskbl; + locallab.spots.at(j).chromaskbl = locallab.spots.at(j).chromaskbl && pSpot.chromaskbl == otherSpot.chromaskbl; + locallab.spots.at(j).gammaskbl = locallab.spots.at(j).gammaskbl && pSpot.gammaskbl == otherSpot.gammaskbl; + locallab.spots.at(j).slomaskbl = locallab.spots.at(j).slomaskbl && pSpot.slomaskbl == otherSpot.slomaskbl; + locallab.spots.at(j).lapmaskbl = locallab.spots.at(j).lapmaskbl && pSpot.lapmaskbl == otherSpot.lapmaskbl; + locallab.spots.at(j).shadmaskbl = locallab.spots.at(j).shadmaskbl && pSpot.shadmaskbl == otherSpot.shadmaskbl; + locallab.spots.at(j).shadmaskblsha = locallab.spots.at(j).shadmaskblsha && pSpot.shadmaskblsha == otherSpot.shadmaskblsha; + locallab.spots.at(j).strumaskbl = locallab.spots.at(j).strumaskbl && pSpot.strumaskbl == otherSpot.strumaskbl; + locallab.spots.at(j).Lmaskblcurve = locallab.spots.at(j).Lmaskblcurve && pSpot.Lmaskblcurve == otherSpot.Lmaskblcurve; + locallab.spots.at(j).LLmaskblcurvewav = locallab.spots.at(j).LLmaskblcurvewav && pSpot.LLmaskblcurvewav == otherSpot.LLmaskblcurvewav; + locallab.spots.at(j).csthresholdblur = locallab.spots.at(j).csthresholdblur && pSpot.csthresholdblur == otherSpot.csthresholdblur; + // Tone Mapping + locallab.spots.at(j).visitonemap = locallab.spots.at(j).visitonemap && pSpot.visitonemap == otherSpot.visitonemap; + locallab.spots.at(j).exptonemap = locallab.spots.at(j).exptonemap && pSpot.exptonemap == otherSpot.exptonemap; + locallab.spots.at(j).complextonemap = locallab.spots.at(j).complextonemap && pSpot.complextonemap == otherSpot.complextonemap; + locallab.spots.at(j).stren = locallab.spots.at(j).stren && pSpot.stren == otherSpot.stren; + locallab.spots.at(j).gamma = locallab.spots.at(j).gamma && pSpot.gamma == otherSpot.gamma; + locallab.spots.at(j).estop = locallab.spots.at(j).estop && pSpot.estop == otherSpot.estop; + locallab.spots.at(j).scaltm = locallab.spots.at(j).scaltm && pSpot.scaltm == otherSpot.scaltm; + locallab.spots.at(j).rewei = locallab.spots.at(j).rewei && pSpot.rewei == otherSpot.rewei; + locallab.spots.at(j).satur = locallab.spots.at(j).satur && pSpot.satur == otherSpot.satur; + locallab.spots.at(j).sensitm = locallab.spots.at(j).sensitm && pSpot.sensitm == otherSpot.sensitm; + locallab.spots.at(j).softradiustm = locallab.spots.at(j).softradiustm && pSpot.softradiustm == otherSpot.softradiustm; + locallab.spots.at(j).amount = locallab.spots.at(j).amount && pSpot.amount == otherSpot.amount; + locallab.spots.at(j).equiltm = locallab.spots.at(j).equiltm && pSpot.equiltm == otherSpot.equiltm; + locallab.spots.at(j).CCmasktmcurve = locallab.spots.at(j).CCmasktmcurve && pSpot.CCmasktmcurve == otherSpot.CCmasktmcurve; + locallab.spots.at(j).LLmasktmcurve = locallab.spots.at(j).LLmasktmcurve && pSpot.LLmasktmcurve == otherSpot.LLmasktmcurve; + locallab.spots.at(j).HHmasktmcurve = locallab.spots.at(j).HHmasktmcurve && pSpot.HHmasktmcurve == otherSpot.HHmasktmcurve; + locallab.spots.at(j).enatmMask = locallab.spots.at(j).enatmMask && pSpot.enatmMask == otherSpot.enatmMask; + locallab.spots.at(j).enatmMaskaft = locallab.spots.at(j).enatmMaskaft && pSpot.enatmMaskaft == otherSpot.enatmMaskaft; + locallab.spots.at(j).blendmasktm = locallab.spots.at(j).blendmasktm && pSpot.blendmasktm == otherSpot.blendmasktm; + locallab.spots.at(j).radmasktm = locallab.spots.at(j).radmasktm && pSpot.radmasktm == otherSpot.radmasktm; + locallab.spots.at(j).chromasktm = locallab.spots.at(j).chromasktm && pSpot.chromasktm == otherSpot.chromasktm; + locallab.spots.at(j).gammasktm = locallab.spots.at(j).gammasktm && pSpot.gammasktm == otherSpot.gammasktm; + locallab.spots.at(j).slomasktm = locallab.spots.at(j).slomasktm && pSpot.slomasktm == otherSpot.slomasktm; + locallab.spots.at(j).lapmasktm = locallab.spots.at(j).lapmasktm && pSpot.lapmasktm == otherSpot.lapmasktm; + locallab.spots.at(j).Lmasktmcurve = locallab.spots.at(j).Lmasktmcurve && pSpot.Lmasktmcurve == otherSpot.Lmasktmcurve; + // Retinex + locallab.spots.at(j).visireti = locallab.spots.at(j).visireti && pSpot.visireti == otherSpot.visireti; + locallab.spots.at(j).expreti = locallab.spots.at(j).expreti && pSpot.expreti == otherSpot.expreti; + locallab.spots.at(j).complexreti = locallab.spots.at(j).complexreti && pSpot.complexreti == otherSpot.complexreti; + locallab.spots.at(j).retinexMethod = locallab.spots.at(j).retinexMethod && pSpot.retinexMethod == otherSpot.retinexMethod; + locallab.spots.at(j).str = locallab.spots.at(j).str && pSpot.str == otherSpot.str; + locallab.spots.at(j).chrrt = locallab.spots.at(j).chrrt && pSpot.chrrt == otherSpot.chrrt; + locallab.spots.at(j).neigh = locallab.spots.at(j).neigh && pSpot.neigh == otherSpot.neigh; + locallab.spots.at(j).vart = locallab.spots.at(j).vart && pSpot.vart == otherSpot.vart; + locallab.spots.at(j).offs = locallab.spots.at(j).offs && pSpot.offs == otherSpot.offs; + locallab.spots.at(j).dehaz = locallab.spots.at(j).dehaz && pSpot.dehaz == otherSpot.dehaz; + locallab.spots.at(j).depth = locallab.spots.at(j).depth && pSpot.depth == otherSpot.depth; + locallab.spots.at(j).sensih = locallab.spots.at(j).sensih && pSpot.sensih == otherSpot.sensih; + locallab.spots.at(j).localTgaincurve = locallab.spots.at(j).localTgaincurve && pSpot.localTgaincurve == otherSpot.localTgaincurve; + locallab.spots.at(j).localTtranscurve = locallab.spots.at(j).localTtranscurve && pSpot.localTtranscurve == otherSpot.localTtranscurve; + locallab.spots.at(j).inversret = locallab.spots.at(j).inversret && pSpot.inversret == otherSpot.inversret; + locallab.spots.at(j).equilret = locallab.spots.at(j).equilret && pSpot.equilret == otherSpot.equilret; + locallab.spots.at(j).loglin = locallab.spots.at(j).loglin && pSpot.loglin == otherSpot.loglin; + locallab.spots.at(j).lumonly = locallab.spots.at(j).lumonly && pSpot.lumonly == otherSpot.lumonly; + locallab.spots.at(j).softradiusret = locallab.spots.at(j).softradiusret && pSpot.softradiusret == otherSpot.softradiusret; + locallab.spots.at(j).CCmaskreticurve = locallab.spots.at(j).CCmaskreticurve && pSpot.CCmaskreticurve == otherSpot.CCmaskreticurve; + locallab.spots.at(j).LLmaskreticurve = locallab.spots.at(j).LLmaskreticurve && pSpot.LLmaskreticurve == otherSpot.LLmaskreticurve; + locallab.spots.at(j).HHmaskreticurve = locallab.spots.at(j).HHmaskreticurve && pSpot.HHmaskreticurve == otherSpot.HHmaskreticurve; + locallab.spots.at(j).enaretiMask = locallab.spots.at(j).enaretiMask && pSpot.enaretiMask == otherSpot.enaretiMask; + locallab.spots.at(j).enaretiMasktmap = locallab.spots.at(j).enaretiMasktmap && pSpot.enaretiMasktmap == otherSpot.enaretiMasktmap; + locallab.spots.at(j).blendmaskreti = locallab.spots.at(j).blendmaskreti && pSpot.blendmaskreti == otherSpot.blendmaskreti; + locallab.spots.at(j).radmaskreti = locallab.spots.at(j).radmaskreti && pSpot.radmaskreti == otherSpot.radmaskreti; + locallab.spots.at(j).chromaskreti = locallab.spots.at(j).chromaskreti && pSpot.chromaskreti == otherSpot.chromaskreti; + locallab.spots.at(j).gammaskreti = locallab.spots.at(j).gammaskreti && pSpot.gammaskreti == otherSpot.gammaskreti; + locallab.spots.at(j).slomaskreti = locallab.spots.at(j).slomaskreti && pSpot.slomaskreti == otherSpot.slomaskreti; + locallab.spots.at(j).lapmaskreti = locallab.spots.at(j).lapmaskreti && pSpot.lapmaskreti == otherSpot.lapmaskreti; + locallab.spots.at(j).scalereti = locallab.spots.at(j).scalereti && pSpot.scalereti == otherSpot.scalereti; + locallab.spots.at(j).darkness = locallab.spots.at(j).darkness && pSpot.darkness == otherSpot.darkness; + locallab.spots.at(j).lightnessreti = locallab.spots.at(j).lightnessreti && pSpot.lightnessreti == otherSpot.lightnessreti; + locallab.spots.at(j).limd = locallab.spots.at(j).limd && pSpot.limd == otherSpot.limd; + locallab.spots.at(j).cliptm = locallab.spots.at(j).cliptm && pSpot.cliptm == otherSpot.cliptm; + locallab.spots.at(j).fftwreti = locallab.spots.at(j).fftwreti && pSpot.fftwreti == otherSpot.fftwreti; + locallab.spots.at(j).Lmaskreticurve = locallab.spots.at(j).Lmaskreticurve && pSpot.Lmaskreticurve == otherSpot.Lmaskreticurve; + // Sharpening + locallab.spots.at(j).visisharp = locallab.spots.at(j).visisharp && pSpot.visisharp == otherSpot.visisharp; + locallab.spots.at(j).expsharp = locallab.spots.at(j).expsharp && pSpot.expsharp == otherSpot.expsharp; + locallab.spots.at(j).complexsharp = locallab.spots.at(j).complexsharp && pSpot.complexsharp == otherSpot.complexsharp; + locallab.spots.at(j).sharcontrast = locallab.spots.at(j).sharcontrast && pSpot.sharcontrast == otherSpot.sharcontrast; + locallab.spots.at(j).sharradius = locallab.spots.at(j).sharradius && pSpot.sharradius == otherSpot.sharradius; + locallab.spots.at(j).sharamount = locallab.spots.at(j).sharamount && pSpot.sharamount == otherSpot.sharamount; + locallab.spots.at(j).shardamping = locallab.spots.at(j).shardamping && pSpot.shardamping == otherSpot.shardamping; + locallab.spots.at(j).shariter = locallab.spots.at(j).shariter && pSpot.shariter == otherSpot.shariter; + locallab.spots.at(j).sharblur = locallab.spots.at(j).sharblur && pSpot.sharblur == otherSpot.sharblur; + locallab.spots.at(j).sensisha = locallab.spots.at(j).sensisha && pSpot.sensisha == otherSpot.sensisha; + locallab.spots.at(j).inverssha = locallab.spots.at(j).inverssha && pSpot.inverssha == otherSpot.inverssha; + // Local Contrast + locallab.spots.at(j).visicontrast = locallab.spots.at(j).visicontrast && pSpot.visicontrast == otherSpot.visicontrast; + locallab.spots.at(j).expcontrast = locallab.spots.at(j).expcontrast && pSpot.expcontrast == otherSpot.expcontrast; + locallab.spots.at(j).complexcontrast = locallab.spots.at(j).complexcontrast && pSpot.complexcontrast == otherSpot.complexcontrast; + locallab.spots.at(j).lcradius = locallab.spots.at(j).lcradius && pSpot.lcradius == otherSpot.lcradius; + locallab.spots.at(j).lcamount = locallab.spots.at(j).lcamount && pSpot.lcamount == otherSpot.lcamount; + locallab.spots.at(j).lcdarkness = locallab.spots.at(j).lcdarkness && pSpot.lcdarkness == otherSpot.lcdarkness; + locallab.spots.at(j).lclightness = locallab.spots.at(j).lclightness && pSpot.lclightness == otherSpot.lclightness; + locallab.spots.at(j).sigmalc = locallab.spots.at(j).sigmalc && pSpot.sigmalc == otherSpot.sigmalc; + locallab.spots.at(j).levelwav = locallab.spots.at(j).levelwav && pSpot.levelwav == otherSpot.levelwav; + locallab.spots.at(j).residcont = locallab.spots.at(j).residcont && pSpot.residcont == otherSpot.residcont; + locallab.spots.at(j).residsha = locallab.spots.at(j).residsha && pSpot.residsha == otherSpot.residsha; + locallab.spots.at(j).residshathr = locallab.spots.at(j).residshathr && pSpot.residshathr == otherSpot.residshathr; + locallab.spots.at(j).residhi = locallab.spots.at(j).residhi && pSpot.residhi == otherSpot.residhi; + locallab.spots.at(j).residhithr = locallab.spots.at(j).residhithr && pSpot.residhithr == otherSpot.residhithr; + locallab.spots.at(j).residblur = locallab.spots.at(j).residblur && pSpot.residblur == otherSpot.residblur; + locallab.spots.at(j).levelblur = locallab.spots.at(j).levelblur && pSpot.levelblur == otherSpot.levelblur; + locallab.spots.at(j).sigmabl = locallab.spots.at(j).sigmabl && pSpot.sigmabl == otherSpot.sigmabl; + locallab.spots.at(j).residchro = locallab.spots.at(j).residchro && pSpot.residchro == otherSpot.residchro; + locallab.spots.at(j).residcomp = locallab.spots.at(j).residcomp && pSpot.residcomp == otherSpot.residcomp; + locallab.spots.at(j).sigma = locallab.spots.at(j).sigma && pSpot.sigma == otherSpot.sigma; + locallab.spots.at(j).offset = locallab.spots.at(j).offset && pSpot.offset == otherSpot.offset; + locallab.spots.at(j).sigmadr = locallab.spots.at(j).sigmadr && pSpot.sigmadr == otherSpot.sigmadr; + locallab.spots.at(j).threswav = locallab.spots.at(j).threswav && pSpot.threswav == otherSpot.threswav; + locallab.spots.at(j).chromalev = locallab.spots.at(j).chromalev && pSpot.chromalev == otherSpot.chromalev; + locallab.spots.at(j).chromablu = locallab.spots.at(j).chromablu && pSpot.chromablu == otherSpot.chromablu; + locallab.spots.at(j).sigmadc = locallab.spots.at(j).sigmadc && pSpot.sigmadc == otherSpot.sigmadc; + locallab.spots.at(j).deltad = locallab.spots.at(j).deltad && pSpot.deltad == otherSpot.deltad; + locallab.spots.at(j).fatres = locallab.spots.at(j).fatres && pSpot.fatres == otherSpot.fatres; + locallab.spots.at(j).clarilres = locallab.spots.at(j).clarilres && pSpot.clarilres == otherSpot.clarilres; + locallab.spots.at(j).claricres = locallab.spots.at(j).claricres && pSpot.claricres == otherSpot.claricres; + locallab.spots.at(j).clarisoft = locallab.spots.at(j).clarisoft && pSpot.clarisoft == otherSpot.clarisoft; + locallab.spots.at(j).sigmalc2 = locallab.spots.at(j).sigmalc2 && pSpot.sigmalc2 == otherSpot.sigmalc2; + locallab.spots.at(j).strwav = locallab.spots.at(j).strwav && pSpot.strwav == otherSpot.strwav; + locallab.spots.at(j).angwav = locallab.spots.at(j).angwav && pSpot.angwav == otherSpot.angwav; + locallab.spots.at(j).strengthw = locallab.spots.at(j).strengthw && pSpot.strengthw == otherSpot.strengthw; + locallab.spots.at(j).sigmaed = locallab.spots.at(j).sigmaed && pSpot.sigmaed == otherSpot.sigmaed; + locallab.spots.at(j).radiusw = locallab.spots.at(j).radiusw && pSpot.radiusw == otherSpot.radiusw; + locallab.spots.at(j).detailw = locallab.spots.at(j).detailw && pSpot.detailw == otherSpot.detailw; + locallab.spots.at(j).gradw = locallab.spots.at(j).gradw && pSpot.gradw == otherSpot.gradw; + locallab.spots.at(j).tloww = locallab.spots.at(j).tloww && pSpot.tloww == otherSpot.tloww; + locallab.spots.at(j).thigw = locallab.spots.at(j).thigw && pSpot.thigw == otherSpot.thigw; + locallab.spots.at(j).edgw = locallab.spots.at(j).edgw && pSpot.edgw == otherSpot.edgw; + locallab.spots.at(j).basew = locallab.spots.at(j).basew && pSpot.basew == otherSpot.basew; + locallab.spots.at(j).sensilc = locallab.spots.at(j).sensilc && pSpot.sensilc == otherSpot.sensilc; + locallab.spots.at(j).fftwlc = locallab.spots.at(j).fftwlc && pSpot.fftwlc == otherSpot.fftwlc; + locallab.spots.at(j).blurlc = locallab.spots.at(j).blurlc && pSpot.blurlc == otherSpot.blurlc; + locallab.spots.at(j).wavblur = locallab.spots.at(j).wavblur && pSpot.wavblur == otherSpot.wavblur; + locallab.spots.at(j).wavedg = locallab.spots.at(j).wavedg && pSpot.wavedg == otherSpot.wavedg; + locallab.spots.at(j).waveshow = locallab.spots.at(j).waveshow && pSpot.waveshow == otherSpot.waveshow; + locallab.spots.at(j).wavcont = locallab.spots.at(j).wavcont && pSpot.wavcont == otherSpot.wavcont; + locallab.spots.at(j).wavcomp = locallab.spots.at(j).wavcomp && pSpot.wavcomp == otherSpot.wavcomp; + locallab.spots.at(j).wavgradl = locallab.spots.at(j).wavgradl && pSpot.wavgradl == otherSpot.wavgradl; + locallab.spots.at(j).wavcompre = locallab.spots.at(j).wavcompre && pSpot.wavcompre == otherSpot.wavcompre; + locallab.spots.at(j).origlc = locallab.spots.at(j).origlc && pSpot.origlc == otherSpot.origlc; + locallab.spots.at(j).localcontMethod = locallab.spots.at(j).localcontMethod && pSpot.localcontMethod == otherSpot.localcontMethod; + locallab.spots.at(j).localedgMethod = locallab.spots.at(j).localedgMethod && pSpot.localedgMethod == otherSpot.localedgMethod; + locallab.spots.at(j).localneiMethod = locallab.spots.at(j).localneiMethod && pSpot.localneiMethod == otherSpot.localneiMethod; + locallab.spots.at(j).locwavcurve = locallab.spots.at(j).locwavcurve && pSpot.locwavcurve == otherSpot.locwavcurve; + locallab.spots.at(j).csthreshold = locallab.spots.at(j).csthreshold && pSpot.csthreshold == otherSpot.csthreshold; + locallab.spots.at(j).loclevwavcurve = locallab.spots.at(j).loclevwavcurve && pSpot.loclevwavcurve == otherSpot.loclevwavcurve; + locallab.spots.at(j).locconwavcurve = locallab.spots.at(j).locconwavcurve && pSpot.locconwavcurve == otherSpot.locconwavcurve; + locallab.spots.at(j).loccompwavcurve = locallab.spots.at(j).loccompwavcurve && pSpot.loccompwavcurve == otherSpot.loccompwavcurve; + locallab.spots.at(j).loccomprewavcurve = locallab.spots.at(j).loccomprewavcurve && pSpot.loccomprewavcurve == otherSpot.loccomprewavcurve; + locallab.spots.at(j).locedgwavcurve = locallab.spots.at(j).locedgwavcurve && pSpot.locedgwavcurve == otherSpot.locedgwavcurve; + locallab.spots.at(j).CCmasklccurve = locallab.spots.at(j).CCmasklccurve && pSpot.CCmasklccurve == otherSpot.CCmasklccurve; + locallab.spots.at(j).LLmasklccurve = locallab.spots.at(j).LLmasklccurve && pSpot.LLmasklccurve == otherSpot.LLmasklccurve; + locallab.spots.at(j).HHmasklccurve = locallab.spots.at(j).HHmasklccurve && pSpot.HHmasklccurve == otherSpot.HHmasklccurve; + locallab.spots.at(j).enalcMask = locallab.spots.at(j).enalcMask && pSpot.enalcMask == otherSpot.enalcMask; + locallab.spots.at(j).blendmasklc = locallab.spots.at(j).blendmasklc && pSpot.blendmasklc == otherSpot.blendmasklc; + locallab.spots.at(j).radmasklc = locallab.spots.at(j).radmasklc && pSpot.radmaskcb == otherSpot.radmasklc; + locallab.spots.at(j).chromasklc = locallab.spots.at(j).chromasklc && pSpot.chromasklc == otherSpot.chromasklc; + locallab.spots.at(j).Lmasklccurve = locallab.spots.at(j).Lmasklccurve && pSpot.Lmasklccurve == otherSpot.Lmasklccurve; + // Contrast by detail levels + locallab.spots.at(j).visicbdl = locallab.spots.at(j).visicbdl && pSpot.visicbdl == otherSpot.visicbdl; + locallab.spots.at(j).expcbdl = locallab.spots.at(j).expcbdl && pSpot.expcbdl == otherSpot.expcbdl; + locallab.spots.at(j).complexcbdl = locallab.spots.at(j).complexcbdl && pSpot.complexcbdl == otherSpot.complexcbdl; + + for (int k = 0; k < 6; k++) { + locallab.spots.at(j).mult[k] = locallab.spots.at(j).mult[k] && pSpot.mult[k] == otherSpot.mult[k]; + } + + locallab.spots.at(j).chromacbdl = locallab.spots.at(j).chromacbdl && pSpot.chromacbdl == otherSpot.chromacbdl; + locallab.spots.at(j).threshold = locallab.spots.at(j).threshold && pSpot.threshold == otherSpot.threshold; + locallab.spots.at(j).sensicb = locallab.spots.at(j).sensicb && pSpot.sensicb == otherSpot.sensicb; + locallab.spots.at(j).clarityml = locallab.spots.at(j).clarityml && pSpot.clarityml == otherSpot.clarityml; + locallab.spots.at(j).contresid = locallab.spots.at(j).contresid && pSpot.contresid == otherSpot.contresid; + locallab.spots.at(j).blurcbdl = locallab.spots.at(j).blurcbdl && pSpot.blurcbdl == otherSpot.blurcbdl; + locallab.spots.at(j).softradiuscb = locallab.spots.at(j).softradiuscb && pSpot.softradiuscb == otherSpot.softradiuscb; + locallab.spots.at(j).enacbMask = locallab.spots.at(j).enacbMask && pSpot.enacbMask == otherSpot.enacbMask; + locallab.spots.at(j).CCmaskcbcurve = locallab.spots.at(j).CCmaskcbcurve && pSpot.CCmaskcbcurve == otherSpot.CCmaskcbcurve; + locallab.spots.at(j).LLmaskcbcurve = locallab.spots.at(j).LLmaskcbcurve && pSpot.LLmaskcbcurve == otherSpot.LLmaskcbcurve; + locallab.spots.at(j).HHmaskcbcurve = locallab.spots.at(j).HHmaskcbcurve && pSpot.HHmaskcbcurve == otherSpot.HHmaskcbcurve; + locallab.spots.at(j).blendmaskcb = locallab.spots.at(j).blendmaskcb && pSpot.blendmaskcb == otherSpot.blendmaskcb; + locallab.spots.at(j).radmaskcb = locallab.spots.at(j).radmaskcb && pSpot.radmaskcb == otherSpot.radmaskcb; + locallab.spots.at(j).chromaskcb = locallab.spots.at(j).chromaskcb && pSpot.chromaskcb == otherSpot.chromaskcb; + locallab.spots.at(j).gammaskcb = locallab.spots.at(j).gammaskcb && pSpot.gammaskcb == otherSpot.gammaskcb; + locallab.spots.at(j).slomaskcb = locallab.spots.at(j).slomaskcb && pSpot.slomaskcb == otherSpot.slomaskcb; + locallab.spots.at(j).lapmaskcb = locallab.spots.at(j).lapmaskcb && pSpot.lapmaskcb == otherSpot.lapmaskcb; + locallab.spots.at(j).Lmaskcbcurve = locallab.spots.at(j).Lmaskcbcurve && pSpot.Lmaskcbcurve == otherSpot.Lmaskcbcurve; + // Log encoding + locallab.spots.at(j).visilog = locallab.spots.at(j).visilog && pSpot.visilog == otherSpot.visilog; + locallab.spots.at(j).explog = locallab.spots.at(j).explog && pSpot.explog == otherSpot.explog; + locallab.spots.at(j).autocompute = locallab.spots.at(j).autocompute && pSpot.autocompute == otherSpot.autocompute; + locallab.spots.at(j).sourceGray = locallab.spots.at(j).sourceGray && pSpot.sourceGray == otherSpot.sourceGray; + locallab.spots.at(j).targetGray = locallab.spots.at(j).targetGray && pSpot.targetGray == otherSpot.targetGray; + locallab.spots.at(j).Autogray = locallab.spots.at(j).Autogray && pSpot.Autogray == otherSpot.Autogray; + locallab.spots.at(j).fullimage = locallab.spots.at(j).fullimage && pSpot.fullimage == otherSpot.fullimage; + locallab.spots.at(j).blackEv = locallab.spots.at(j).blackEv && pSpot.blackEv == otherSpot.blackEv; + locallab.spots.at(j).whiteEv = locallab.spots.at(j).whiteEv && pSpot.whiteEv == otherSpot.whiteEv; + locallab.spots.at(j).detail = locallab.spots.at(j).detail && pSpot.detail == otherSpot.detail; + locallab.spots.at(j).sensilog = locallab.spots.at(j).sensilog && pSpot.sensilog == otherSpot.sensilog; + locallab.spots.at(j).baselog = locallab.spots.at(j).baselog && pSpot.baselog == otherSpot.baselog; + locallab.spots.at(j).strlog = locallab.spots.at(j).strlog && pSpot.strlog == otherSpot.strlog; + locallab.spots.at(j).anglog = locallab.spots.at(j).anglog && pSpot.anglog == otherSpot.anglog; + } + } + + if (!isSpotNumberEqual) { + // All LocallabSpotEdited are set to false because cannot be combined + locallab.spots.clear(); + locallab.spots.resize(p.locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(false)); + } + pcvignette.enabled = pcvignette.enabled && p.pcvignette.enabled == other.pcvignette.enabled; pcvignette.strength = pcvignette.strength && p.pcvignette.strength == other.pcvignette.strength; pcvignette.feather = pcvignette.feather && p.pcvignette.feather == other.pcvignette.feather; @@ -1707,7 +2222,7 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (colorToning.labregionsShowMask) { toEdit.colorToning.labregionsShowMask = mods.colorToning.labregionsShowMask; } - + if (sharpenEdge.enabled) { toEdit.sharpenEdge.enabled = mods.sharpenEdge.enabled; } @@ -2411,6 +2926,10 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.lensProf.lfLens = mods.lensProf.lfLens; } + if (perspective.method) { + toEdit.perspective.method = mods.perspective.method; + } + if (perspective.horizontal) { toEdit.perspective.horizontal = dontforceSet && options.baBehav[ADDSET_PERSPECTIVE] ? toEdit.perspective.horizontal + mods.perspective.horizontal : mods.perspective.horizontal; } @@ -2419,6 +2938,54 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.perspective.vertical = dontforceSet && options.baBehav[ADDSET_PERSPECTIVE] ? toEdit.perspective.vertical + mods.perspective.vertical : mods.perspective.vertical; } + if (perspective.camera_crop_factor) { + toEdit.perspective.camera_crop_factor = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_FOCAL_LENGTH] ? toEdit.perspective.camera_crop_factor + mods.perspective.camera_crop_factor : mods.perspective.camera_crop_factor; + } + + if (perspective.camera_focal_length) { + toEdit.perspective.camera_focal_length = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_FOCAL_LENGTH] ? toEdit.perspective.camera_focal_length + mods.perspective.camera_focal_length : mods.perspective.camera_focal_length; + } + + if (perspective.camera_pitch) { + toEdit.perspective.camera_pitch = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_ANGLE] ? toEdit.perspective.camera_pitch + mods.perspective.camera_pitch : mods.perspective.camera_pitch; + } + + if (perspective.camera_roll) { + toEdit.perspective.camera_roll = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_ANGLE] ? toEdit.perspective.camera_roll + mods.perspective.camera_roll : mods.perspective.camera_roll; + } + + if (perspective.camera_shift_horiz) { + toEdit.perspective.camera_shift_horiz = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_SHIFT] ? toEdit.perspective.camera_shift_horiz + mods.perspective.camera_shift_horiz : mods.perspective.camera_shift_horiz; + } + + if (perspective.camera_shift_vert) { + toEdit.perspective.camera_shift_vert = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_SHIFT] ? toEdit.perspective.camera_shift_vert + mods.perspective.camera_shift_vert : mods.perspective.camera_shift_vert; + } + + if (perspective.camera_yaw) { + toEdit.perspective.camera_yaw = dontforceSet && options.baBehav[ADDSET_PERSP_CAM_ANGLE] ? toEdit.perspective.camera_yaw + mods.perspective.camera_yaw : mods.perspective.camera_yaw; + } + + if (perspective.projection_pitch) { + toEdit.perspective.projection_pitch = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_ANGLE] ? toEdit.perspective.projection_pitch + mods.perspective.projection_pitch : mods.perspective.projection_pitch; + } + + if (perspective.projection_rotate) { + toEdit.perspective.projection_rotate = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_ROTATE] ? toEdit.perspective.projection_rotate + mods.perspective.projection_rotate : mods.perspective.projection_rotate; + } + + if (perspective.projection_shift_horiz) { + toEdit.perspective.projection_shift_horiz = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_SHIFT] ? toEdit.perspective.projection_shift_horiz + mods.perspective.projection_shift_horiz : mods.perspective.projection_shift_horiz; + } + + if (perspective.projection_shift_vert) { + toEdit.perspective.projection_shift_vert = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_SHIFT] ? toEdit.perspective.projection_shift_vert + mods.perspective.projection_shift_vert : mods.perspective.projection_shift_vert; + } + + if (perspective.projection_yaw) { + toEdit.perspective.projection_yaw = dontforceSet && options.baBehav[ADDSET_PERSP_PROJ_ANGLE] ? toEdit.perspective.projection_yaw + mods.perspective.projection_yaw : mods.perspective.projection_yaw; + } + if (gradient.enabled) { toEdit.gradient.enabled = mods.gradient.enabled; } @@ -2443,6 +3010,1779 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng toEdit.gradient.centerY = dontforceSet && options.baBehav[ADDSET_GRADIENT_CENTER] ? toEdit.gradient.centerY + mods.gradient.centerY : mods.gradient.centerY; } + + if (locallab.enabled) { + toEdit.locallab.enabled = mods.locallab.enabled; + } + + if (locallab.selspot) { + toEdit.locallab.selspot = mods.locallab.selspot; + } + + // Resizing locallab spots vector according to pedited + toEdit.locallab.spots.resize(locallab.spots.size()); + + // Updating each locallab spot according to pedited + for (size_t i = 0; i < toEdit.locallab.spots.size() && i < mods.locallab.spots.size() && i < locallab.spots.size(); i++) { + // Control spot settings + if (locallab.spots.at(i).name) { + toEdit.locallab.spots.at(i).name = mods.locallab.spots.at(i).name; + } + + if (locallab.spots.at(i).isvisible) { + toEdit.locallab.spots.at(i).isvisible = mods.locallab.spots.at(i).isvisible; + } + + if (locallab.spots.at(i).shape) { + toEdit.locallab.spots.at(i).shape = mods.locallab.spots.at(i).shape; + } + + if (locallab.spots.at(i).spotMethod) { + toEdit.locallab.spots.at(i).spotMethod = mods.locallab.spots.at(i).spotMethod; + } + + if (locallab.spots.at(i).wavMethod) { + toEdit.locallab.spots.at(i).wavMethod = mods.locallab.spots.at(i).wavMethod; + } + + if (locallab.spots.at(i).sensiexclu) { + toEdit.locallab.spots.at(i).sensiexclu = mods.locallab.spots.at(i).sensiexclu; + } + + if (locallab.spots.at(i).structexclu) { + toEdit.locallab.spots.at(i).structexclu = mods.locallab.spots.at(i).structexclu; + } + + if (locallab.spots.at(i).struc) { + toEdit.locallab.spots.at(i).struc = mods.locallab.spots.at(i).struc; + } + + if (locallab.spots.at(i).shapeMethod) { + toEdit.locallab.spots.at(i).shapeMethod = mods.locallab.spots.at(i).shapeMethod; + } + + if (locallab.spots.at(i).loc) { + toEdit.locallab.spots.at(i).loc = mods.locallab.spots.at(i).loc; + } + + if (locallab.spots.at(i).centerX) { + toEdit.locallab.spots.at(i).centerX = mods.locallab.spots.at(i).centerX; + } + + if (locallab.spots.at(i).centerY) { + toEdit.locallab.spots.at(i).centerY = mods.locallab.spots.at(i).centerY; + } + + if (locallab.spots.at(i).circrad) { + toEdit.locallab.spots.at(i).circrad = mods.locallab.spots.at(i).circrad; + } + + if (locallab.spots.at(i).qualityMethod) { + toEdit.locallab.spots.at(i).qualityMethod = mods.locallab.spots.at(i).qualityMethod; + } + + if (locallab.spots.at(i).complexMethod) { + toEdit.locallab.spots.at(i).complexMethod = mods.locallab.spots.at(i).complexMethod; + } + + if (locallab.spots.at(i).transit) { + toEdit.locallab.spots.at(i).transit = mods.locallab.spots.at(i).transit; + } + + if (locallab.spots.at(i).feather) { + toEdit.locallab.spots.at(i).feather = mods.locallab.spots.at(i).feather; + } + + if (locallab.spots.at(i).thresh) { + toEdit.locallab.spots.at(i).thresh = mods.locallab.spots.at(i).thresh; + } + + if (locallab.spots.at(i).iter) { + toEdit.locallab.spots.at(i).iter = mods.locallab.spots.at(i).iter; + } + + if (locallab.spots.at(i).balan) { + toEdit.locallab.spots.at(i).balan = mods.locallab.spots.at(i).balan; + } + + if (locallab.spots.at(i).balanh) { + toEdit.locallab.spots.at(i).balanh = mods.locallab.spots.at(i).balanh; + } + + if (locallab.spots.at(i).colorde) { + toEdit.locallab.spots.at(i).colorde = mods.locallab.spots.at(i).colorde; + } + + if (locallab.spots.at(i).colorscope) { + toEdit.locallab.spots.at(i).colorscope = mods.locallab.spots.at(i).colorscope; + } + + if (locallab.spots.at(i).transitweak) { + toEdit.locallab.spots.at(i).transitweak = mods.locallab.spots.at(i).transitweak; + } + + if (locallab.spots.at(i).transitgrad) { + toEdit.locallab.spots.at(i).transitgrad = mods.locallab.spots.at(i).transitgrad; + } + + if (locallab.spots.at(i).avoid) { + toEdit.locallab.spots.at(i).avoid = mods.locallab.spots.at(i).avoid; + } + + if (locallab.spots.at(i).blwh) { + toEdit.locallab.spots.at(i).blwh = mods.locallab.spots.at(i).blwh; + } + + if (locallab.spots.at(i).recurs) { + toEdit.locallab.spots.at(i).recurs = mods.locallab.spots.at(i).recurs; + } + + if (locallab.spots.at(i).laplac) { + toEdit.locallab.spots.at(i).laplac = mods.locallab.spots.at(i).laplac; + } + + if (locallab.spots.at(i).deltae) { + toEdit.locallab.spots.at(i).deltae = mods.locallab.spots.at(i).deltae; + } + + if (locallab.spots.at(i).shortc) { + toEdit.locallab.spots.at(i).shortc = mods.locallab.spots.at(i).shortc; + } + + if (locallab.spots.at(i).savrest) { + toEdit.locallab.spots.at(i).savrest = mods.locallab.spots.at(i).savrest; + } + + if (locallab.spots.at(i).scopemask) { + toEdit.locallab.spots.at(i).scopemask = mods.locallab.spots.at(i).scopemask; + } + + if (locallab.spots.at(i).lumask) { + toEdit.locallab.spots.at(i).lumask = mods.locallab.spots.at(i).lumask; + } + + // Color & Light + if (locallab.spots.at(i).visicolor) { + toEdit.locallab.spots.at(i).visicolor = mods.locallab.spots.at(i).visicolor; + } + + if (locallab.spots.at(i).expcolor) { + toEdit.locallab.spots.at(i).expcolor = mods.locallab.spots.at(i).expcolor; + } + + if (locallab.spots.at(i).complexcolor) { + toEdit.locallab.spots.at(i).complexcolor = mods.locallab.spots.at(i).complexcolor; + } + + if (locallab.spots.at(i).curvactiv) { + toEdit.locallab.spots.at(i).curvactiv = mods.locallab.spots.at(i).curvactiv; + } + + if (locallab.spots.at(i).lightness) { + toEdit.locallab.spots.at(i).lightness = mods.locallab.spots.at(i).lightness; + } + + if (locallab.spots.at(i).contrast) { + toEdit.locallab.spots.at(i).contrast = mods.locallab.spots.at(i).contrast; + } + + if (locallab.spots.at(i).chroma) { + toEdit.locallab.spots.at(i).chroma = mods.locallab.spots.at(i).chroma; + } + + if (locallab.spots.at(i).labgridALow) { + toEdit.locallab.spots.at(i).labgridALow = mods.locallab.spots.at(i).labgridALow; + } + + if (locallab.spots.at(i).labgridBLow) { + toEdit.locallab.spots.at(i).labgridBLow = mods.locallab.spots.at(i).labgridBLow; + } + + if (locallab.spots.at(i).labgridAHigh) { + toEdit.locallab.spots.at(i).labgridAHigh = mods.locallab.spots.at(i).labgridAHigh; + } + + if (locallab.spots.at(i).labgridBHigh) { + toEdit.locallab.spots.at(i).labgridBHigh = mods.locallab.spots.at(i).labgridBHigh; + } + + if (locallab.spots.at(i).labgridALowmerg) { + toEdit.locallab.spots.at(i).labgridALowmerg = mods.locallab.spots.at(i).labgridALowmerg; + } + + if (locallab.spots.at(i).labgridBLowmerg) { + toEdit.locallab.spots.at(i).labgridBLowmerg = mods.locallab.spots.at(i).labgridBLowmerg; + } + + if (locallab.spots.at(i).labgridAHighmerg) { + toEdit.locallab.spots.at(i).labgridAHighmerg = mods.locallab.spots.at(i).labgridAHighmerg; + } + + if (locallab.spots.at(i).labgridBHighmerg) { + toEdit.locallab.spots.at(i).labgridBHighmerg = mods.locallab.spots.at(i).labgridBHighmerg; + } + + if (locallab.spots.at(i).strengthgrid) { + toEdit.locallab.spots.at(i).strengthgrid = mods.locallab.spots.at(i).strengthgrid; + } + + if (locallab.spots.at(i).sensi) { + toEdit.locallab.spots.at(i).sensi = mods.locallab.spots.at(i).sensi; + } + + if (locallab.spots.at(i).structcol) { + toEdit.locallab.spots.at(i).structcol = mods.locallab.spots.at(i).structcol; + } + + if (locallab.spots.at(i).strcol) { + toEdit.locallab.spots.at(i).strcol = mods.locallab.spots.at(i).strcol; + } + + if (locallab.spots.at(i).strcolab) { + toEdit.locallab.spots.at(i).strcolab = mods.locallab.spots.at(i).strcolab; + } + + if (locallab.spots.at(i).strcolh) { + toEdit.locallab.spots.at(i).strcolh = mods.locallab.spots.at(i).strcolh; + } + + if (locallab.spots.at(i).angcol) { + toEdit.locallab.spots.at(i).angcol = mods.locallab.spots.at(i).angcol; + } + + if (locallab.spots.at(i).blurcolde) { + toEdit.locallab.spots.at(i).blurcolde = mods.locallab.spots.at(i).blurcolde; + } + + if (locallab.spots.at(i).blurcol) { + toEdit.locallab.spots.at(i).blurcol = mods.locallab.spots.at(i).blurcol; + } + + if (locallab.spots.at(i).contcol) { + toEdit.locallab.spots.at(i).contcol = mods.locallab.spots.at(i).contcol; + } + + if (locallab.spots.at(i).blendmaskcol) { + toEdit.locallab.spots.at(i).blendmaskcol = mods.locallab.spots.at(i).blendmaskcol; + } + + if (locallab.spots.at(i).radmaskcol) { + toEdit.locallab.spots.at(i).radmaskcol = mods.locallab.spots.at(i).radmaskcol; + } + + if (locallab.spots.at(i).chromaskcol) { + toEdit.locallab.spots.at(i).chromaskcol = mods.locallab.spots.at(i).chromaskcol; + } + + if (locallab.spots.at(i).gammaskcol) { + toEdit.locallab.spots.at(i).gammaskcol = mods.locallab.spots.at(i).gammaskcol; + } + + if (locallab.spots.at(i).slomaskcol) { + toEdit.locallab.spots.at(i).slomaskcol = mods.locallab.spots.at(i).slomaskcol; + } + + if (locallab.spots.at(i).shadmaskcol) { + toEdit.locallab.spots.at(i).shadmaskcol = mods.locallab.spots.at(i).shadmaskcol; + } + + if (locallab.spots.at(i).strumaskcol) { + toEdit.locallab.spots.at(i).strumaskcol = mods.locallab.spots.at(i).strumaskcol; + } + + if (locallab.spots.at(i).lapmaskcol) { + toEdit.locallab.spots.at(i).lapmaskcol = mods.locallab.spots.at(i).lapmaskcol; + } + + if (locallab.spots.at(i).qualitycurveMethod) { + toEdit.locallab.spots.at(i).qualitycurveMethod = mods.locallab.spots.at(i).qualitycurveMethod; + } + + if (locallab.spots.at(i).gridMethod) { + toEdit.locallab.spots.at(i).gridMethod = mods.locallab.spots.at(i).gridMethod; + } + + if (locallab.spots.at(i).merMethod) { + toEdit.locallab.spots.at(i).merMethod = mods.locallab.spots.at(i).merMethod; + } + + if (locallab.spots.at(i).toneMethod) { + toEdit.locallab.spots.at(i).toneMethod = mods.locallab.spots.at(i).toneMethod; + } + + if (locallab.spots.at(i).mergecolMethod) { + toEdit.locallab.spots.at(i).mergecolMethod = mods.locallab.spots.at(i).mergecolMethod; + } + + if (locallab.spots.at(i).llcurve) { + toEdit.locallab.spots.at(i).llcurve = mods.locallab.spots.at(i).llcurve; + } + + if (locallab.spots.at(i).lccurve) { + toEdit.locallab.spots.at(i).lccurve = mods.locallab.spots.at(i).lccurve; + } + + if (locallab.spots.at(i).cccurve) { + toEdit.locallab.spots.at(i).cccurve = mods.locallab.spots.at(i).cccurve; + } + + if (locallab.spots.at(i).clcurve) { + toEdit.locallab.spots.at(i).clcurve = mods.locallab.spots.at(i).clcurve; + } + + if (locallab.spots.at(i).rgbcurve) { + toEdit.locallab.spots.at(i).rgbcurve = mods.locallab.spots.at(i).rgbcurve; + } + + if (locallab.spots.at(i).LHcurve) { + toEdit.locallab.spots.at(i).LHcurve = mods.locallab.spots.at(i).LHcurve; + } + + if (locallab.spots.at(i).HHcurve) { + toEdit.locallab.spots.at(i).HHcurve = mods.locallab.spots.at(i).HHcurve; + } + + if (locallab.spots.at(i).invers) { + toEdit.locallab.spots.at(i).invers = mods.locallab.spots.at(i).invers; + } + + if (locallab.spots.at(i).special) { + toEdit.locallab.spots.at(i).special = mods.locallab.spots.at(i).special; + } + + if (locallab.spots.at(i).toolcol) { + toEdit.locallab.spots.at(i).toolcol = mods.locallab.spots.at(i).toolcol; + } + + if (locallab.spots.at(i).enaColorMask) { + toEdit.locallab.spots.at(i).enaColorMask = mods.locallab.spots.at(i).enaColorMask; + } + + if (locallab.spots.at(i).fftColorMask) { + toEdit.locallab.spots.at(i).fftColorMask = mods.locallab.spots.at(i).fftColorMask; + } + + if (locallab.spots.at(i).CCmaskcurve) { + toEdit.locallab.spots.at(i).CCmaskcurve = mods.locallab.spots.at(i).CCmaskcurve; + } + + if (locallab.spots.at(i).LLmaskcurve) { + toEdit.locallab.spots.at(i).LLmaskcurve = mods.locallab.spots.at(i).LLmaskcurve; + } + + if (locallab.spots.at(i).HHmaskcurve) { + toEdit.locallab.spots.at(i).HHmaskcurve = mods.locallab.spots.at(i).HHmaskcurve; + } + + if (locallab.spots.at(i).HHhmaskcurve) { + toEdit.locallab.spots.at(i).HHhmaskcurve = mods.locallab.spots.at(i).HHhmaskcurve; + } + + if (locallab.spots.at(i).softradiuscol) { + toEdit.locallab.spots.at(i).softradiuscol = mods.locallab.spots.at(i).softradiuscol; + } + + if (locallab.spots.at(i).opacol) { + toEdit.locallab.spots.at(i).opacol = mods.locallab.spots.at(i).opacol; + } + + if (locallab.spots.at(i).mercol) { + toEdit.locallab.spots.at(i).mercol = mods.locallab.spots.at(i).mercol; + } + + if (locallab.spots.at(i).merlucol) { + toEdit.locallab.spots.at(i).merlucol = mods.locallab.spots.at(i).merlucol; + } + + if (locallab.spots.at(i).conthrcol) { + toEdit.locallab.spots.at(i).conthrcol = mods.locallab.spots.at(i).conthrcol; + } + + if (locallab.spots.at(i).Lmaskcurve) { + toEdit.locallab.spots.at(i).Lmaskcurve = mods.locallab.spots.at(i).Lmaskcurve; + } + + if (locallab.spots.at(i).LLmaskcolcurvewav) { + toEdit.locallab.spots.at(i).LLmaskcolcurvewav = mods.locallab.spots.at(i).LLmaskcolcurvewav; + } + + if (locallab.spots.at(i).csthresholdcol) { + toEdit.locallab.spots.at(i).csthresholdcol = mods.locallab.spots.at(i).csthresholdcol; + } + + // Exposure + if (locallab.spots.at(i).visiexpose) { + toEdit.locallab.spots.at(i).visiexpose = mods.locallab.spots.at(i).visiexpose; + } + + if (locallab.spots.at(i).expexpose) { + toEdit.locallab.spots.at(i).expexpose = mods.locallab.spots.at(i).expexpose; + } + + if (locallab.spots.at(i).complexexpose) { + toEdit.locallab.spots.at(i).complexexpose = mods.locallab.spots.at(i).complexexpose; + } + + if (locallab.spots.at(i).expcomp) { + toEdit.locallab.spots.at(i).expcomp = mods.locallab.spots.at(i).expcomp; + } + + if (locallab.spots.at(i).hlcompr) { + toEdit.locallab.spots.at(i).hlcompr = mods.locallab.spots.at(i).hlcompr; + } + + if (locallab.spots.at(i).hlcomprthresh) { + toEdit.locallab.spots.at(i).hlcomprthresh = mods.locallab.spots.at(i).hlcomprthresh; + } + + if (locallab.spots.at(i).black) { + toEdit.locallab.spots.at(i).black = mods.locallab.spots.at(i).black; + } + + if (locallab.spots.at(i).shadex) { + toEdit.locallab.spots.at(i).shadex = mods.locallab.spots.at(i).shadex; + } + + if (locallab.spots.at(i).shcompr) { + toEdit.locallab.spots.at(i).shcompr = mods.locallab.spots.at(i).shcompr; + } + + if (locallab.spots.at(i).expchroma) { + toEdit.locallab.spots.at(i).expchroma = mods.locallab.spots.at(i).expchroma; + } + + if (locallab.spots.at(i).sensiex) { + toEdit.locallab.spots.at(i).sensiex = mods.locallab.spots.at(i).sensiex; + } + + if (locallab.spots.at(i).structexp) { + toEdit.locallab.spots.at(i).structexp = mods.locallab.spots.at(i).structexp; + } + + if (locallab.spots.at(i).blurexpde) { + toEdit.locallab.spots.at(i).blurexpde = mods.locallab.spots.at(i).blurexpde; + } + + if (locallab.spots.at(i).strexp) { + toEdit.locallab.spots.at(i).strexp = mods.locallab.spots.at(i).strexp; + } + + if (locallab.spots.at(i).angexp) { + toEdit.locallab.spots.at(i).angexp = mods.locallab.spots.at(i).angexp; + } + + if (locallab.spots.at(i).excurve) { + toEdit.locallab.spots.at(i).excurve = mods.locallab.spots.at(i).excurve; + } + + if (locallab.spots.at(i).inversex) { + toEdit.locallab.spots.at(i).inversex = mods.locallab.spots.at(i).inversex; + } + + if (locallab.spots.at(i).enaExpMask) { + toEdit.locallab.spots.at(i).enaExpMask = mods.locallab.spots.at(i).enaExpMask; + } + + if (locallab.spots.at(i).enaExpMaskaft) { + toEdit.locallab.spots.at(i).enaExpMaskaft = mods.locallab.spots.at(i).enaExpMaskaft; + } + + if (locallab.spots.at(i).CCmaskexpcurve) { + toEdit.locallab.spots.at(i).CCmaskexpcurve = mods.locallab.spots.at(i).CCmaskexpcurve; + } + + if (locallab.spots.at(i).LLmaskexpcurve) { + toEdit.locallab.spots.at(i).LLmaskexpcurve = mods.locallab.spots.at(i).LLmaskexpcurve; + } + + if (locallab.spots.at(i).HHmaskexpcurve) { + toEdit.locallab.spots.at(i).HHmaskexpcurve = mods.locallab.spots.at(i).HHmaskexpcurve; + } + + if (locallab.spots.at(i).blendmaskexp) { + toEdit.locallab.spots.at(i).blendmaskexp = mods.locallab.spots.at(i).blendmaskexp; + } + + if (locallab.spots.at(i).radmaskexp) { + toEdit.locallab.spots.at(i).radmaskexp = mods.locallab.spots.at(i).radmaskexp; + } + + if (locallab.spots.at(i).chromaskexp) { + toEdit.locallab.spots.at(i).chromaskexp = mods.locallab.spots.at(i).chromaskexp; + } + + if (locallab.spots.at(i).gammaskexp) { + toEdit.locallab.spots.at(i).gammaskexp = mods.locallab.spots.at(i).gammaskexp; + } + + if (locallab.spots.at(i).slomaskexp) { + toEdit.locallab.spots.at(i).slomaskexp = mods.locallab.spots.at(i).slomaskexp; + } + + if (locallab.spots.at(i).lapmaskexp) { + toEdit.locallab.spots.at(i).lapmaskexp = mods.locallab.spots.at(i).lapmaskexp; + } + + if (locallab.spots.at(i).strmaskexp) { + toEdit.locallab.spots.at(i).strmaskexp = mods.locallab.spots.at(i).strmaskexp; + } + + if (locallab.spots.at(i).angmaskexp) { + toEdit.locallab.spots.at(i).angmaskexp = mods.locallab.spots.at(i).angmaskexp; + } + + if (locallab.spots.at(i).softradiusexp) { + toEdit.locallab.spots.at(i).softradiusexp = mods.locallab.spots.at(i).softradiusexp; + } + + if (locallab.spots.at(i).Lmaskexpcurve) { + toEdit.locallab.spots.at(i).Lmaskexpcurve = mods.locallab.spots.at(i).Lmaskexpcurve; + } + + if (locallab.spots.at(i).expMethod) { + toEdit.locallab.spots.at(i).expMethod = mods.locallab.spots.at(i).expMethod; + } + + if (locallab.spots.at(i).exnoiseMethod) { + toEdit.locallab.spots.at(i).exnoiseMethod = mods.locallab.spots.at(i).exnoiseMethod; + } + + if (locallab.spots.at(i).laplacexp) { + toEdit.locallab.spots.at(i).laplacexp = mods.locallab.spots.at(i).laplacexp; + } + + if (locallab.spots.at(i).balanexp) { + toEdit.locallab.spots.at(i).balanexp = mods.locallab.spots.at(i).balanexp; + } + + if (locallab.spots.at(i).linear) { + toEdit.locallab.spots.at(i).linear = mods.locallab.spots.at(i).linear; + } + + if (locallab.spots.at(i).gamm) { + toEdit.locallab.spots.at(i).gamm = mods.locallab.spots.at(i).gamm; + } + + if (locallab.spots.at(i).fatamount) { + toEdit.locallab.spots.at(i).fatamount = mods.locallab.spots.at(i).fatamount; + } + + if (locallab.spots.at(i).fatdetail) { + toEdit.locallab.spots.at(i).fatdetail = mods.locallab.spots.at(i).fatdetail; + } + + if (locallab.spots.at(i).fatanchor) { + toEdit.locallab.spots.at(i).fatanchor = mods.locallab.spots.at(i).fatanchor; + } + + if (locallab.spots.at(i).fatlevel) { + toEdit.locallab.spots.at(i).fatlevel = mods.locallab.spots.at(i).fatlevel; + } + + // Shadow highlight + if (locallab.spots.at(i).visishadhigh) { + toEdit.locallab.spots.at(i).visishadhigh = mods.locallab.spots.at(i).visishadhigh; + } + + if (locallab.spots.at(i).expshadhigh) { + toEdit.locallab.spots.at(i).expshadhigh = mods.locallab.spots.at(i).expshadhigh; + } + + if (locallab.spots.at(i).complexshadhigh) { + toEdit.locallab.spots.at(i).complexshadhigh = mods.locallab.spots.at(i).complexshadhigh; + } + + if (locallab.spots.at(i).shMethod) { + toEdit.locallab.spots.at(i).shMethod = mods.locallab.spots.at(i).shMethod; + } + + for (int j = 0; j < 5; j++) { + if (locallab.spots.at(i).multsh[j]) { + toEdit.locallab.spots.at(i).multsh[j] = mods.locallab.spots.at(i).multsh[j]; + } + } + + if (locallab.spots.at(i).highlights) { + toEdit.locallab.spots.at(i).highlights = mods.locallab.spots.at(i).highlights; + } + + if (locallab.spots.at(i).h_tonalwidth) { + toEdit.locallab.spots.at(i).h_tonalwidth = mods.locallab.spots.at(i).h_tonalwidth; + } + + if (locallab.spots.at(i).shadows) { + toEdit.locallab.spots.at(i).shadows = mods.locallab.spots.at(i).shadows; + } + + if (locallab.spots.at(i).s_tonalwidth) { + toEdit.locallab.spots.at(i).s_tonalwidth = mods.locallab.spots.at(i).s_tonalwidth; + } + + if (locallab.spots.at(i).sh_radius) { + toEdit.locallab.spots.at(i).sh_radius = mods.locallab.spots.at(i).sh_radius; + } + + if (locallab.spots.at(i).sensihs) { + toEdit.locallab.spots.at(i).sensihs = mods.locallab.spots.at(i).sensihs; + } + + if (locallab.spots.at(i).enaSHMask) { + toEdit.locallab.spots.at(i).enaSHMask = mods.locallab.spots.at(i).enaSHMask; + } + + if (locallab.spots.at(i).CCmaskSHcurve) { + toEdit.locallab.spots.at(i).CCmaskSHcurve = mods.locallab.spots.at(i).CCmaskSHcurve; + } + + if (locallab.spots.at(i).LLmaskSHcurve) { + toEdit.locallab.spots.at(i).LLmaskSHcurve = mods.locallab.spots.at(i).LLmaskSHcurve; + } + + if (locallab.spots.at(i).HHmaskSHcurve) { + toEdit.locallab.spots.at(i).HHmaskSHcurve = mods.locallab.spots.at(i).HHmaskSHcurve; + } + + if (locallab.spots.at(i).blendmaskSH) { + toEdit.locallab.spots.at(i).blendmaskSH = mods.locallab.spots.at(i).blendmaskSH; + } + + if (locallab.spots.at(i).radmaskSH) { + toEdit.locallab.spots.at(i).radmaskSH = mods.locallab.spots.at(i).radmaskSH; + } + + if (locallab.spots.at(i).blurSHde) { + toEdit.locallab.spots.at(i).blurSHde = mods.locallab.spots.at(i).blurSHde; + } + + if (locallab.spots.at(i).strSH) { + toEdit.locallab.spots.at(i).strSH = mods.locallab.spots.at(i).strSH; + } + + if (locallab.spots.at(i).angSH) { + toEdit.locallab.spots.at(i).angSH = mods.locallab.spots.at(i).angSH; + } + + if (locallab.spots.at(i).inverssh) { + toEdit.locallab.spots.at(i).inverssh = mods.locallab.spots.at(i).inverssh; + } + + if (locallab.spots.at(i).chromaskSH) { + toEdit.locallab.spots.at(i).chromaskSH = mods.locallab.spots.at(i).chromaskSH; + } + + if (locallab.spots.at(i).gammaskSH) { + toEdit.locallab.spots.at(i).gammaskSH = mods.locallab.spots.at(i).gammaskSH; + } + + if (locallab.spots.at(i).slomaskSH) { + toEdit.locallab.spots.at(i).slomaskSH = mods.locallab.spots.at(i).slomaskSH; + } + + if (locallab.spots.at(i).lapmaskSH) { + toEdit.locallab.spots.at(i).lapmaskSH = mods.locallab.spots.at(i).lapmaskSH; + } + + if (locallab.spots.at(i).detailSH) { + toEdit.locallab.spots.at(i).detailSH = mods.locallab.spots.at(i).detailSH; + } + + if (locallab.spots.at(i).LmaskSHcurve) { + toEdit.locallab.spots.at(i).LmaskSHcurve = mods.locallab.spots.at(i).LmaskSHcurve; + } + + if (locallab.spots.at(i).fatamountSH) { + toEdit.locallab.spots.at(i).fatamountSH = mods.locallab.spots.at(i).fatamountSH; + } + + if (locallab.spots.at(i).fatanchorSH) { + toEdit.locallab.spots.at(i).fatanchorSH = mods.locallab.spots.at(i).fatanchorSH; + } + + if (locallab.spots.at(i).gamSH) { + toEdit.locallab.spots.at(i).gamSH = mods.locallab.spots.at(i).gamSH; + } + + if (locallab.spots.at(i).sloSH) { + toEdit.locallab.spots.at(i).sloSH = mods.locallab.spots.at(i).sloSH; + } + + // Vibrance + if (locallab.spots.at(i).visivibrance) { + toEdit.locallab.spots.at(i).visivibrance = mods.locallab.spots.at(i).visivibrance; + } + + if (locallab.spots.at(i).expvibrance) { + toEdit.locallab.spots.at(i).expvibrance = mods.locallab.spots.at(i).expvibrance; + } + + if (locallab.spots.at(i).complexvibrance) { + toEdit.locallab.spots.at(i).complexvibrance = mods.locallab.spots.at(i).complexvibrance; + } + + if (locallab.spots.at(i).saturated) { + toEdit.locallab.spots.at(i).saturated = mods.locallab.spots.at(i).saturated; + } + + if (locallab.spots.at(i).pastels) { + toEdit.locallab.spots.at(i).pastels = mods.locallab.spots.at(i).pastels; + } + + if (locallab.spots.at(i).warm) { + toEdit.locallab.spots.at(i).warm = mods.locallab.spots.at(i).warm; + } + + if (locallab.spots.at(i).psthreshold) { + toEdit.locallab.spots.at(i).psthreshold = mods.locallab.spots.at(i).psthreshold; + } + + if (locallab.spots.at(i).protectskins) { + toEdit.locallab.spots.at(i).protectskins = mods.locallab.spots.at(i).protectskins; + } + + if (locallab.spots.at(i).avoidcolorshift) { + toEdit.locallab.spots.at(i).avoidcolorshift = mods.locallab.spots.at(i).avoidcolorshift; + } + + if (locallab.spots.at(i).pastsattog) { + toEdit.locallab.spots.at(i).pastsattog = mods.locallab.spots.at(i).pastsattog; + } + + if (locallab.spots.at(i).sensiv) { + toEdit.locallab.spots.at(i).sensiv = mods.locallab.spots.at(i).sensiv; + } + + if (locallab.spots.at(i).skintonescurve) { + toEdit.locallab.spots.at(i).skintonescurve = mods.locallab.spots.at(i).skintonescurve; + } + + if (locallab.spots.at(i).CCmaskvibcurve) { + toEdit.locallab.spots.at(i).CCmaskvibcurve = mods.locallab.spots.at(i).CCmaskvibcurve; + } + + if (locallab.spots.at(i).LLmaskvibcurve) { + toEdit.locallab.spots.at(i).LLmaskvibcurve = mods.locallab.spots.at(i).LLmaskvibcurve; + } + + if (locallab.spots.at(i).HHmaskvibcurve) { + toEdit.locallab.spots.at(i).HHmaskvibcurve = mods.locallab.spots.at(i).HHmaskvibcurve; + } + + if (locallab.spots.at(i).enavibMask) { + toEdit.locallab.spots.at(i).enavibMask = mods.locallab.spots.at(i).enavibMask; + } + + if (locallab.spots.at(i).blendmaskvib) { + toEdit.locallab.spots.at(i).blendmaskvib = mods.locallab.spots.at(i).blendmaskvib; + } + + if (locallab.spots.at(i).radmaskvib) { + toEdit.locallab.spots.at(i).radmaskvib = mods.locallab.spots.at(i).radmaskvib; + } + + if (locallab.spots.at(i).chromaskvib) { + toEdit.locallab.spots.at(i).chromaskvib = mods.locallab.spots.at(i).chromaskvib; + } + + if (locallab.spots.at(i).gammaskvib) { + toEdit.locallab.spots.at(i).gammaskvib = mods.locallab.spots.at(i).gammaskvib; + } + + if (locallab.spots.at(i).slomaskvib) { + toEdit.locallab.spots.at(i).slomaskvib = mods.locallab.spots.at(i).slomaskvib; + } + + if (locallab.spots.at(i).lapmaskvib) { + toEdit.locallab.spots.at(i).lapmaskvib = mods.locallab.spots.at(i).lapmaskvib; + } + + if (locallab.spots.at(i).strvib) { + toEdit.locallab.spots.at(i).strvib = mods.locallab.spots.at(i).strvib; + } + + if (locallab.spots.at(i).strvibab) { + toEdit.locallab.spots.at(i).strvibab = mods.locallab.spots.at(i).strvibab; + } + + if (locallab.spots.at(i).strvibh) { + toEdit.locallab.spots.at(i).strvibh = mods.locallab.spots.at(i).strvibh; + } + + if (locallab.spots.at(i).angvib) { + toEdit.locallab.spots.at(i).angvib = mods.locallab.spots.at(i).angvib; + } + + if (locallab.spots.at(i).Lmaskvibcurve) { + toEdit.locallab.spots.at(i).Lmaskvibcurve = mods.locallab.spots.at(i).Lmaskvibcurve; + } + + // Soft Light + if (locallab.spots.at(i).visisoft) { + toEdit.locallab.spots.at(i).visisoft = mods.locallab.spots.at(i).visisoft; + } + + if (locallab.spots.at(i).expsoft) { + toEdit.locallab.spots.at(i).expsoft = mods.locallab.spots.at(i).expsoft; + } + + if (locallab.spots.at(i).complexsoft) { + toEdit.locallab.spots.at(i).complexsoft = mods.locallab.spots.at(i).complexsoft; + } + + if (locallab.spots.at(i).streng) { + toEdit.locallab.spots.at(i).streng = mods.locallab.spots.at(i).streng; + } + + if (locallab.spots.at(i).sensisf) { + toEdit.locallab.spots.at(i).sensisf = mods.locallab.spots.at(i).sensisf; + } + + if (locallab.spots.at(i).laplace) { + toEdit.locallab.spots.at(i).laplace = mods.locallab.spots.at(i).laplace; + } + + if (locallab.spots.at(i).softMethod) { + toEdit.locallab.spots.at(i).softMethod = mods.locallab.spots.at(i).softMethod; + } + + // Blur & Noise + if (locallab.spots.at(i).visiblur) { + toEdit.locallab.spots.at(i).visiblur = mods.locallab.spots.at(i).visiblur; + } + + if (locallab.spots.at(i).expblur) { + toEdit.locallab.spots.at(i).expblur = mods.locallab.spots.at(i).expblur; + } + + if (locallab.spots.at(i).complexblur) { + toEdit.locallab.spots.at(i).complexblur = mods.locallab.spots.at(i).complexblur; + } + + if (locallab.spots.at(i).radius) { + toEdit.locallab.spots.at(i).radius = mods.locallab.spots.at(i).radius; + } + + if (locallab.spots.at(i).strength) { + toEdit.locallab.spots.at(i).strength = mods.locallab.spots.at(i).strength; + } + + if (locallab.spots.at(i).sensibn) { + toEdit.locallab.spots.at(i).sensibn = mods.locallab.spots.at(i).sensibn; + } + + if (locallab.spots.at(i).itera) { + toEdit.locallab.spots.at(i).itera = mods.locallab.spots.at(i).itera; + } + + if (locallab.spots.at(i).guidbl) { + toEdit.locallab.spots.at(i).guidbl = mods.locallab.spots.at(i).guidbl; + } + + if (locallab.spots.at(i).strbl) { + toEdit.locallab.spots.at(i).strbl = mods.locallab.spots.at(i).strbl; + } + + if (locallab.spots.at(i).isogr) { + toEdit.locallab.spots.at(i).isogr = mods.locallab.spots.at(i).isogr; + } + + if (locallab.spots.at(i).strengr) { + toEdit.locallab.spots.at(i).strengr = mods.locallab.spots.at(i).strengr; + } + + if (locallab.spots.at(i).scalegr) { + toEdit.locallab.spots.at(i).scalegr = mods.locallab.spots.at(i).scalegr; + } + + if (locallab.spots.at(i).epsbl) { + toEdit.locallab.spots.at(i).epsbl = mods.locallab.spots.at(i).epsbl; + } + + if (locallab.spots.at(i).blMethod) { + toEdit.locallab.spots.at(i).blMethod = mods.locallab.spots.at(i).blMethod; + } + + if (locallab.spots.at(i).chroMethod) { + toEdit.locallab.spots.at(i).chroMethod = mods.locallab.spots.at(i).chroMethod; + } + + if (locallab.spots.at(i).blurMethod) { + toEdit.locallab.spots.at(i).blurMethod = mods.locallab.spots.at(i).blurMethod; + } + + if (locallab.spots.at(i).medMethod) { + toEdit.locallab.spots.at(i).medMethod = mods.locallab.spots.at(i).medMethod; + } + + if (locallab.spots.at(i).activlum) { + toEdit.locallab.spots.at(i).activlum = mods.locallab.spots.at(i).activlum; + } + + if (locallab.spots.at(i).noiselumf) { + toEdit.locallab.spots.at(i).noiselumf = mods.locallab.spots.at(i).noiselumf; + } + + if (locallab.spots.at(i).noiselumf0) { + toEdit.locallab.spots.at(i).noiselumf0 = mods.locallab.spots.at(i).noiselumf0; + } + + if (locallab.spots.at(i).noiselumf2) { + toEdit.locallab.spots.at(i).noiselumf2 = mods.locallab.spots.at(i).noiselumf2; + } + + if (locallab.spots.at(i).noiselumc) { + toEdit.locallab.spots.at(i).noiselumc = mods.locallab.spots.at(i).noiselumc; + } + + if (locallab.spots.at(i).noiselumdetail) { + toEdit.locallab.spots.at(i).noiselumdetail = mods.locallab.spots.at(i).noiselumdetail; + } + + if (locallab.spots.at(i).noiselequal) { + toEdit.locallab.spots.at(i).noiselequal = mods.locallab.spots.at(i).noiselequal; + } + + if (locallab.spots.at(i).noisechrof) { + toEdit.locallab.spots.at(i).noisechrof = mods.locallab.spots.at(i).noisechrof; + } + + if (locallab.spots.at(i).noisechroc) { + toEdit.locallab.spots.at(i).noisechroc = mods.locallab.spots.at(i).noisechroc; + } + + if (locallab.spots.at(i).noisechrodetail) { + toEdit.locallab.spots.at(i).noisechrodetail = mods.locallab.spots.at(i).noisechrodetail; + } + + if (locallab.spots.at(i).adjblur) { + toEdit.locallab.spots.at(i).adjblur = mods.locallab.spots.at(i).adjblur; + } + + if (locallab.spots.at(i).bilateral) { + toEdit.locallab.spots.at(i).bilateral = mods.locallab.spots.at(i).bilateral; + } + + if (locallab.spots.at(i).sensiden) { + toEdit.locallab.spots.at(i).sensiden = mods.locallab.spots.at(i).sensiden; + } + + if (locallab.spots.at(i).detailthr) { + toEdit.locallab.spots.at(i).detailthr = mods.locallab.spots.at(i).detailthr; + } + + if (locallab.spots.at(i).locwavcurveden) { + toEdit.locallab.spots.at(i).locwavcurveden = mods.locallab.spots.at(i).locwavcurveden; + } + + if (locallab.spots.at(i).showmaskblMethodtyp) { + toEdit.locallab.spots.at(i).showmaskblMethodtyp = mods.locallab.spots.at(i).showmaskblMethodtyp; + } + + if (locallab.spots.at(i).CCmaskblcurve) { + toEdit.locallab.spots.at(i).CCmaskblcurve = mods.locallab.spots.at(i).CCmaskblcurve; + } + + if (locallab.spots.at(i).LLmaskblcurve) { + toEdit.locallab.spots.at(i).LLmaskblcurve = mods.locallab.spots.at(i).LLmaskblcurve; + } + + if (locallab.spots.at(i).HHmaskblcurve) { + toEdit.locallab.spots.at(i).HHmaskblcurve = mods.locallab.spots.at(i).HHmaskblcurve; + } + + if (locallab.spots.at(i).enablMask) { + toEdit.locallab.spots.at(i).enablMask = mods.locallab.spots.at(i).enablMask; + } + + if (locallab.spots.at(i).fftwbl) { + toEdit.locallab.spots.at(i).fftwbl = mods.locallab.spots.at(i).fftwbl; + } + + if (locallab.spots.at(i).toolbl) { + toEdit.locallab.spots.at(i).toolbl = mods.locallab.spots.at(i).toolbl; + } + + if (locallab.spots.at(i).blendmaskbl) { + toEdit.locallab.spots.at(i).blendmaskbl = mods.locallab.spots.at(i).blendmaskbl; + } + + if (locallab.spots.at(i).radmaskbl) { + toEdit.locallab.spots.at(i).radmaskbl = mods.locallab.spots.at(i).radmaskbl; + } + + if (locallab.spots.at(i).chromaskbl) { + toEdit.locallab.spots.at(i).chromaskbl = mods.locallab.spots.at(i).chromaskbl; + } + + if (locallab.spots.at(i).gammaskbl) { + toEdit.locallab.spots.at(i).gammaskbl = mods.locallab.spots.at(i).gammaskbl; + } + + if (locallab.spots.at(i).slomaskbl) { + toEdit.locallab.spots.at(i).slomaskbl = mods.locallab.spots.at(i).slomaskbl; + } + + if (locallab.spots.at(i).lapmaskbl) { + toEdit.locallab.spots.at(i).lapmaskbl = mods.locallab.spots.at(i).lapmaskbl; + } + + if (locallab.spots.at(i).shadmaskbl) { + toEdit.locallab.spots.at(i).shadmaskbl = mods.locallab.spots.at(i).shadmaskbl; + } + + if (locallab.spots.at(i).shadmaskblsha) { + toEdit.locallab.spots.at(i).shadmaskblsha = mods.locallab.spots.at(i).shadmaskblsha; + } + + if (locallab.spots.at(i).strumaskbl) { + toEdit.locallab.spots.at(i).strumaskbl = mods.locallab.spots.at(i).strumaskbl; + } + + if (locallab.spots.at(i).Lmaskblcurve) { + toEdit.locallab.spots.at(i).Lmaskblcurve = mods.locallab.spots.at(i).Lmaskblcurve; + } + + if (locallab.spots.at(i).LLmaskblcurvewav) { + toEdit.locallab.spots.at(i).LLmaskblcurvewav = mods.locallab.spots.at(i).LLmaskblcurvewav; + } + + if (locallab.spots.at(i).csthresholdblur) { + toEdit.locallab.spots.at(i).csthresholdblur = mods.locallab.spots.at(i).csthresholdblur; + } + + // Tone Mapping + if (locallab.spots.at(i).visitonemap) { + toEdit.locallab.spots.at(i).visitonemap = mods.locallab.spots.at(i).visitonemap; + } + + if (locallab.spots.at(i).exptonemap) { + toEdit.locallab.spots.at(i).exptonemap = mods.locallab.spots.at(i).exptonemap; + } + + if (locallab.spots.at(i).complextonemap) { + toEdit.locallab.spots.at(i).complextonemap = mods.locallab.spots.at(i).complextonemap; + } + + if (locallab.spots.at(i).stren) { + toEdit.locallab.spots.at(i).stren = mods.locallab.spots.at(i).stren; + } + + if (locallab.spots.at(i).gamma) { + toEdit.locallab.spots.at(i).gamma = mods.locallab.spots.at(i).gamma; + } + + if (locallab.spots.at(i).estop) { + toEdit.locallab.spots.at(i).estop = mods.locallab.spots.at(i).estop; + } + + if (locallab.spots.at(i).scaltm) { + toEdit.locallab.spots.at(i).scaltm = mods.locallab.spots.at(i).scaltm; + } + + if (locallab.spots.at(i).rewei) { + toEdit.locallab.spots.at(i).rewei = mods.locallab.spots.at(i).rewei; + } + + if (locallab.spots.at(i).satur) { + toEdit.locallab.spots.at(i).satur = mods.locallab.spots.at(i).satur; + } + + if (locallab.spots.at(i).sensitm) { + toEdit.locallab.spots.at(i).sensitm = mods.locallab.spots.at(i).sensitm; + } + + if (locallab.spots.at(i).softradiustm) { + toEdit.locallab.spots.at(i).softradiustm = mods.locallab.spots.at(i).softradiustm; + } + + if (locallab.spots.at(i).amount) { + toEdit.locallab.spots.at(i).amount = mods.locallab.spots.at(i).amount; + } + + if (locallab.spots.at(i).equiltm) { + toEdit.locallab.spots.at(i).equiltm = mods.locallab.spots.at(i).equiltm; + } + + if (locallab.spots.at(i).CCmasktmcurve) { + toEdit.locallab.spots.at(i).CCmasktmcurve = mods.locallab.spots.at(i).CCmasktmcurve; + } + + if (locallab.spots.at(i).LLmasktmcurve) { + toEdit.locallab.spots.at(i).LLmasktmcurve = mods.locallab.spots.at(i).LLmasktmcurve; + } + + if (locallab.spots.at(i).HHmasktmcurve) { + toEdit.locallab.spots.at(i).HHmasktmcurve = mods.locallab.spots.at(i).HHmasktmcurve; + } + + if (locallab.spots.at(i).enatmMask) { + toEdit.locallab.spots.at(i).enatmMask = mods.locallab.spots.at(i).enatmMask; + } + + if (locallab.spots.at(i).enatmMaskaft) { + toEdit.locallab.spots.at(i).enatmMaskaft = mods.locallab.spots.at(i).enatmMaskaft; + } + + if (locallab.spots.at(i).blendmasktm) { + toEdit.locallab.spots.at(i).blendmasktm = mods.locallab.spots.at(i).blendmasktm; + } + + if (locallab.spots.at(i).radmasktm) { + toEdit.locallab.spots.at(i).radmasktm = mods.locallab.spots.at(i).radmasktm; + } + + if (locallab.spots.at(i).chromasktm) { + toEdit.locallab.spots.at(i).chromasktm = mods.locallab.spots.at(i).chromasktm; + } + + if (locallab.spots.at(i).gammasktm) { + toEdit.locallab.spots.at(i).gammasktm = mods.locallab.spots.at(i).gammasktm; + } + + if (locallab.spots.at(i).slomasktm) { + toEdit.locallab.spots.at(i).slomasktm = mods.locallab.spots.at(i).slomasktm; + } + + if (locallab.spots.at(i).lapmasktm) { + toEdit.locallab.spots.at(i).lapmasktm = mods.locallab.spots.at(i).lapmasktm; + } + + if (locallab.spots.at(i).Lmasktmcurve) { + toEdit.locallab.spots.at(i).Lmasktmcurve = mods.locallab.spots.at(i).Lmasktmcurve; + } + + // Retinex + if (locallab.spots.at(i).visireti) { + toEdit.locallab.spots.at(i).visireti = mods.locallab.spots.at(i).visireti; + } + + if (locallab.spots.at(i).expreti) { + toEdit.locallab.spots.at(i).expreti = mods.locallab.spots.at(i).expreti; + } + + if (locallab.spots.at(i).complexreti) { + toEdit.locallab.spots.at(i).complexreti = mods.locallab.spots.at(i).complexreti; + } + + if (locallab.spots.at(i).retinexMethod) { + toEdit.locallab.spots.at(i).retinexMethod = mods.locallab.spots.at(i).retinexMethod; + } + + if (locallab.spots.at(i).str) { + toEdit.locallab.spots.at(i).str = mods.locallab.spots.at(i).str; + } + + if (locallab.spots.at(i).chrrt) { + toEdit.locallab.spots.at(i).chrrt = mods.locallab.spots.at(i).chrrt; + } + + if (locallab.spots.at(i).neigh) { + toEdit.locallab.spots.at(i).neigh = mods.locallab.spots.at(i).neigh; + } + + if (locallab.spots.at(i).vart) { + toEdit.locallab.spots.at(i).vart = mods.locallab.spots.at(i).vart; + } + + if (locallab.spots.at(i).offs) { + toEdit.locallab.spots.at(i).offs = mods.locallab.spots.at(i).offs; + } + + if (locallab.spots.at(i).dehaz) { + toEdit.locallab.spots.at(i).dehaz = mods.locallab.spots.at(i).dehaz; + } + + if (locallab.spots.at(i).depth) { + toEdit.locallab.spots.at(i).depth = mods.locallab.spots.at(i).depth; + } + + if (locallab.spots.at(i).sensih) { + toEdit.locallab.spots.at(i).sensih = mods.locallab.spots.at(i).sensih; + } + + if (locallab.spots.at(i).localTgaincurve) { + toEdit.locallab.spots.at(i).localTgaincurve = mods.locallab.spots.at(i).localTgaincurve; + } + + if (locallab.spots.at(i).localTtranscurve) { + toEdit.locallab.spots.at(i).localTtranscurve = mods.locallab.spots.at(i).localTtranscurve; + } + + if (locallab.spots.at(i).inversret) { + toEdit.locallab.spots.at(i).inversret = mods.locallab.spots.at(i).inversret; + } + + if (locallab.spots.at(i).equilret) { + toEdit.locallab.spots.at(i).equilret = mods.locallab.spots.at(i).equilret; + } + + if (locallab.spots.at(i).loglin) { + toEdit.locallab.spots.at(i).loglin = mods.locallab.spots.at(i).loglin; + } + + if (locallab.spots.at(i).lumonly) { + toEdit.locallab.spots.at(i).lumonly = mods.locallab.spots.at(i).lumonly; + } + + if (locallab.spots.at(i).softradiusret) { + toEdit.locallab.spots.at(i).softradiusret = mods.locallab.spots.at(i).softradiusret; + } + + if (locallab.spots.at(i).CCmaskreticurve) { + toEdit.locallab.spots.at(i).CCmaskreticurve = mods.locallab.spots.at(i).CCmaskreticurve; + } + + if (locallab.spots.at(i).LLmaskreticurve) { + toEdit.locallab.spots.at(i).LLmaskreticurve = mods.locallab.spots.at(i).LLmaskreticurve; + } + + if (locallab.spots.at(i).HHmaskreticurve) { + toEdit.locallab.spots.at(i).HHmaskreticurve = mods.locallab.spots.at(i).HHmaskreticurve; + } + + if (locallab.spots.at(i).enaretiMask) { + toEdit.locallab.spots.at(i).enaretiMask = mods.locallab.spots.at(i).enaretiMask; + } + + if (locallab.spots.at(i).enaretiMasktmap) { + toEdit.locallab.spots.at(i).enaretiMasktmap = mods.locallab.spots.at(i).enaretiMasktmap; + } + + if (locallab.spots.at(i).blendmaskreti) { + toEdit.locallab.spots.at(i).blendmaskreti = mods.locallab.spots.at(i).blendmaskreti; + } + + if (locallab.spots.at(i).radmaskreti) { + toEdit.locallab.spots.at(i).radmaskreti = mods.locallab.spots.at(i).radmaskreti; + } + + if (locallab.spots.at(i).chromaskreti) { + toEdit.locallab.spots.at(i).chromaskreti = mods.locallab.spots.at(i).chromaskreti; + } + + if (locallab.spots.at(i).gammaskreti) { + toEdit.locallab.spots.at(i).gammaskreti = mods.locallab.spots.at(i).gammaskreti; + } + + if (locallab.spots.at(i).slomaskreti) { + toEdit.locallab.spots.at(i).slomaskreti = mods.locallab.spots.at(i).slomaskreti; + } + + if (locallab.spots.at(i).lapmaskreti) { + toEdit.locallab.spots.at(i).lapmaskreti = mods.locallab.spots.at(i).lapmaskreti; + } + + if (locallab.spots.at(i).scalereti) { + toEdit.locallab.spots.at(i).scalereti = mods.locallab.spots.at(i).scalereti; + } + + if (locallab.spots.at(i).darkness) { + toEdit.locallab.spots.at(i).darkness = mods.locallab.spots.at(i).darkness; + } + + if (locallab.spots.at(i).lightnessreti) { + toEdit.locallab.spots.at(i).lightnessreti = mods.locallab.spots.at(i).lightnessreti; + } + + if (locallab.spots.at(i).limd) { + toEdit.locallab.spots.at(i).limd = mods.locallab.spots.at(i).limd; + } + + if (locallab.spots.at(i).cliptm) { + toEdit.locallab.spots.at(i).cliptm = mods.locallab.spots.at(i).cliptm; + } + + if (locallab.spots.at(i).fftwreti) { + toEdit.locallab.spots.at(i).fftwreti = mods.locallab.spots.at(i).fftwreti; + } + + if (locallab.spots.at(i).Lmaskreticurve) { + toEdit.locallab.spots.at(i).Lmaskreticurve = mods.locallab.spots.at(i).Lmaskreticurve; + } + + // Sharpening + if (locallab.spots.at(i).visisharp) { + toEdit.locallab.spots.at(i).visisharp = mods.locallab.spots.at(i).visisharp; + } + + if (locallab.spots.at(i).expsharp) { + toEdit.locallab.spots.at(i).expsharp = mods.locallab.spots.at(i).expsharp; + } + + if (locallab.spots.at(i).complexsharp) { + toEdit.locallab.spots.at(i).complexsharp = mods.locallab.spots.at(i).complexsharp; + } + + if (locallab.spots.at(i).sharcontrast) { + toEdit.locallab.spots.at(i).sharcontrast = mods.locallab.spots.at(i).sharcontrast; + } + + if (locallab.spots.at(i).sharradius) { + toEdit.locallab.spots.at(i).sharradius = mods.locallab.spots.at(i).sharradius; + } + + if (locallab.spots.at(i).sharamount) { + toEdit.locallab.spots.at(i).sharamount = mods.locallab.spots.at(i).sharamount; + } + + if (locallab.spots.at(i).shardamping) { + toEdit.locallab.spots.at(i).shardamping = mods.locallab.spots.at(i).shardamping; + } + + if (locallab.spots.at(i).shariter) { + toEdit.locallab.spots.at(i).shariter = mods.locallab.spots.at(i).shariter; + } + + if (locallab.spots.at(i).sharblur) { + toEdit.locallab.spots.at(i).sharblur = mods.locallab.spots.at(i).sharblur; + } + + if (locallab.spots.at(i).sensisha) { + toEdit.locallab.spots.at(i).sensisha = mods.locallab.spots.at(i).sensisha; + } + + if (locallab.spots.at(i).inverssha) { + toEdit.locallab.spots.at(i).inverssha = mods.locallab.spots.at(i).inverssha; + } + + // Local Contrast + if (locallab.spots.at(i).visicontrast) { + toEdit.locallab.spots.at(i).visicontrast = mods.locallab.spots.at(i).visicontrast; + } + + if (locallab.spots.at(i).expcontrast) { + toEdit.locallab.spots.at(i).expcontrast = mods.locallab.spots.at(i).expcontrast; + } + + if (locallab.spots.at(i).complexcontrast) { + toEdit.locallab.spots.at(i).complexcontrast = mods.locallab.spots.at(i).complexcontrast; + } + + if (locallab.spots.at(i).lcradius) { + toEdit.locallab.spots.at(i).lcradius = mods.locallab.spots.at(i).lcradius; + } + + if (locallab.spots.at(i).lcamount) { + toEdit.locallab.spots.at(i).lcamount = mods.locallab.spots.at(i).lcamount; + } + + if (locallab.spots.at(i).lcdarkness) { + toEdit.locallab.spots.at(i).lcdarkness = mods.locallab.spots.at(i).lcdarkness; + } + + if (locallab.spots.at(i).lclightness) { + toEdit.locallab.spots.at(i).lclightness = mods.locallab.spots.at(i).lclightness; + } + + if (locallab.spots.at(i).sigmalc) { + toEdit.locallab.spots.at(i).sigmalc = mods.locallab.spots.at(i).sigmalc; + } + + if (locallab.spots.at(i).levelwav) { + toEdit.locallab.spots.at(i).levelwav = mods.locallab.spots.at(i).levelwav; + } + + if (locallab.spots.at(i).residcont) { + toEdit.locallab.spots.at(i).residcont = mods.locallab.spots.at(i).residcont; + } + + if (locallab.spots.at(i).residsha) { + toEdit.locallab.spots.at(i).residsha = mods.locallab.spots.at(i).residsha; + } + + if (locallab.spots.at(i).residshathr) { + toEdit.locallab.spots.at(i).residshathr = mods.locallab.spots.at(i).residshathr; + } + + if (locallab.spots.at(i).residhi) { + toEdit.locallab.spots.at(i).residhi = mods.locallab.spots.at(i).residhi; + } + + if (locallab.spots.at(i).residhithr) { + toEdit.locallab.spots.at(i).residhithr = mods.locallab.spots.at(i).residhithr; + } + + if (locallab.spots.at(i).residblur) { + toEdit.locallab.spots.at(i).residblur = mods.locallab.spots.at(i).residblur; + } + + if (locallab.spots.at(i).levelblur) { + toEdit.locallab.spots.at(i).levelblur = mods.locallab.spots.at(i).levelblur; + } + + if (locallab.spots.at(i).sigmabl) { + toEdit.locallab.spots.at(i).sigmabl = mods.locallab.spots.at(i).sigmabl; + } + + if (locallab.spots.at(i).residchro) { + toEdit.locallab.spots.at(i).residchro = mods.locallab.spots.at(i).residchro; + } + + if (locallab.spots.at(i).residcomp) { + toEdit.locallab.spots.at(i).residcomp = mods.locallab.spots.at(i).residcomp; + } + + + if (locallab.spots.at(i).sigma) { + toEdit.locallab.spots.at(i).sigma = mods.locallab.spots.at(i).sigma; + } + + if (locallab.spots.at(i).offset) { + toEdit.locallab.spots.at(i).offset = mods.locallab.spots.at(i).offset; + } + + if (locallab.spots.at(i).sigmadr) { + toEdit.locallab.spots.at(i).sigmadr = mods.locallab.spots.at(i).sigmadr; + } + + if (locallab.spots.at(i).threswav) { + toEdit.locallab.spots.at(i).threswav = mods.locallab.spots.at(i).threswav; + } + + if (locallab.spots.at(i).chromalev) { + toEdit.locallab.spots.at(i).chromalev = mods.locallab.spots.at(i).chromalev; + } + + if (locallab.spots.at(i).chromablu) { + toEdit.locallab.spots.at(i).chromablu = mods.locallab.spots.at(i).chromablu; + } + + if (locallab.spots.at(i).sigmadc) { + toEdit.locallab.spots.at(i).sigmadc = mods.locallab.spots.at(i).sigmadc; + } + + if (locallab.spots.at(i).deltad) { + toEdit.locallab.spots.at(i).deltad = mods.locallab.spots.at(i).deltad; + } + + if (locallab.spots.at(i).fatres) { + toEdit.locallab.spots.at(i).fatres = mods.locallab.spots.at(i).fatres; + } + + if (locallab.spots.at(i).clarilres) { + toEdit.locallab.spots.at(i).clarilres = mods.locallab.spots.at(i).clarilres; + } + + if (locallab.spots.at(i).claricres) { + toEdit.locallab.spots.at(i).claricres = mods.locallab.spots.at(i).claricres; + } + + if (locallab.spots.at(i).clarisoft) { + toEdit.locallab.spots.at(i).clarisoft = mods.locallab.spots.at(i).clarisoft; + } + + if (locallab.spots.at(i).sigmalc2) { + toEdit.locallab.spots.at(i).sigmalc2 = mods.locallab.spots.at(i).sigmalc2; + } + + if (locallab.spots.at(i).strwav) { + toEdit.locallab.spots.at(i).strwav = mods.locallab.spots.at(i).strwav; + } + + if (locallab.spots.at(i).angwav) { + toEdit.locallab.spots.at(i).angwav = mods.locallab.spots.at(i).angwav; + } + + if (locallab.spots.at(i).strengthw) { + toEdit.locallab.spots.at(i).strengthw = mods.locallab.spots.at(i).strengthw; + } + + if (locallab.spots.at(i).sigmaed) { + toEdit.locallab.spots.at(i).sigmaed = mods.locallab.spots.at(i).sigmaed; + } + + if (locallab.spots.at(i).radiusw) { + toEdit.locallab.spots.at(i).radiusw = mods.locallab.spots.at(i).radiusw; + } + + if (locallab.spots.at(i).detailw) { + toEdit.locallab.spots.at(i).detailw = mods.locallab.spots.at(i).detailw; + } + + if (locallab.spots.at(i).gradw) { + toEdit.locallab.spots.at(i).gradw = mods.locallab.spots.at(i).gradw; + } + + if (locallab.spots.at(i).tloww) { + toEdit.locallab.spots.at(i).tloww = mods.locallab.spots.at(i).tloww; + } + + if (locallab.spots.at(i).thigw) { + toEdit.locallab.spots.at(i).thigw = mods.locallab.spots.at(i).thigw; + } + + if (locallab.spots.at(i).edgw) { + toEdit.locallab.spots.at(i).edgw = mods.locallab.spots.at(i).edgw; + } + + if (locallab.spots.at(i).basew) { + toEdit.locallab.spots.at(i).basew = mods.locallab.spots.at(i).basew; + } + + if (locallab.spots.at(i).sensilc) { + toEdit.locallab.spots.at(i).sensilc = mods.locallab.spots.at(i).sensilc; + } + + if (locallab.spots.at(i).fftwlc) { + toEdit.locallab.spots.at(i).fftwlc = mods.locallab.spots.at(i).fftwlc; + } + + if (locallab.spots.at(i).blurlc) { + toEdit.locallab.spots.at(i).blurlc = mods.locallab.spots.at(i).blurlc; + } + + if (locallab.spots.at(i).wavblur) { + toEdit.locallab.spots.at(i).wavblur = mods.locallab.spots.at(i).wavblur; + } + + if (locallab.spots.at(i).wavedg) { + toEdit.locallab.spots.at(i).wavedg = mods.locallab.spots.at(i).wavedg; + } + + if (locallab.spots.at(i).waveshow) { + toEdit.locallab.spots.at(i).waveshow = mods.locallab.spots.at(i).waveshow; + } + + if (locallab.spots.at(i).wavcont) { + toEdit.locallab.spots.at(i).wavcont = mods.locallab.spots.at(i).wavcont; + } + + if (locallab.spots.at(i).wavcomp) { + toEdit.locallab.spots.at(i).wavcomp = mods.locallab.spots.at(i).wavcomp; + } + + if (locallab.spots.at(i).wavgradl) { + toEdit.locallab.spots.at(i).wavgradl = mods.locallab.spots.at(i).wavgradl; + } + + if (locallab.spots.at(i).wavcompre) { + toEdit.locallab.spots.at(i).wavcompre = mods.locallab.spots.at(i).wavcompre; + } + + if (locallab.spots.at(i).origlc) { + toEdit.locallab.spots.at(i).origlc = mods.locallab.spots.at(i).origlc; + } + + if (locallab.spots.at(i).localcontMethod) { + toEdit.locallab.spots.at(i).localcontMethod = mods.locallab.spots.at(i).localcontMethod; + } + + if (locallab.spots.at(i).localedgMethod) { + toEdit.locallab.spots.at(i).localedgMethod = mods.locallab.spots.at(i).localedgMethod; + } + + if (locallab.spots.at(i).localneiMethod) { + toEdit.locallab.spots.at(i).localneiMethod = mods.locallab.spots.at(i).localneiMethod; + } + + if (locallab.spots.at(i).locwavcurve) { + toEdit.locallab.spots.at(i).locwavcurve = mods.locallab.spots.at(i).locwavcurve; + } + + if (locallab.spots.at(i).csthreshold) { + toEdit.locallab.spots.at(i).csthreshold = mods.locallab.spots.at(i).csthreshold; + } + + if (locallab.spots.at(i).loclevwavcurve) { + toEdit.locallab.spots.at(i).loclevwavcurve = mods.locallab.spots.at(i).loclevwavcurve; + } + + if (locallab.spots.at(i).locconwavcurve) { + toEdit.locallab.spots.at(i).locconwavcurve = mods.locallab.spots.at(i).locconwavcurve; + } + + if (locallab.spots.at(i).loccompwavcurve) { + toEdit.locallab.spots.at(i).loccompwavcurve = mods.locallab.spots.at(i).loccompwavcurve; + } + + if (locallab.spots.at(i).loccomprewavcurve) { + toEdit.locallab.spots.at(i).loccomprewavcurve = mods.locallab.spots.at(i).loccomprewavcurve; + } + + if (locallab.spots.at(i).locedgwavcurve) { + toEdit.locallab.spots.at(i).locedgwavcurve = mods.locallab.spots.at(i).locedgwavcurve; + } + + if (locallab.spots.at(i).CCmasklccurve) { + toEdit.locallab.spots.at(i).CCmasklccurve = mods.locallab.spots.at(i).CCmasklccurve; + } + + if (locallab.spots.at(i).LLmasklccurve) { + toEdit.locallab.spots.at(i).LLmasklccurve = mods.locallab.spots.at(i).LLmasklccurve; + } + + if (locallab.spots.at(i).HHmasklccurve) { + toEdit.locallab.spots.at(i).HHmasklccurve = mods.locallab.spots.at(i).HHmasklccurve; + } + + if (locallab.spots.at(i).enalcMask) { + toEdit.locallab.spots.at(i).enalcMask = mods.locallab.spots.at(i).enalcMask; + } + + if (locallab.spots.at(i).blendmasklc) { + toEdit.locallab.spots.at(i).blendmasklc = mods.locallab.spots.at(i).blendmasklc; + } + + if (locallab.spots.at(i).radmasklc) { + toEdit.locallab.spots.at(i).radmasklc = mods.locallab.spots.at(i).radmasklc; + } + + if (locallab.spots.at(i).chromasklc) { + toEdit.locallab.spots.at(i).chromasklc = mods.locallab.spots.at(i).chromasklc; + } + + if (locallab.spots.at(i).Lmasklccurve) { + toEdit.locallab.spots.at(i).Lmasklccurve = mods.locallab.spots.at(i).Lmasklccurve; + } + + // Contrast by detail levels + if (locallab.spots.at(i).visicbdl) { + toEdit.locallab.spots.at(i).visicbdl = mods.locallab.spots.at(i).visicbdl; + } + + if (locallab.spots.at(i).expcbdl) { + toEdit.locallab.spots.at(i).expcbdl = mods.locallab.spots.at(i).expcbdl; + } + + if (locallab.spots.at(i).complexcbdl) { + toEdit.locallab.spots.at(i).complexcbdl = mods.locallab.spots.at(i).complexcbdl; + } + + for (int j = 0; j < 6; j++) { + if (locallab.spots.at(i).mult[j]) { + toEdit.locallab.spots.at(i).mult[j] = mods.locallab.spots.at(i).mult[j]; + } + } + + if (locallab.spots.at(i).chromacbdl) { + toEdit.locallab.spots.at(i).chromacbdl = mods.locallab.spots.at(i).chromacbdl; + } + + if (locallab.spots.at(i).threshold) { + toEdit.locallab.spots.at(i).threshold = mods.locallab.spots.at(i).threshold; + } + + if (locallab.spots.at(i).sensicb) { + toEdit.locallab.spots.at(i).sensicb = mods.locallab.spots.at(i).sensicb; + } + + if (locallab.spots.at(i).clarityml) { + toEdit.locallab.spots.at(i).clarityml = mods.locallab.spots.at(i).clarityml; + } + + if (locallab.spots.at(i).contresid) { + toEdit.locallab.spots.at(i).contresid = mods.locallab.spots.at(i).contresid; + } + + if (locallab.spots.at(i).blurcbdl) { + toEdit.locallab.spots.at(i).blurcbdl = mods.locallab.spots.at(i).blurcbdl; + } + + if (locallab.spots.at(i).softradiuscb) { + toEdit.locallab.spots.at(i).softradiuscb = mods.locallab.spots.at(i).softradiuscb; + } + + if (locallab.spots.at(i).enacbMask) { + toEdit.locallab.spots.at(i).enacbMask = mods.locallab.spots.at(i).enacbMask; + } + + if (locallab.spots.at(i).CCmaskcbcurve) { + toEdit.locallab.spots.at(i).CCmaskcbcurve = mods.locallab.spots.at(i).CCmaskcbcurve; + } + + if (locallab.spots.at(i).LLmaskcbcurve) { + toEdit.locallab.spots.at(i).LLmaskcbcurve = mods.locallab.spots.at(i).LLmaskcbcurve; + } + + if (locallab.spots.at(i).HHmaskcbcurve) { + toEdit.locallab.spots.at(i).HHmaskcbcurve = mods.locallab.spots.at(i).HHmaskcbcurve; + } + + if (locallab.spots.at(i).blendmaskcb) { + toEdit.locallab.spots.at(i).blendmaskcb = mods.locallab.spots.at(i).blendmaskcb; + } + + if (locallab.spots.at(i).radmaskcb) { + toEdit.locallab.spots.at(i).radmaskcb = mods.locallab.spots.at(i).radmaskcb; + } + + if (locallab.spots.at(i).chromaskcb) { + toEdit.locallab.spots.at(i).chromaskcb = mods.locallab.spots.at(i).chromaskcb; + } + + if (locallab.spots.at(i).gammaskcb) { + toEdit.locallab.spots.at(i).gammaskcb = mods.locallab.spots.at(i).gammaskcb; + } + + if (locallab.spots.at(i).slomaskcb) { + toEdit.locallab.spots.at(i).slomaskcb = mods.locallab.spots.at(i).slomaskcb; + } + + if (locallab.spots.at(i).lapmaskcb) { + toEdit.locallab.spots.at(i).lapmaskcb = mods.locallab.spots.at(i).lapmaskcb; + } + + if (locallab.spots.at(i).Lmaskcbcurve) { + toEdit.locallab.spots.at(i).Lmaskcbcurve = mods.locallab.spots.at(i).Lmaskcbcurve; + } + + // Log encoding + if (locallab.spots.at(i).visilog) { + toEdit.locallab.spots.at(i).visilog = mods.locallab.spots.at(i).visilog; + } + + if (locallab.spots.at(i).explog) { + toEdit.locallab.spots.at(i).explog = mods.locallab.spots.at(i).explog; + } + + if (locallab.spots.at(i).autocompute) { + toEdit.locallab.spots.at(i).autocompute = mods.locallab.spots.at(i).autocompute; + } + + if (locallab.spots.at(i).sourceGray) { + toEdit.locallab.spots.at(i).sourceGray = mods.locallab.spots.at(i).sourceGray; + } + + if (locallab.spots.at(i).targetGray) { + toEdit.locallab.spots.at(i).targetGray = mods.locallab.spots.at(i).targetGray; + } + + if (locallab.spots.at(i).Autogray) { + toEdit.locallab.spots.at(i).Autogray = mods.locallab.spots.at(i).Autogray; + } + + if (locallab.spots.at(i).fullimage) { + toEdit.locallab.spots.at(i).fullimage = mods.locallab.spots.at(i).fullimage; + } + + if (locallab.spots.at(i).blackEv) { + toEdit.locallab.spots.at(i).blackEv = mods.locallab.spots.at(i).blackEv; + } + + if (locallab.spots.at(i).whiteEv) { + toEdit.locallab.spots.at(i).whiteEv = mods.locallab.spots.at(i).whiteEv; + } + + if (locallab.spots.at(i).detail) { + toEdit.locallab.spots.at(i).detail = mods.locallab.spots.at(i).detail; + } + + if (locallab.spots.at(i).sensilog) { + toEdit.locallab.spots.at(i).sensilog = mods.locallab.spots.at(i).sensilog; + } + + if (locallab.spots.at(i).baselog) { + toEdit.locallab.spots.at(i).baselog = mods.locallab.spots.at(i).baselog; + } + + if (locallab.spots.at(i).strlog) { + toEdit.locallab.spots.at(i).strlog = mods.locallab.spots.at(i).strlog; + } + + if (locallab.spots.at(i).anglog) { + toEdit.locallab.spots.at(i).anglog = mods.locallab.spots.at(i).anglog; + } + } + if (pcvignette.enabled) { toEdit.pcvignette.enabled = mods.pcvignette.enabled; } @@ -3469,7 +5809,7 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng if (dehaze.strength) { toEdit.dehaze.strength = dontforceSet && options.baBehav[ADDSET_DEHAZE_STRENGTH] ? toEdit.dehaze.strength + mods.dehaze.strength : mods.dehaze.strength; } - + if (dehaze.depth) { toEdit.dehaze.depth = mods.dehaze.depth; } @@ -3561,6 +5901,917 @@ bool FilmNegativeParamsEdited::isUnchanged() const return enabled && redRatio && greenExp && blueRatio && baseValues; } +LocallabParamsEdited::LocallabSpotEdited::LocallabSpotEdited(bool v) : + // Control spot settings + name(v), + isvisible(v), + shape(v), + spotMethod(v), + wavMethod(v), + sensiexclu(v), + structexclu(v), + struc(v), + shapeMethod(v), + loc(v), + centerX(v), + centerY(v), + circrad(v), + qualityMethod(v), + complexMethod(v), + transit(v), + feather(v), + thresh(v), + iter(v), + balan(v), + balanh(v), + colorde(v), + colorscope(v), + transitweak(v), + transitgrad(v), + avoid(v), + blwh(v), + recurs(v), + laplac(v), + deltae(v), + shortc(v), + savrest(v), + scopemask(v), + lumask(v), + // Color & Light + visicolor(v), + expcolor(v), + complexcolor(v), + curvactiv(v), + lightness(v), + contrast(v), + chroma(v), + labgridALow(v), + labgridBLow(v), + labgridAHigh(v), + labgridBHigh(v), + labgridALowmerg(v), + labgridBLowmerg(v), + labgridAHighmerg(v), + labgridBHighmerg(v), + strengthgrid(v), + sensi(v), + structcol(v), + strcol(v), + strcolab(v), + strcolh(v), + angcol(v), + blurcolde(v), + blurcol(v), + contcol(v), + blendmaskcol(v), + radmaskcol(v), + chromaskcol(v), + gammaskcol(v), + slomaskcol(v), + shadmaskcol(v), + strumaskcol(v), + lapmaskcol(v), + qualitycurveMethod(v), + gridMethod(v), + merMethod(v), + toneMethod(v), + mergecolMethod(v), + llcurve(v), + lccurve(v), + cccurve(v), + clcurve(v), + rgbcurve(v), + LHcurve(v), + HHcurve(v), + invers(v), + special(v), + toolcol(v), + enaColorMask(v), + fftColorMask(v), + CCmaskcurve(v), + LLmaskcurve(v), + HHmaskcurve(v), + HHhmaskcurve(v), + softradiuscol(v), + opacol(v), + mercol(v), + merlucol(v), + conthrcol(v), + Lmaskcurve(v), + LLmaskcolcurvewav(v), + csthresholdcol(v), + // Exposure + visiexpose(v), + expexpose(v), + complexexpose(v), + expcomp(v), + hlcompr(v), + hlcomprthresh(v), + black(v), + shadex(v), + shcompr(v), + expchroma(v), + sensiex(v), + structexp(v), + blurexpde(v), + strexp(v), + angexp(v), + excurve(v), + inversex(v), + enaExpMask(v), + enaExpMaskaft(v), + CCmaskexpcurve(v), + LLmaskexpcurve(v), + HHmaskexpcurve(v), + blendmaskexp(v), + radmaskexp(v), + chromaskexp(v), + gammaskexp(v), + slomaskexp(v), + lapmaskexp(v), + strmaskexp(v), + angmaskexp(v), + softradiusexp(v), + Lmaskexpcurve(v), + expMethod(v), + exnoiseMethod(v), + laplacexp(v), + balanexp(v), + linear(v), + gamm(v), + fatamount(v), + fatdetail(v), + fatanchor(v), + fatlevel(v), + // Shadow highlight + visishadhigh(v), + expshadhigh(v), + complexshadhigh(v), + shMethod(v), + multsh{v, v, v, v, v, v}, + highlights(v), + h_tonalwidth(v), + shadows(v), + s_tonalwidth(v), + sh_radius(v), + sensihs(v), + enaSHMask(v), + CCmaskSHcurve(v), + LLmaskSHcurve(v), + HHmaskSHcurve(v), + blendmaskSH(v), + radmaskSH(v), + blurSHde(v), + strSH(v), + angSH(v), + inverssh(v), + chromaskSH(v), + gammaskSH(v), + slomaskSH(v), + lapmaskSH(v), + detailSH(v), + LmaskSHcurve(v), + fatamountSH(v), + fatanchorSH(v), + gamSH(v), + sloSH(v), + // Vibrance + visivibrance(v), + expvibrance(v), + complexvibrance(v), + saturated(v), + pastels(v), + warm(v), + psthreshold(v), + protectskins(v), + avoidcolorshift(v), + pastsattog(v), + sensiv(v), + skintonescurve(v), + CCmaskvibcurve(v), + LLmaskvibcurve(v), + HHmaskvibcurve(v), + enavibMask(v), + blendmaskvib(v), + radmaskvib(v), + chromaskvib(v), + gammaskvib(v), + slomaskvib(v), + lapmaskvib(v), + strvib(v), + strvibab(v), + strvibh(v), + angvib(v), + Lmaskvibcurve(v), + // Soft Light + visisoft(v), + expsoft(v), + complexsoft(v), + streng(v), + sensisf(v), + laplace(v), + softMethod(v), + // Blur & Noise + visiblur(v), + expblur(v), + complexblur(v), + radius(v), + strength(v), + sensibn(v), + itera(v), + guidbl(v), + strbl(v), + isogr(v), + strengr(v), + scalegr(v), + epsbl(v), + blMethod(v), + chroMethod(v), + blurMethod(v), + medMethod(v), + activlum(v), + noiselumf(v), + noiselumf0(v), + noiselumf2(v), + noiselumc(v), + noiselumdetail(v), + noiselequal(v), + noisechrof(v), + noisechroc(v), + noisechrodetail(v), + adjblur(v), + bilateral(v), + sensiden(v), + detailthr(v), + locwavcurveden(v), + showmaskblMethodtyp(v), + CCmaskblcurve(v), + LLmaskblcurve(v), + HHmaskblcurve(v), + enablMask(v), + fftwbl(v), + toolbl(v), + blendmaskbl(v), + radmaskbl(v), + chromaskbl(v), + gammaskbl(v), + slomaskbl(v), + lapmaskbl(v), + shadmaskbl(v), + shadmaskblsha(v), + strumaskbl(v), + Lmaskblcurve(v), + LLmaskblcurvewav(v), + csthresholdblur(v), + // Tone Mapping + visitonemap(v), + exptonemap(v), + complextonemap(v), + stren(v), + gamma(v), + estop(v), + scaltm(v), + rewei(v), + satur(v), + sensitm(v), + softradiustm(v), + amount(v), + equiltm(v), + CCmasktmcurve(v), + LLmasktmcurve(v), + HHmasktmcurve(v), + enatmMask(v), + enatmMaskaft(v), + blendmasktm(v), + radmasktm(v), + chromasktm(v), + gammasktm(v), + slomasktm(v), + lapmasktm(v), + Lmasktmcurve(v), + // Retinex + visireti(v), + expreti(v), + complexreti(v), + retinexMethod(v), + str(v), + chrrt(v), + neigh(v), + vart(v), + offs(v), + dehaz(v), + depth(v), + sensih(v), + localTgaincurve(v), + localTtranscurve(v), + inversret(v), + equilret(v), + loglin(v), + lumonly(v), + softradiusret(v), + CCmaskreticurve(v), + LLmaskreticurve(v), + HHmaskreticurve(v), + enaretiMask(v), + enaretiMasktmap(v), + blendmaskreti(v), + radmaskreti(v), + chromaskreti(v), + gammaskreti(v), + slomaskreti(v), + lapmaskreti(v), + scalereti(v), + darkness(v), + lightnessreti(v), + limd(v), + cliptm(v), + fftwreti(v), + Lmaskreticurve(v), + // Sharpening + visisharp(v), + expsharp(v), + complexsharp(v), + sharcontrast(v), + sharradius(v), + sharamount(v), + shardamping(v), + shariter(v), + sharblur(v), + sensisha(v), + inverssha(v), + // Local Contrast + visicontrast(v), + expcontrast(v), + complexcontrast(v), + lcradius(v), + lcamount(v), + lcdarkness(v), + lclightness(v), + sigmalc(v), + levelwav(v), + residcont(v), + residsha(v), + residshathr(v), + residhi(v), + residhithr(v), + residblur(v), + levelblur(v), + sigmabl(v), + residchro(v), + residcomp(v), + sigma(v), + offset(v), + sigmadr(v), + threswav(v), + chromalev(v), + chromablu(v), + sigmadc(v), + deltad(v), + fatres(v), + clarilres(v), + claricres(v), + clarisoft(v), + sigmalc2(v), + strwav(v), + angwav(v), + strengthw(v), + sigmaed(v), + radiusw(v), + detailw(v), + gradw(v), + tloww(v), + thigw(v), + edgw(v), + basew(v), + sensilc(v), + fftwlc(v), + blurlc(v), + wavblur(v), + wavedg(v), + waveshow(v), + wavcont(v), + wavcomp(v), + wavgradl(v), + wavcompre(v), + origlc(v), + localcontMethod(v), + localedgMethod(v), + localneiMethod(v), + locwavcurve(v), + csthreshold(v), + loclevwavcurve(v), + locconwavcurve(v), + loccompwavcurve(v), + loccomprewavcurve(v), + locedgwavcurve(v), + CCmasklccurve(v), + LLmasklccurve(v), + HHmasklccurve(v), + enalcMask(v), + blendmasklc(v), + radmasklc(v), + chromasklc(v), + Lmasklccurve(v), + // Contrast by detail levels + visicbdl(v), + expcbdl(v), + complexcbdl(v), + mult{v, v, v, v, v, v}, + chromacbdl(v), + threshold(v), + sensicb(v), + clarityml(v), + contresid(v), + blurcbdl(v), + softradiuscb(v), + enacbMask(v), + CCmaskcbcurve(v), + LLmaskcbcurve(v), + HHmaskcbcurve(v), + blendmaskcb(v), + radmaskcb(v), + chromaskcb(v), + gammaskcb(v), + slomaskcb(v), + lapmaskcb(v), + Lmaskcbcurve(v), + // Log encoding + visilog(v), + explog(v), + autocompute(v), + sourceGray(v), + targetGray(v), + Autogray(v), + fullimage(v), + blackEv(v), + whiteEv(v), + detail(v), + sensilog(v), + baselog(v), + strlog(v), + anglog(v) +{ +} + +void LocallabParamsEdited::LocallabSpotEdited::set(bool v) +{ + name = v; + isvisible = v; + shape = v; + spotMethod = v; + wavMethod = v; + sensiexclu = v; + structexclu = v; + struc = v; + shapeMethod = v; + loc = v; + centerX = v; + centerY = v; + circrad = v; + qualityMethod = v; + complexMethod = v; + transit = v; + feather = v; + thresh = v; + iter = v; + balan = v; + balanh = v; + colorde = v; + colorscope = v; + transitweak = v; + transitgrad = v; + avoid = v; + blwh = v; + recurs = v; + laplac = v; + deltae = v; + shortc = v; + savrest = v; + scopemask = v; + lumask = v; + // Color & Light + visicolor = v; + expcolor = v; + complexcolor = v; + curvactiv = v; + lightness = v; + contrast = v; + chroma = v; + labgridALow = v; + labgridBLow = v; + labgridAHigh = v; + labgridBHigh = v; + labgridALowmerg = v; + labgridBLowmerg = v; + labgridAHighmerg = v; + labgridBHighmerg = v; + strengthgrid = v; + sensi = v; + structcol = v; + strcol = v; + strcolab = v; + strcolh = v; + angcol = v; + blurcolde = v; + blurcol = v; + contcol = v; + blendmaskcol = v; + radmaskcol = v; + chromaskcol = v; + gammaskcol = v; + slomaskcol = v; + shadmaskcol = v; + strumaskcol = v; + lapmaskcol = v; + qualitycurveMethod = v; + gridMethod = v; + merMethod = v; + toneMethod = v; + mergecolMethod = v; + llcurve = v; + lccurve = v; + cccurve = v; + clcurve = v; + rgbcurve = v; + LHcurve = v; + HHcurve = v; + invers = v; + special = v; + toolcol = v; + enaColorMask = v; + fftColorMask = v; + CCmaskcurve = v; + LLmaskcurve = v; + HHmaskcurve = v; + HHhmaskcurve = v; + softradiuscol = v; + opacol = v; + mercol = v; + merlucol = v; + conthrcol = v; + Lmaskcurve = v; + LLmaskcolcurvewav = v; + csthresholdcol = v; + // Exposure + visiexpose = v; + expexpose = v; + complexexpose = v; + expcomp = v; + hlcompr = v; + hlcomprthresh = v; + black = v; + shadex = v; + shcompr = v; + expchroma = v; + sensiex = v; + structexp = v; + blurexpde = v; + strexp = v; + angexp = v; + excurve = v; + inversex = v; + enaExpMask = v; + enaExpMaskaft = v; + CCmaskexpcurve = v; + LLmaskexpcurve = v; + HHmaskexpcurve = v; + blendmaskexp = v; + radmaskexp = v; + chromaskexp = v; + gammaskexp = v; + slomaskexp = v; + lapmaskexp = v; + strmaskexp = v; + angmaskexp = v; + softradiusexp = v; + Lmaskexpcurve = v; + expMethod = v; + exnoiseMethod = v; + laplacexp = v; + balanexp = v; + linear = v; + gamm = v; + fatamount = v; + fatdetail = v; + fatanchor = v; + fatlevel = v; + // Shadow highlight + visishadhigh = v; + expshadhigh = v; + complexshadhigh = v; + shMethod = v; + + for (int i = 0; i < 5; i++) { + multsh[i] = v; + } + + highlights = v; + h_tonalwidth = v; + shadows = v; + s_tonalwidth = v; + sh_radius = v; + sensihs = v; + enaSHMask = v; + CCmaskSHcurve = v; + LLmaskSHcurve = v; + HHmaskSHcurve = v; + blendmaskSH = v; + radmaskSH = v; + blurSHde = v; + strSH = v; + angSH = v; + inverssh = v; + chromaskSH = v; + gammaskSH = v; + slomaskSH = v; + lapmaskSH = v; + detailSH = v; + LmaskSHcurve = v; + fatamountSH = v; + fatanchorSH = v; + gamSH = v; + sloSH = v; + // Vibrance + visivibrance = v; + expvibrance = v; + complexvibrance = v; + saturated = v; + pastels = v; + warm = v; + psthreshold = v; + protectskins = v; + avoidcolorshift = v; + pastsattog = v; + sensiv = v; + skintonescurve = v; + CCmaskvibcurve = v; + LLmaskvibcurve = v; + HHmaskvibcurve = v; + enavibMask = v; + blendmaskvib = v; + radmaskvib = v; + chromaskvib = v; + gammaskvib = v; + slomaskvib = v; + lapmaskvib = v; + strvib = v; + strvibab = v; + strvibh = v; + angvib = v; + Lmaskvibcurve = v; + // Soft Light + visisoft = v; + expsoft = v; + complexsoft = v; + streng = v; + sensisf = v; + laplace = v; + softMethod = v; + // Blur & Noise + visiblur = v; + expblur = v; + complexblur = v; + radius = v; + strength = v; + sensibn = v; + itera = v; + guidbl = v; + strbl = v; + isogr = v; + strengr = v; + scalegr = v; + epsbl = v; + blMethod = v; + chroMethod = v; + blurMethod = v; + medMethod = v; + activlum = v; + noiselumf = v; + noiselumf0 = v; + noiselumf2 = v; + noiselumc = v; + noiselumdetail = v; + noiselequal = v; + noisechrof = v; + noisechroc = v; + noisechrodetail = v; + adjblur = v; + bilateral = v; + sensiden = v; + detailthr = v; + locwavcurveden = v; + showmaskblMethodtyp = v; + CCmaskblcurve = v; + LLmaskblcurve = v; + HHmaskblcurve = v; + enablMask = v; + fftwbl = v; + toolbl = v; + blendmaskbl = v; + radmaskbl = v; + chromaskbl = v; + gammaskbl = v; + slomaskbl = v; + lapmaskbl = v; + shadmaskbl = v; + shadmaskblsha = v; + strumaskbl = v; + Lmaskblcurve = v; + LLmaskblcurvewav = v; + csthresholdblur = v; + // Tone Mapping + visitonemap = v; + exptonemap = v; + complextonemap = v; + stren = v; + gamma = v; + estop = v; + scaltm = v; + rewei = v; + satur = v; + sensitm = v; + softradiustm = v; + amount = v; + equiltm = v; + CCmasktmcurve = v; + LLmasktmcurve = v; + HHmasktmcurve = v; + enatmMask = v; + enatmMaskaft = v; + blendmasktm = v; + radmasktm = v; + chromasktm = v; + gammasktm = v; + slomasktm = v; + lapmasktm = v; + Lmasktmcurve = v; + // Retinex + visireti = v; + expreti = v; + complexreti = v; + retinexMethod = v; + str = v; + chrrt = v; + neigh = v; + vart = v; + offs = v; + dehaz = v; + depth = v; + sensih = v; + localTgaincurve = v; + localTtranscurve = v; + inversret = v; + equilret = v; + loglin = v; + lumonly = v; + softradiusret = v; + CCmaskreticurve = v; + LLmaskreticurve = v; + HHmaskreticurve = v; + enaretiMask = v; + enaretiMasktmap = v; + blendmaskreti = v; + radmaskreti = v; + chromaskreti = v; + gammaskreti = v; + slomaskreti = v; + lapmaskreti = v; + scalereti = v; + darkness = v; + lightnessreti = v; + limd = v; + cliptm = v; + fftwreti = v; + Lmaskreticurve = v; + // Sharpening + visisharp = v; + expsharp = v; + complexsharp = v; + sharcontrast = v; + sharradius = v; + sharamount = v; + shardamping = v; + shariter = v; + sharblur = v; + sensisha = v; + inverssha = v; + // Local Contrast + visicontrast = v; + expcontrast = v; + complexcontrast = v; + lcradius = v; + lcamount = v; + lcdarkness = v; + lclightness = v; + sigmalc = v; + levelwav = v; + residcont = v; + residsha = v; + residshathr = v; + residhi = v; + residhithr = v; + residblur = v; + levelblur = v; + sigmabl = v; + residchro = v; + residcomp = v; + sigma = v; + offset = v; + sigmadr = v; + threswav = v; + chromalev = v; + chromablu = v; + sigmadc = v; + deltad = v; + fatres = v; + clarilres = v; + claricres = v; + clarisoft = v; + sigmalc2 = v; + strwav = v; + angwav = v; + strengthw = v; + sigmaed = v; + radiusw = v; + detailw = v; + gradw = v; + tloww = v; + thigw = v; + edgw = v; + basew = v; + sensilc = v; + fftwlc = v; + blurlc = v; + wavblur = v; + wavedg = v; + waveshow = v; + wavcont = v; + wavcomp = v; + wavgradl = v; + wavcompre = v; + origlc = v; + localcontMethod = v; + localedgMethod = v; + localneiMethod = v; + locwavcurve = v; + csthreshold = v; + loclevwavcurve = v; + locconwavcurve = v; + loccompwavcurve = v; + loccomprewavcurve = v; + locedgwavcurve = v; + CCmasklccurve = v; + LLmasklccurve = v; + HHmasklccurve = v; + enalcMask = v; + blendmasklc = v; + radmasklc = v; + chromasklc = v; + Lmasklccurve = v; + // Contrast by detail levels + visicbdl = v; + expcbdl = v; + complexcbdl = v; + + for (int i = 0; i < 6; i++) { + mult[i] = v; + } + + chromacbdl = v; + threshold = v; + sensicb = v; + clarityml = v; + contresid = v; + softradiuscb = v; + enacbMask = v; + CCmaskcbcurve = v; + LLmaskcbcurve = v; + HHmaskcbcurve = v; + blurcbdl = v; + blendmaskcb = v; + radmaskcb = v; + chromaskcb = v; + gammaskcb = v; + slomaskcb = v; + lapmaskcb = v; + Lmaskcbcurve = v; + // Log encoding + visilog = v; + explog = v; + autocompute = v; + sourceGray = v; + targetGray = v; + Autogray = v; + fullimage = v; + blackEv = v; + whiteEv = v; + detail = v; + sensilog = v; + baselog = v; + strlog = v; + anglog = v; +} + bool CaptureSharpeningParamsEdited::isUnchanged() const { return enabled && contrast && autoContrast && autoRadius && deconvradius && deconvradiusOffset && deconviter && deconvitercheck; @@ -3569,4 +6820,4 @@ bool CaptureSharpeningParamsEdited::isUnchanged() const bool RAWParamsEdited::PreprocessWBParamsEdited::isUnchanged() const { return mode; -} \ No newline at end of file +} diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 306b25ae7..1bd2e045e 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -169,6 +169,10 @@ struct ColorToningEdited { bool labgridBLow; bool labgridAHigh; bool labgridBHigh; + bool labgridALowmerg; + bool labgridBLowmerg; + bool labgridAHighmerg; + bool labgridBHighmerg; bool labregions; bool labregionsShowMask; }; @@ -379,6 +383,468 @@ struct RotateParamsEdited { struct DistortionParamsEdited { bool amount; }; +class LocallabParamsEdited +{ +public: + struct LocallabSpotEdited { + // Control spot settings + bool name; + bool isvisible; + bool shape; + bool spotMethod; + bool wavMethod; + bool sensiexclu; + bool structexclu; + bool struc; + bool shapeMethod; + bool loc; + bool centerX; + bool centerY; + bool circrad; + bool qualityMethod; + bool complexMethod; + bool transit; + bool feather; + bool thresh; + bool iter; + bool balan; + bool balanh; + bool colorde; + bool colorscope; + bool transitweak; + bool transitgrad; + bool avoid; + bool blwh; + bool recurs; + bool laplac; + bool deltae; + bool shortc; + bool savrest; + bool scopemask; + bool lumask; + // Color & Light + bool visicolor; + bool expcolor; + bool complexcolor; + bool curvactiv; + bool lightness; + bool contrast; + bool chroma; + bool labgridALow; + bool labgridBLow; + bool labgridAHigh; + bool labgridBHigh; + bool labgridALowmerg; + bool labgridBLowmerg; + bool labgridAHighmerg; + bool labgridBHighmerg; + bool strengthgrid; + bool sensi; + bool structcol; + bool strcol; + bool strcolab; + bool strcolh; + bool angcol; + bool blurcolde; + bool blurcol; + bool contcol; + bool blendmaskcol; + bool radmaskcol; + bool chromaskcol; + bool gammaskcol; + bool slomaskcol; + bool shadmaskcol; + bool strumaskcol; + bool lapmaskcol; + bool qualitycurveMethod; + bool gridMethod; + bool merMethod; + bool toneMethod; + bool mergecolMethod; + bool llcurve; + bool lccurve; + bool cccurve; + bool clcurve; + bool rgbcurve; + bool LHcurve; + bool HHcurve; + bool invers; + bool special; + bool toolcol; + bool enaColorMask; + bool fftColorMask; + bool CCmaskcurve; + bool LLmaskcurve; + bool HHmaskcurve; + bool HHhmaskcurve; + bool softradiuscol; + bool opacol; + bool mercol; + bool merlucol; + bool conthrcol; + bool Lmaskcurve; + bool LLmaskcolcurvewav; + bool csthresholdcol; + // Exposure + bool visiexpose; + bool expexpose; + bool complexexpose; + bool expcomp; + bool hlcompr; + bool hlcomprthresh; + bool black; + bool shadex; + bool shcompr; + bool expchroma; + bool sensiex; + bool structexp; + bool blurexpde; + bool strexp; + bool angexp; + bool excurve; + bool inversex; + bool enaExpMask; + bool enaExpMaskaft; + bool CCmaskexpcurve; + bool LLmaskexpcurve; + bool HHmaskexpcurve; + bool blendmaskexp; + bool radmaskexp; + bool chromaskexp; + bool gammaskexp; + bool slomaskexp; + bool lapmaskexp; + bool strmaskexp; + bool angmaskexp; + bool softradiusexp; + bool Lmaskexpcurve; + bool expMethod; + bool exnoiseMethod; + bool laplacexp; + bool balanexp; + bool linear; + bool gamm; + bool fatamount; + bool fatdetail; + bool fatanchor; + bool fatlevel; + // Shadow highlight + bool visishadhigh; + bool expshadhigh; + bool complexshadhigh; + bool shMethod; + bool multsh[6]; + bool highlights; + bool h_tonalwidth; + bool shadows; + bool s_tonalwidth; + bool sh_radius; + bool sensihs; + bool enaSHMask; + bool CCmaskSHcurve; + bool LLmaskSHcurve; + bool HHmaskSHcurve; + bool blendmaskSH; + bool radmaskSH; + bool blurSHde; + bool strSH; + bool angSH; + bool inverssh; + bool chromaskSH; + bool gammaskSH; + bool slomaskSH; + bool lapmaskSH; + bool detailSH; + bool LmaskSHcurve; + bool fatamountSH; + bool fatanchorSH; + bool gamSH; + bool sloSH; + // Vibrance + bool visivibrance; + bool expvibrance; + bool complexvibrance; + bool saturated; + bool pastels; + bool warm; + bool psthreshold; + bool protectskins; + bool avoidcolorshift; + bool pastsattog; + bool sensiv; + bool skintonescurve; + bool CCmaskvibcurve; + bool LLmaskvibcurve; + bool HHmaskvibcurve; + bool enavibMask; + bool blendmaskvib; + bool radmaskvib; + bool chromaskvib; + bool gammaskvib; + bool slomaskvib; + bool lapmaskvib; + bool strvib; + bool strvibab; + bool strvibh; + bool angvib; + bool Lmaskvibcurve; + // Soft Light + bool visisoft; + bool expsoft; + bool complexsoft; + bool streng; + bool sensisf; + bool laplace; + bool softMethod; + // Blur & Noise + bool visiblur; + bool expblur; + bool complexblur; + bool radius; + bool strength; + bool sensibn; + bool itera; + bool guidbl; + bool strbl; + bool isogr; + bool strengr; + bool scalegr; + bool epsbl; + bool blMethod; + bool chroMethod; + bool blurMethod; + bool medMethod; + bool activlum; + bool noiselumf; + bool noiselumf0; + bool noiselumf2; + bool noiselumc; + bool noiselumdetail; + bool noiselequal; + bool noisechrof; + bool noisechroc; + bool noisechrodetail; + bool adjblur; + bool bilateral; + bool sensiden; + bool detailthr; + bool locwavcurveden; + bool showmaskblMethodtyp; + bool CCmaskblcurve; + bool LLmaskblcurve; + bool HHmaskblcurve; + bool enablMask; + bool fftwbl; + bool toolbl; + bool blendmaskbl; + bool radmaskbl; + bool chromaskbl; + bool gammaskbl; + bool slomaskbl; + bool lapmaskbl; + bool shadmaskbl; + bool shadmaskblsha; + bool strumaskbl; + bool Lmaskblcurve; + bool LLmaskblcurvewav; + bool csthresholdblur; + // Tone Mapping + bool visitonemap; + bool exptonemap; + bool complextonemap; + bool stren; + bool gamma; + bool estop; + bool scaltm; + bool rewei; + bool satur; + bool sensitm; + bool softradiustm; + bool amount; + bool equiltm; + bool CCmasktmcurve; + bool LLmasktmcurve; + bool HHmasktmcurve; + bool enatmMask; + bool enatmMaskaft; + bool blendmasktm; + bool radmasktm; + bool chromasktm; + bool gammasktm; + bool slomasktm; + bool lapmasktm; + bool Lmasktmcurve; + // Retinex + bool visireti; + bool expreti; + bool complexreti; + bool retinexMethod; + bool str; + bool chrrt; + bool neigh; + bool vart; + bool offs; + bool dehaz; + bool depth; + bool sensih; + bool localTgaincurve; + bool localTtranscurve; + bool inversret; + bool equilret; + bool loglin; + bool lumonly; + bool softradiusret; + bool CCmaskreticurve; + bool LLmaskreticurve; + bool HHmaskreticurve; + bool enaretiMask; + bool enaretiMasktmap; + bool blendmaskreti; + bool radmaskreti; + bool chromaskreti; + bool gammaskreti; + bool slomaskreti; + bool lapmaskreti; + bool scalereti; + bool darkness; + bool lightnessreti; + bool limd; + bool cliptm; + bool fftwreti; + bool Lmaskreticurve; + // Sharpening + bool visisharp; + bool expsharp; + bool complexsharp; + bool sharcontrast; + bool sharradius; + bool sharamount; + bool shardamping; + bool shariter; + bool sharblur; + bool sensisha; + bool inverssha; + // Local Contrast + bool visicontrast; + bool expcontrast; + bool complexcontrast; + bool lcradius; + bool lcamount; + bool lcdarkness; + bool lclightness; + bool sigmalc; + bool levelwav; + bool residcont; + bool residsha; + bool residshathr; + bool residhi; + bool residhithr; + bool residblur; + bool levelblur; + bool sigmabl; + bool residchro; + bool residcomp; + bool sigma; + bool offset; + bool sigmadr; + bool threswav; + bool chromalev; + bool chromablu; + bool sigmadc; + bool deltad; + bool fatres; + bool clarilres; + bool claricres; + bool clarisoft; + bool sigmalc2; + bool strwav; + bool angwav; + bool strengthw; + bool sigmaed; + bool radiusw; + bool detailw; + bool gradw; + bool tloww; + bool thigw; + bool edgw; + bool basew; + bool sensilc; + bool fftwlc; + bool blurlc; + bool wavblur; + bool wavedg; + bool waveshow; + bool wavcont; + bool wavcomp; + bool wavgradl; + bool wavcompre; + bool origlc; + bool localcontMethod; + bool localedgMethod; + bool localneiMethod; + bool locwavcurve; + bool csthreshold; + bool loclevwavcurve; + bool locconwavcurve; + bool loccompwavcurve; + bool loccomprewavcurve; + bool locedgwavcurve; + bool CCmasklccurve; + bool LLmasklccurve; + bool HHmasklccurve; + bool enalcMask; + bool blendmasklc; + bool radmasklc; + bool chromasklc; + bool Lmasklccurve; + // Contrast by detail levels + bool visicbdl; + bool expcbdl; + bool complexcbdl; + bool mult[6]; + bool chromacbdl; + bool threshold; + bool sensicb; + bool clarityml; + bool contresid; + bool blurcbdl; + bool softradiuscb; + bool enacbMask; + bool CCmaskcbcurve; + bool LLmaskcbcurve; + bool HHmaskcbcurve; + bool blendmaskcb; + bool radmaskcb; + bool chromaskcb; + bool gammaskcb; + bool slomaskcb; + bool lapmaskcb; + bool Lmaskcbcurve; + // Log encoding + bool visilog; + bool explog; + bool autocompute; + bool sourceGray; + bool targetGray; + bool Autogray; + bool fullimage; + bool blackEv; + bool whiteEv; + bool detail; + bool sensilog; + bool baselog; + bool strlog; + bool anglog; + + LocallabSpotEdited(bool v); + + void set(bool v); + }; + + bool enabled; + bool selspot; + std::vector spots; +}; struct LensProfParamsEdited { bool lcpFile; @@ -398,8 +864,21 @@ struct LensProfParamsEdited { }; struct PerspectiveParamsEdited { + bool method; bool horizontal; bool vertical; + bool camera_crop_factor; + bool camera_focal_length; + bool camera_pitch; + bool camera_roll; + bool camera_shift_horiz; + bool camera_shift_vert; + bool camera_yaw; + bool projection_pitch; + bool projection_rotate; + bool projection_shift_horiz; + bool projection_shift_vert; + bool projection_yaw; }; struct GradientParamsEdited { @@ -614,7 +1093,6 @@ struct WaveletParamsEdited { bool labgridBLow; bool labgridAHigh; bool labgridBHigh; - }; struct DirPyrEqualizerParamsEdited { @@ -782,6 +1260,7 @@ struct ParamsEdited { LensProfParamsEdited lensProf; PerspectiveParamsEdited perspective; GradientParamsEdited gradient; + LocallabParamsEdited locallab; PCVignetteParamsEdited pcvignette; CACorrParamsEdited cacorrection; VignettingParamsEdited vignetting; diff --git a/rtgui/partialpastedlg.cc b/rtgui/partialpastedlg.cc index c2f3340e5..6808a2cc6 100644 --- a/rtgui/partialpastedlg.cc +++ b/rtgui/partialpastedlg.cc @@ -17,10 +17,174 @@ * along with RawTherapee. If not, see . */ #include "partialpastedlg.h" + +#include "guiutils.h" #include "multilangmgr.h" #include "paramsedited.h" -#include "guiutils.h" +#include "../rtengine/procparams.h" + +using namespace rtengine::procparams; + +/* ==== PartialSpotWidget ==== */ +PartialSpotWidget::PartialSpotWidget(): + // Widget GUI elements + treeview(Gtk::manage(new Gtk::TreeView())), + treemodel(Gtk::ListStore::create(spotRow)), + + // Widget listener + selListener(nullptr) +{ + // Configure tree view + treeview->set_model(treemodel); + treeview->set_enable_search(false); + treeview->set_headers_visible(false); + + // Add tree view columns + auto cell1 = Gtk::manage(new Gtk::CellRendererToggle()); + cell1->signal_toggled().connect( + sigc::mem_fun( + *this, &PartialSpotWidget::keepToggled)); + int cols_count = treeview->append_column("", *cell1); + auto col = treeview->get_column(cols_count - 1); + + if (col) { + col->set_cell_data_func( + *cell1, sigc::mem_fun( + *this, &PartialSpotWidget::render_keep)); + } + + auto cell2 = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview->append_column("", *cell2); + col = treeview->get_column(cols_count - 1); + + if (col) { + col->set_cell_data_func( + *cell2, sigc::mem_fun( + *this, &PartialSpotWidget::render_spotname)); + } + + // Create and configure scrolled window + Gtk::ScrolledWindow* const scrolledwindows = Gtk::manage(new Gtk::ScrolledWindow()); + scrolledwindows->add(*treeview); + scrolledwindows->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + scrolledwindows->set_min_content_height(100); + + // Add widgets to VBox + pack_start(*scrolledwindows); + show_all(); +} + +void PartialSpotWidget::updateSpotWidget(const rtengine::procparams::ProcParams* pp, const bool defValue) +{ + treeviewconn.block(true); + + // Clear tree model + treemodel->clear(); + + // Add tree model element according to pp + Gtk::TreeRow newspot; + + for (size_t i = 0; i < pp->locallab.spots.size(); i++) { + newspot = *(treemodel->append()); + newspot[spotRow.keep] = defValue; + newspot[spotRow.spotname] = pp->locallab.spots.at(i).name; + } + + treeviewconn.block(false); +} + +void PartialSpotWidget::enableAll() +{ + treeviewconn.block(true); + + for (auto &spot : treemodel->children()) { + spot[spotRow.keep] = true; + } + + treeviewconn.block(false); +} + +void PartialSpotWidget::disableAll() +{ + treeviewconn.block(true); + + for (auto &spot : treemodel->children()) { + spot[spotRow.keep] = false; + } + + treeviewconn.block(false); +} + +std::vector PartialSpotWidget::getSelectionStatus() +{ + std::vector keepVect; + + for (auto &spot : treemodel->children()) { + keepVect.push_back(spot[spotRow.keep]); + } + + return keepVect; +} + +void PartialSpotWidget::render_keep(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + const auto spot = *iter; + Gtk::CellRendererToggle* const ct = static_cast(cell); + + // Render cell toggle + ct->property_active() = spot[spotRow.keep]; +} + +void PartialSpotWidget::render_spotname(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + const auto spot = *iter; + Gtk::CellRendererText* const ct = static_cast(cell); + + // Render cell toggle + ct->property_text() = spot[spotRow.spotname]; +} + +void PartialSpotWidget::keepToggled(const Glib::ustring &path) +{ + PartialSpotWidgetListener::UpdateStatus status; + + // Get clicked row + const auto selRow = *(treemodel->get_iter(path)); + + // Update treeview according to selected row + selRow[spotRow.keep] = !selRow[spotRow.keep]; + + // Count total number of spot + const int totalnb = (int)treemodel->children().size(); + + // Count number of toggled elements + int togglednb = 0; + + for (auto &spot : treemodel->children()) { + if (spot[spotRow.keep]) { + togglednb++; + } + } + + // Compute status + if (togglednb == 0) { // No spot toggled + status = PartialSpotWidgetListener::UpdateStatus::NoSelection; + } else { + if (togglednb == totalnb) { // All spot toggled + status = PartialSpotWidgetListener::UpdateStatus::AllSelection; + } else { // Partial number of spots toggled + status = PartialSpotWidgetListener::UpdateStatus::PartialSelection; + } + } + + // Propagate event to listener + if (selListener) { + selListener->partialSpotUpdated(status); + } +} + +/* ==== PartialPasteDlg ==== */ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* parent) : Gtk::Dialog (title, *parent, true) { @@ -45,6 +209,8 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren raw ->set_name("PartialPasteHeader"); advanced = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_ADVANCEDGROUP"))); advanced ->set_name("PartialPasteHeader"); + locallab = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_LOCALLABGROUP"))); + locallab ->set_name("PartialPasteHeader"); // Basic Settings: wb = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_WHITEBALANCE"))); @@ -103,6 +269,10 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren exifch = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_EXIFCHANGES"))); iptc = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_IPTCINFO"))); + // Locallab: + spots = Gtk::manage(new PartialSpotWidget()); + spots->setPartialSpotWidgetListener(this); + // Raw Settings: raw_method = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_DMETHOD"))); raw_imagenum = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAW_IMAGENUM"))); @@ -134,17 +304,17 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren raw_ca_autocorrect = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAWCACORR_AUTO"))); raw_caredblue = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAWCACORR_CAREDBLUE"))); raw_ca_avoid_colourshift = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_RAWCACORR_AVOIDCOLORSHIFT"))); - //--- + //... filmNegative = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_FILMNEGATIVE")) ); //--- captureSharpening = Gtk::manage (new Gtk::CheckButton (M("TP_PDSHARPENING_LABEL")) ); //--- raw_preprocwb = Gtk::manage (new Gtk::CheckButton (M("PARTIALPASTE_PREPROCWB"))); - Gtk::VBox* vboxes[8]; - Gtk::HSeparator* hseps[8]; + Gtk::VBox* vboxes[9]; + Gtk::HSeparator* hseps[9]; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 9; i++) { vboxes[i] = Gtk::manage (new Gtk::VBox ()); vboxes[i]->set_name("PartialPasteGroupContainer"); hseps[i] = Gtk::manage (new Gtk::HSeparator ()); @@ -215,50 +385,48 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren vboxes[5]->pack_start (*colorappearance, Gtk::PACK_SHRINK, 2); vboxes[5]->pack_start (*wavelet, Gtk::PACK_SHRINK, 2); - //META - vboxes[6]->pack_start (*meta, Gtk::PACK_SHRINK, 2); + //LOCALLAB + vboxes[6]->pack_start(*locallab, Gtk::PACK_SHRINK, 2); + vboxes[6]->pack_start(*spots, Gtk::PACK_SHRINK, 2); vboxes[6]->pack_start (*hseps[6], Gtk::PACK_SHRINK, 2); - vboxes[6]->pack_start(*metadata, Gtk::PACK_SHRINK, 2); - vboxes[6]->pack_start (*exifch, Gtk::PACK_SHRINK, 2); - vboxes[6]->pack_start (*iptc, Gtk::PACK_SHRINK, 2); - //RAW - vboxes[7]->pack_start (*raw, Gtk::PACK_SHRINK, 2); + //META + vboxes[7]->pack_start (*meta, Gtk::PACK_SHRINK, 2); vboxes[7]->pack_start (*hseps[7], Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_method, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_border, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_imagenum, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_pixelshift, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_ccSteps, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_dcb_iterations, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_dcb_enhance, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_lmmse_iterations, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); - vboxes[7]->pack_start (*raw_linenoise, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_greenthresh, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_hotpix_filt, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_deadpix_filt, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_pdaf_lines_filter, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); - vboxes[7]->pack_start (*raw_expos, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_black, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_preprocwb, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); - vboxes[7]->pack_start (*df_file, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*df_AutoSelect, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); - vboxes[7]->pack_start (*ff_file, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*ff_AutoSelect, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*ff_BlurType, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*ff_BlurRadius, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*ff_ClipControl, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); - vboxes[7]->pack_start (*raw_ca_autocorrect, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_caredblue, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*raw_ca_avoid_colourshift, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); - vboxes[7]->pack_start (*filmNegative, Gtk::PACK_SHRINK, 2); - vboxes[7]->pack_start (*captureSharpening, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_method, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_border, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_imagenum, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_pixelshift, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_ccSteps, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_dcb_iterations, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_dcb_enhance, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_lmmse_iterations, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); + vboxes[8]->pack_start (*raw_linenoise, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_greenthresh, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_hotpix_filt, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_deadpix_filt, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_pdaf_lines_filter, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); + vboxes[8]->pack_start (*raw_expos, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_black, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_preprocwb, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); + vboxes[8]->pack_start (*df_file, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*df_AutoSelect, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); + vboxes[8]->pack_start (*ff_file, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*ff_AutoSelect, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*ff_BlurType, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*ff_BlurRadius, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*ff_ClipControl, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); + vboxes[8]->pack_start (*raw_ca_autocorrect, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_caredblue, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*raw_ca_avoid_colourshift, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*Gtk::manage (new Gtk::HSeparator ()), Gtk::PACK_SHRINK, 0); + vboxes[8]->pack_start (*filmNegative, Gtk::PACK_SHRINK, 2); + vboxes[8]->pack_start (*captureSharpening, Gtk::PACK_SHRINK, 2); Gtk::VBox* vbCol1 = Gtk::manage (new Gtk::VBox ()); Gtk::VBox* vbCol2 = Gtk::manage (new Gtk::VBox ()); @@ -268,11 +436,11 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren vbCol1->pack_start (*vboxes[i], Gtk::PACK_SHRINK, 2); } - for (int i = 3; i < 7; i++) { + for (int i = 3; i < 8; i++) { vbCol2->pack_start (*vboxes[i], Gtk::PACK_SHRINK, 2); } - for (int i = 7; i < 8; i++) { + for (int i = 8; i < 9; i++) { vbCol3->pack_start (*vboxes[i], Gtk::PACK_SHRINK, 2); } @@ -317,6 +485,7 @@ PartialPasteDlg::PartialPasteDlg (const Glib::ustring &title, Gtk::Window* paren metaConn = meta->signal_toggled().connect (sigc::mem_fun(*this, &PartialPasteDlg::metaToggled)); rawConn = raw->signal_toggled().connect (sigc::mem_fun(*this, &PartialPasteDlg::rawToggled)); advancedConn = advanced->signal_toggled().connect (sigc::mem_fun(*this, &PartialPasteDlg::advancedToggled)); + locallabConn = locallab->signal_toggled().connect (sigc::mem_fun(*this, &PartialPasteDlg::locallabToggled)); // Basic Settings wbConn = wb->signal_toggled().connect (sigc::bind (sigc::mem_fun(*basic, &Gtk::CheckButton::set_inconsistent), true)); @@ -431,6 +600,7 @@ void PartialPasteDlg::everythingToggled () ConnectionBlocker metaBlocker(metaConn); ConnectionBlocker rawBlocker(rawConn); ConnectionBlocker advancedBlocker(advancedConn); + ConnectionBlocker locallabBlocker(locallabConn); everything->set_inconsistent (false); @@ -443,6 +613,7 @@ void PartialPasteDlg::everythingToggled () meta->set_active(everything->get_active()); raw->set_active(everything->get_active()); advanced->set_active(everything->get_active()); + locallab->set_active(everything->get_active()); //toggle group children PartialPasteDlg::basicToggled (); @@ -453,6 +624,7 @@ void PartialPasteDlg::everythingToggled () PartialPasteDlg::metaToggled (); PartialPasteDlg::rawToggled (); PartialPasteDlg::advancedToggled (); + PartialPasteDlg::locallabToggled(); } void PartialPasteDlg::rawToggled () @@ -661,6 +833,17 @@ void PartialPasteDlg::metaToggled () iptc->set_active (meta->get_active ()); } +void PartialPasteDlg::locallabToggled() +{ + locallab->set_inconsistent (false); + + if (locallab->get_active()) { + spots->enableAll(); + } else { + spots->disableAll(); + } +} + /* * Copies the selected items from the source ProcParams+ParamsEdited(optional) @@ -670,7 +853,9 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param { ParamsEdited falsePE; // falsePE is a workaround to set a group of ParamsEdited to false + falsePE.locallab.spots.resize(srcPP->locallab.spots.size(), LocallabParamsEdited::LocallabSpotEdited(false)); ParamsEdited filterPE(true); // Contains the initial information about the loaded values + filterPE.locallab.spots.resize(srcPP->locallab.spots.size(), LocallabParamsEdited::LocallabSpotEdited(true)); if (srcPE) { filterPE = *srcPE; @@ -1011,10 +1196,65 @@ void PartialPasteDlg::applyPaste (rtengine::procparams::ProcParams* dstPP, Param filterPE.raw.preprocessWB.mode = falsePE.raw.preprocessWB.mode; } - if (dstPE) { - *dstPE = filterPE; - } + // Locallab shall be kept in last position + if (!locallab->get_active () && !locallab->get_inconsistent()) { + filterPE.locallab = falsePE.locallab; - // Apply the filter! - filterPE.combine(*dstPP, *srcPP, true); + if (dstPE) { + *dstPE = filterPE; + } + + // Apply the filter! + filterPE.combine(*dstPP, *srcPP, true); + } else { // Update PE and PP according to chosen spot + // Get chosen spots + std::vector chosenSpots = spots->getSelectionStatus(); + + // Create temporary PP and PE based on scrPP and scrPE + rtengine::procparams::ProcParams tmpPP = rtengine::procparams::ProcParams(*srcPP); + ParamsEdited tmpPE = ParamsEdited(filterPE); + + // Update tmpPP and tmpPE according to chosen spots + for (int i = ((int)chosenSpots.size() - 1); i >= 0; i--) { + if (!chosenSpots.at(i)) { + tmpPP.locallab.spots.erase(tmpPP.locallab.spots.begin() + i); + tmpPE.locallab.spots.erase(tmpPE.locallab.spots.begin() + i); + } + } + + if (dstPE) { + *dstPE = tmpPE; + } + + // Apply the filter! + tmpPE.combine(*dstPP, tmpPP, true); + } +} + +void PartialPasteDlg::updateSpotWidget(const rtengine::procparams::ProcParams* pp) +{ + locallab->set_inconsistent(false); + + if (pp->locallab.spots.size() > 0) { + spots->set_visible(true); + spots->updateSpotWidget(pp, locallab->get_active()); + } else { + spots->set_visible(false); // Hide widget if there is no locallab spot + } +} + +void PartialPasteDlg::partialSpotUpdated(const UpdateStatus status) +{ + switch (status) { + case (AllSelection): + locallab->set_active(true); + break; + + case (NoSelection): + locallab->set_active(false); + break; + + case (PartialSelection): + locallab->set_inconsistent(true); + } } diff --git a/rtgui/partialpastedlg.h b/rtgui/partialpastedlg.h index e835ec779..94bc2868c 100644 --- a/rtgui/partialpastedlg.h +++ b/rtgui/partialpastedlg.h @@ -34,7 +34,81 @@ class ProcParams; struct ParamsEdited; -class PartialPasteDlg final : public Gtk::Dialog +/* ==== PartialSpotWidgetListener ==== */ +class PartialSpotWidget; +class PartialSpotWidgetListener +{ +public: + enum UpdateStatus { + AllSelection = 1, + NoSelection = 2, + PartialSelection = 3 + }; + +public: + PartialSpotWidgetListener() {}; + virtual ~PartialSpotWidgetListener() {}; + + virtual void partialSpotUpdated(const UpdateStatus status) = 0; +}; + +/* ==== PartialSpotWidget ==== */ +class PartialSpotWidget: + public Gtk::VBox +{ +private: + // Tree model to manage spot selection widget + class SpotRow: + public Gtk::TreeModel::ColumnRecord + { + public: + Gtk::TreeModelColumn keep; + Gtk::TreeModelColumn spotname; + + SpotRow() + { + add(keep); + add(spotname); + } + }; + + // Spot selection widgets + Gtk::TreeView* const treeview; + sigc::connection treeviewconn; + SpotRow spotRow; + Glib::RefPtr treemodel; + + // Spot selection listener + PartialSpotWidgetListener* selListener; + +public: + PartialSpotWidget(); + + // Setter for spot selection listener + void setPartialSpotWidgetListener(PartialSpotWidgetListener* pswl) + { + selListener = pswl; + } + + // Spot selection widget management functions + void updateSpotWidget(const rtengine::procparams::ProcParams* pp, const bool defValue); + void enableAll(); + void disableAll(); + std::vector getSelectionStatus(); + +private: + // GUI aspect management functions + void render_keep(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_spotname(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + + // Event management function + void keepToggled(const Glib::ustring &path); +}; + +/* ==== PartialPasteDlg ==== */ +class PartialPasteDlg final: + public Gtk::Dialog, + public PartialSpotWidgetListener { public: @@ -52,6 +126,7 @@ public: Gtk::CheckButton* meta; Gtk::CheckButton* raw; Gtk::CheckButton* advanced; + Gtk::CheckButton* locallab; // options in basic: Gtk::CheckButton* wb; @@ -111,6 +186,8 @@ public: Gtk::CheckButton* exifch; Gtk::CheckButton* iptc; + // options in locallab: + PartialSpotWidget* spots; // options in raw: Gtk::CheckButton* raw_expos; @@ -145,7 +222,7 @@ public: Gtk::CheckButton* raw_preprocwb; sigc::connection everythingConn, basicConn, detailConn, colorConn, lensConn, compositionConn, metaConn, rawConn, advancedConn; - + sigc::connection locallabConn; sigc::connection wbConn, exposureConn, localcontrastConn, shConn, pcvignetteConn, gradientConn, labcurveConn, colorappearanceConn; sigc::connection sharpenConn, gradsharpenConn, microcontrastConn, impdenConn, dirpyrdenConn, defringeConn, epdConn, fattalConn, dirpyreqConn, waveletConn, retinexConn, dehazeConn; sigc::connection vibranceConn, chmixerConn, hsveqConn, rgbcurvesConn, chmixerbwConn, colortoningConn, filmSimulationConn, softlightConn; @@ -172,4 +249,8 @@ public: void metaToggled (); void rawToggled (); void advancedToggled (); + void locallabToggled (); + + void updateSpotWidget(const rtengine::procparams::ProcParams* pp); + void partialSpotUpdated(const UpdateStatus status); }; diff --git a/rtgui/perspective.cc b/rtgui/perspective.cc index b86da6a52..bdba2dcc1 100644 --- a/rtgui/perspective.cc +++ b/rtgui/perspective.cc @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ +#include "eventmapper.h" #include "perspective.h" #include "rtimage.h" @@ -28,22 +29,166 @@ using namespace rtengine::procparams; PerspCorrection::PerspCorrection () : FoldableToolPanel(this, "perspective", M("TP_PERSPECTIVE_LABEL")) { + auto mapper = ProcEventMapper::getInstance(); + EvPerspCamAngle = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_CAM_ANGLE"); + EvPerspCamFocalLength = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_CAM_FL"); + EvPerspCamShift = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_CAM_SHIFT"); + EvPerspMethod = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_METHOD"); + EvPerspProjAngle = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_ANGLE"); + EvPerspProjRotate = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_ROTATE"); + EvPerspProjShift = mapper->newEvent(TRANSFORM, "HISTORY_MSG_PERSP_PROJ_SHIFT"); + lens_geom_listener = nullptr; + metadata = nullptr; + Gtk::Image* ipersHL = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png")); Gtk::Image* ipersHR = Gtk::manage (new RTImage ("perspective-horizontal-right-small.png")); Gtk::Image* ipersVL = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png")); Gtk::Image* ipersVR = Gtk::manage (new RTImage ("perspective-vertical-top-small.png")); - horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_HORIZONTAL"), -100, 100, 0.1, 0, ipersHL, ipersHR)); - horiz->setAdjusterListener (this); + Gtk::Image* ipers_auto_pitch = Gtk::manage (new RTImage ("perspective-vertical-bottom.png")); + Gtk::Image* ipers_auto_yaw = Gtk::manage (new RTImage ("perspective-horizontal-left.png")); + Gtk::Image* ipers_auto_pitch_yaw = Gtk::manage (new RTImage ("perspective-horizontal-vertical.png")); + + Gtk::Image* ipers_cam_yaw_left = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png")); + Gtk::Image* ipers_cam_yaw_right = Gtk::manage (new RTImage ("perspective-horizontal-right-small.png")); + Gtk::Image* ipers_cam_pitch_left = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png")); + Gtk::Image* ipers_cam_pitch_right = Gtk::manage (new RTImage ("perspective-vertical-top-small.png")); + Gtk::Image* ipers_proj_yaw_left = Gtk::manage (new RTImage ("perspective-horizontal-left-small.png")); + Gtk::Image* ipers_proj_yaw_right = Gtk::manage (new RTImage ("perspective-horizontal-right-small.png")); + Gtk::Image* ipers_proj_pitch_left = Gtk::manage (new RTImage ("perspective-vertical-bottom-small.png")); + Gtk::Image* ipers_proj_pitch_right = Gtk::manage (new RTImage ("perspective-vertical-top-small.png")); + Gtk::Image* ipers_rotate_left = Gtk::manage(new RTImage("rotate-right-small.png")); + Gtk::Image* ipers_rotate_right = Gtk::manage(new RTImage("rotate-left-small.png")); + + Gtk::HBox* method_hbox = Gtk::manage (new Gtk::HBox()); + Gtk::Label* method_label = Gtk::manage (new Gtk::Label (M("TP_PERSPECTIVE_METHOD") + ": ")); + method = Gtk::manage (new MyComboBoxText ()); + method->append (M("TP_PERSPECTIVE_METHOD_SIMPLE")); + method->append (M("TP_PERSPECTIVE_METHOD_CAMERA_BASED")); + method_hbox->pack_start(*method_label, Gtk::PACK_SHRINK); + method_hbox->pack_start(*method); + pack_start(*method_hbox); + + simple = Gtk::manage( new Gtk::VBox() ); vert = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_VERTICAL"), -100, 100, 0.1, 0, ipersVL, ipersVR)); vert->setAdjusterListener (this); - pack_start (*horiz); - pack_start (*vert); + horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_HORIZONTAL"), -100, 100, 0.1, 0, ipersHL, ipersHR)); + horiz->setAdjusterListener (this); + + camera_based = Gtk::manage( new Gtk::VBox() ); + + Gtk::Frame* camera_frame = Gtk::manage (new Gtk::Frame + (M("TP_PERSPECTIVE_CAMERA_FRAME"))); + camera_frame->set_label_align(0.025, 0.5); + + Gtk::VBox* camera_vbox = Gtk::manage (new Gtk::VBox()); + + camera_focal_length = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH"), 0.5, 2000, 0.01, 24)); + camera_focal_length->setAdjusterListener (this); + + camera_crop_factor = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_CROP_FACTOR"), 0.1, 30, 0.01, 1)); + camera_crop_factor->setAdjusterListener (this); + + camera_shift_horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL"), -50, 50, 0.01, 0)); + camera_shift_horiz->setAdjusterListener (this); + + camera_shift_vert = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL"), -50, 50, 0.01, 0)); + camera_shift_vert->setAdjusterListener (this); + + camera_roll = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_ROLL"), -45, 45, 0.01, 0)); + camera_roll->setAdjusterListener (this); + + camera_pitch = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_PITCH"), + -60, 60, 0.1, 0, ipers_cam_pitch_left, ipers_cam_pitch_right)); + camera_pitch->setAdjusterListener (this); + + camera_yaw = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_CAMERA_YAW"), + -60, 60, 0.1, 0, ipers_cam_yaw_left, ipers_cam_yaw_right)); + camera_yaw->setAdjusterListener (this); + + auto_pitch = Gtk::manage (new Gtk::Button ()); + auto_pitch->set_image(*ipers_auto_pitch); + auto_pitch->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_pitch) ); + + auto_yaw = Gtk::manage (new Gtk::Button ()); + auto_yaw->set_image(*ipers_auto_yaw); + auto_yaw->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_yaw) ); + + auto_pitch_yaw = Gtk::manage (new Gtk::Button ()); + auto_pitch_yaw->set_image(*ipers_auto_pitch_yaw); + auto_pitch_yaw->signal_pressed().connect( sigc::bind(sigc::mem_fun(*this, &PerspCorrection::autoCorrectionPressed), auto_pitch_yaw) ); + + Gtk::HBox* auto_hbox = Gtk::manage (new Gtk::HBox()); + Gtk::Label* auto_label = Gtk::manage (new Gtk::Label (M("GENERAL_AUTO") + ": ")); + auto_hbox->pack_start(*auto_label, Gtk::PACK_SHRINK); + + Gtk::Frame* pca_frame = Gtk::manage (new Gtk::Frame + (M("TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME"))); + pca_frame->set_label_align(0.025, 0.5); + + Gtk::VBox* pca_vbox = Gtk::manage (new Gtk::VBox()); + + projection_shift_horiz = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL"), -100, 100, 0.01, 0)); + projection_shift_horiz->setAdjusterListener (this); + + projection_shift_vert = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL"), -100, 100, 0.01, 0)); + projection_shift_vert->setAdjusterListener (this); + + projection_rotate = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_ROTATE"), -45, 45, 0.01, 0, ipers_rotate_left, ipers_rotate_right)); + projection_rotate->setAdjusterListener (this); + + Gtk::Frame* recovery_frame = Gtk::manage (new Gtk::Frame + (M("TP_PERSPECTIVE_RECOVERY_FRAME"))); + recovery_frame->set_label_align(0.025, 0.5); + + Gtk::VBox* recovery_vbox = Gtk::manage (new Gtk::VBox()); + + projection_pitch = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_PITCH"), -60, 60, 0.1, 0, ipers_proj_pitch_left, ipers_proj_pitch_right)); + projection_pitch->setAdjusterListener (this); + + projection_yaw = Gtk::manage (new Adjuster (M("TP_PERSPECTIVE_PROJECTION_YAW"), -60, 60, 0.1, 0, ipers_proj_yaw_left, ipers_proj_yaw_right)); + projection_yaw->setAdjusterListener (this); + + simple->pack_start (*horiz); + simple->pack_start (*vert); + + auto_hbox->pack_start (*auto_pitch); + auto_hbox->pack_start (*auto_yaw); + auto_hbox->pack_start (*auto_pitch_yaw); + + camera_vbox->pack_start (*camera_focal_length); + camera_vbox->pack_start (*camera_crop_factor); + camera_vbox->pack_start (*camera_shift_horiz); + camera_vbox->pack_start (*camera_shift_vert); + camera_vbox->pack_start (*camera_roll); + camera_vbox->pack_start (*camera_pitch); + camera_vbox->pack_start (*camera_yaw); + camera_vbox->pack_start (*auto_hbox); + camera_frame->add(*camera_vbox); + camera_based->pack_start(*camera_frame); + + pca_vbox->pack_start (*projection_shift_horiz); + pca_vbox->pack_start (*projection_shift_vert); + pca_vbox->pack_start (*projection_rotate); + pca_frame->add(*pca_vbox); + camera_based->pack_start(*pca_frame); + + recovery_vbox->pack_start (*projection_yaw); + recovery_vbox->pack_start (*projection_pitch); + recovery_frame->add(*recovery_vbox); + camera_based->pack_start(*recovery_frame); + + pack_start(*simple); + pack_start(*camera_based); horiz->setLogScale(2, 0); vert->setLogScale(2, 0); + camera_focal_length->setLogScale(4000, 0.5); + camera_crop_factor->setLogScale(300, 0.1); + + method->signal_changed().connect(sigc::mem_fun(*this, &PerspCorrection::methodChanged)); show_all(); } @@ -56,10 +201,41 @@ void PerspCorrection::read (const ProcParams* pp, const ParamsEdited* pedited) if (pedited) { horiz->setEditedState (pedited->perspective.horizontal ? Edited : UnEdited); vert->setEditedState (pedited->perspective.vertical ? Edited : UnEdited); + camera_crop_factor->setEditedState (pedited->perspective.camera_crop_factor ? Edited : UnEdited); + camera_focal_length->setEditedState (pedited->perspective.camera_focal_length ? Edited : UnEdited); + camera_pitch->setEditedState (pedited->perspective.camera_pitch ? Edited : UnEdited); + camera_roll->setEditedState (pedited->perspective.camera_roll ? Edited : UnEdited); + camera_shift_horiz->setEditedState (pedited->perspective.camera_shift_horiz ? Edited : UnEdited); + camera_shift_vert->setEditedState (pedited->perspective.camera_shift_vert ? Edited : UnEdited); + camera_yaw->setEditedState (pedited->perspective.camera_yaw ? Edited : UnEdited); + projection_pitch->setEditedState (pedited->perspective.projection_pitch ? Edited : UnEdited); + projection_rotate->setEditedState (pedited->perspective.projection_rotate ? Edited : UnEdited); + projection_shift_horiz->setEditedState (pedited->perspective.projection_shift_horiz ? Edited : UnEdited); + projection_shift_vert->setEditedState (pedited->perspective.projection_shift_vert ? Edited : UnEdited); + projection_yaw->setEditedState (pedited->perspective.projection_yaw ? Edited : UnEdited); } horiz->setValue (pp->perspective.horizontal); vert->setValue (pp->perspective.vertical); + setFocalLengthValue (pp, metadata); + camera_pitch->setValue (pp->perspective.camera_pitch); + camera_roll->setValue (pp->perspective.camera_roll); + camera_shift_horiz->setValue (pp->perspective.camera_shift_horiz); + camera_shift_vert->setValue (pp->perspective.camera_shift_vert); + camera_yaw->setValue (pp->perspective.camera_yaw); + projection_pitch->setValue (pp->perspective.projection_pitch); + projection_rotate->setValue (pp->perspective.projection_rotate); + projection_shift_horiz->setValue (pp->perspective.projection_shift_horiz); + projection_shift_vert->setValue (pp->perspective.projection_shift_vert); + projection_yaw->setValue (pp->perspective.projection_yaw); + + if (pedited && !pedited->perspective.method) { + method->set_active (2); + } else if (pp->perspective.method == "simple") { + method->set_active (0); + } else if (pp->perspective.method == "camera_based") { + method->set_active (1); + } enableListener (); } @@ -69,10 +245,41 @@ void PerspCorrection::write (ProcParams* pp, ParamsEdited* pedited) pp->perspective.horizontal = horiz->getValue (); pp->perspective.vertical = vert->getValue (); + pp->perspective.camera_crop_factor= camera_crop_factor->getValue (); + pp->perspective.camera_focal_length = camera_focal_length->getValue (); + pp->perspective.camera_pitch = camera_pitch->getValue (); + pp->perspective.camera_roll = camera_roll->getValue (); + pp->perspective.camera_shift_horiz = camera_shift_horiz->getValue (); + pp->perspective.camera_shift_vert = camera_shift_vert->getValue (); + pp->perspective.camera_yaw = camera_yaw->getValue (); + pp->perspective.projection_pitch = projection_pitch->getValue (); + pp->perspective.projection_rotate = projection_rotate->getValue (); + pp->perspective.projection_shift_horiz = projection_shift_horiz->getValue (); + pp->perspective.projection_shift_vert = projection_shift_vert->getValue (); + pp->perspective.projection_yaw = projection_yaw->getValue (); + + if (method->get_active_row_number() == 0) { + pp->perspective.method = "simple"; + } else if (method->get_active_row_number() == 1) { + pp->perspective.method = "camera_based"; + } if (pedited) { + pedited->perspective.method = method->get_active_row_number() != 2; pedited->perspective.horizontal = horiz->getEditedState (); pedited->perspective.vertical = vert->getEditedState (); + pedited->perspective.camera_crop_factor= camera_crop_factor->getEditedState (); + pedited->perspective.camera_focal_length = camera_focal_length->getEditedState (); + pedited->perspective.camera_pitch = camera_pitch->getEditedState(); + pedited->perspective.camera_roll = camera_roll->getEditedState(); + pedited->perspective.camera_shift_horiz = camera_shift_horiz->getEditedState(); + pedited->perspective.camera_shift_vert = camera_shift_vert->getEditedState(); + pedited->perspective.camera_yaw = camera_yaw->getEditedState(); + pedited->perspective.projection_pitch = projection_pitch->getEditedState(); + pedited->perspective.projection_rotate = projection_rotate->getEditedState(); + pedited->perspective.projection_shift_horiz = projection_shift_horiz->getEditedState(); + pedited->perspective.projection_shift_vert = projection_shift_vert->getEditedState(); + pedited->perspective.projection_yaw = projection_yaw->getEditedState(); } } @@ -81,28 +288,175 @@ void PerspCorrection::setDefaults (const ProcParams* defParams, const ParamsEdit horiz->setDefault (defParams->perspective.horizontal); vert->setDefault (defParams->perspective.vertical); + camera_crop_factor->setDefault (defParams->perspective.camera_crop_factor); + camera_focal_length->setDefault (defParams->perspective.camera_focal_length); + camera_pitch->setDefault (defParams->perspective.camera_pitch); + camera_roll->setDefault (defParams->perspective.camera_roll); + camera_shift_horiz->setDefault (defParams->perspective.camera_shift_horiz); + camera_shift_vert->setDefault (defParams->perspective.camera_shift_vert); + camera_yaw->setDefault (defParams->perspective.camera_yaw); + projection_pitch->setDefault (defParams->perspective.projection_pitch); + projection_rotate->setDefault (defParams->perspective.projection_rotate); + projection_shift_horiz->setDefault (defParams->perspective.projection_shift_horiz); + projection_shift_vert->setDefault (defParams->perspective.projection_shift_vert); + projection_yaw->setDefault (defParams->perspective.projection_yaw); if (pedited) { horiz->setDefaultEditedState (pedited->perspective.horizontal ? Edited : UnEdited); vert->setDefaultEditedState (pedited->perspective.vertical ? Edited : UnEdited); + camera_crop_factor->setDefaultEditedState (pedited->perspective.camera_crop_factor ? Edited : UnEdited); + camera_focal_length->setDefaultEditedState (pedited->perspective.camera_focal_length ? Edited : UnEdited); + camera_pitch->setDefaultEditedState (pedited->perspective.camera_pitch ? Edited : UnEdited); + camera_roll->setDefaultEditedState (pedited->perspective.camera_roll ? Edited : UnEdited); + camera_shift_horiz->setDefaultEditedState (pedited->perspective.camera_shift_horiz ? Edited : UnEdited); + camera_shift_vert->setDefaultEditedState (pedited->perspective.camera_shift_vert ? Edited : UnEdited); + camera_yaw->setDefaultEditedState (pedited->perspective.camera_yaw ? Edited : UnEdited); + projection_pitch->setDefaultEditedState (pedited->perspective.projection_pitch ? Edited : UnEdited); + projection_rotate->setDefaultEditedState (pedited->perspective.projection_rotate ? Edited : UnEdited); + projection_shift_horiz->setDefaultEditedState (pedited->perspective.projection_shift_horiz ? Edited : UnEdited); + projection_shift_vert->setDefaultEditedState (pedited->perspective.projection_shift_vert ? Edited : UnEdited); + projection_yaw->setDefaultEditedState (pedited->perspective.projection_yaw ? Edited : UnEdited); } else { horiz->setDefaultEditedState (Irrelevant); vert->setDefaultEditedState (Irrelevant); + camera_crop_factor->setDefaultEditedState (Irrelevant); + camera_focal_length->setDefaultEditedState (Irrelevant); + camera_pitch->setDefaultEditedState (Irrelevant); + camera_roll->setDefaultEditedState (Irrelevant); + camera_shift_horiz->setDefaultEditedState (Irrelevant); + camera_shift_vert->setDefaultEditedState (Irrelevant); + camera_yaw->setDefaultEditedState (Irrelevant); + projection_pitch->setDefaultEditedState (Irrelevant); + projection_rotate->setDefaultEditedState (Irrelevant); + projection_shift_horiz->setDefaultEditedState (Irrelevant); + projection_shift_vert->setDefaultEditedState (Irrelevant); + projection_yaw->setDefaultEditedState (Irrelevant); } } void PerspCorrection::adjusterChanged(Adjuster* a, double newval) { if (listener) { - listener->panelChanged (EvPerspCorr, Glib::ustring::compose ("%1=%3\n%2=%4", M("TP_PERSPECTIVE_HORIZONTAL"), M("TP_PERSPECTIVE_VERTICAL"), horiz->getValue(), vert->getValue())); + if (a == horiz || a == vert) { + listener->panelChanged (EvPerspCorr, + Glib::ustring::compose("%1=%2\n%3=%4", + M("TP_PERSPECTIVE_HORIZONTAL"), + horiz->getValue(), + M("TP_PERSPECTIVE_VERTICAL"), + vert->getValue())); + } else if (a == camera_focal_length || a == camera_crop_factor) { + listener->panelChanged (EvPerspCamFocalLength, + Glib::ustring::compose("%1=%2\n%3=%4", + M("TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH"), + camera_focal_length->getValue(), + M("TP_PERSPECTIVE_CAMERA_CROP_FACTOR"), + camera_crop_factor->getValue())); + } else if (a == camera_shift_horiz || a == camera_shift_vert) { + listener->panelChanged (EvPerspCamShift, + Glib::ustring::compose("%1=%2\n%3=%4", + M("TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL"), + camera_shift_horiz->getValue(), + M("TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL"), + camera_shift_vert->getValue())); + } else if (a == camera_pitch || a == camera_roll|| a == camera_yaw) { + listener->panelChanged (EvPerspCamAngle, + Glib::ustring::compose("%1=%2\n%3=%4\n%5=%6", + M("TP_PERSPECTIVE_CAMERA_ROLL"), + camera_roll->getValue(), + M("TP_PERSPECTIVE_CAMERA_YAW"), + camera_yaw->getValue(), + M("TP_PERSPECTIVE_CAMERA_PITCH"), + camera_pitch->getValue())); + } else if (a == projection_shift_horiz || a == projection_shift_vert) { + listener->panelChanged (EvPerspProjShift, + Glib::ustring::compose("%1=%2\n%3=%4", + M("TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL"), + projection_shift_horiz->getValue(), + M("TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL"), + projection_shift_vert->getValue())); + } else if (a == projection_rotate) { + listener->panelChanged (EvPerspProjRotate, + Glib::ustring::format(projection_rotate->getValue())); + } else if (a == projection_pitch || a == projection_yaw) { + listener->panelChanged (EvPerspProjAngle, + Glib::ustring::compose("%1=%2\n%3=%4", + M("TP_PERSPECTIVE_PROJECTION_PITCH"), + projection_pitch->getValue(), + M("TP_PERSPECTIVE_PROJECTION_YAW"), + projection_yaw->getValue())); + } } } -void PerspCorrection::setAdjusterBehavior (bool badd) +void PerspCorrection::autoCorrectionPressed(Gtk::Button* b) +{ + if (!lens_geom_listener) { + return; + } + + double rot = 0; + double pitch = 0; + double yaw = 0; + + if (b == auto_pitch) { + lens_geom_listener->autoPerspRequested(true, false, rot, pitch, yaw); + } else if (b == auto_yaw) { + lens_geom_listener->autoPerspRequested(false, true, rot, pitch, yaw); + } else if (b == auto_pitch_yaw) { + lens_geom_listener->autoPerspRequested(true, true, rot, pitch, yaw); + } + + disableListener(); + camera_pitch->setValue(pitch); + camera_roll->setValue(rot); + camera_yaw->setValue(yaw); + enableListener(); + + adjusterChanged(camera_pitch, pitch); +} + +void PerspCorrection::methodChanged (void) +{ + + if (!batchMode) { + removeIfThere (this, simple, false); + removeIfThere (this, camera_based, false); + + if (method->get_active_row_number() == 0) { + pack_start (*simple); + } else if (method->get_active_row_number() == 1) { + pack_start (*camera_based); + } + } + + if (listener) { + listener->panelChanged (EvPerspMethod, method->get_active_text ()); + } + +} + +void PerspCorrection::setAdjusterBehavior (bool badd, bool camera_focal_length_add, bool camera_shift_add, bool camera_angle_add, bool projection_angle_add, bool projection_shift_add, bool projection_rotate_add) { horiz->setAddMode(badd); vert->setAddMode(badd); + camera_crop_factor->setAddMode(camera_focal_length_add); + camera_focal_length->setAddMode(camera_focal_length_add); + camera_pitch->setAddMode(camera_angle_add); + camera_roll->setAddMode(camera_angle_add); + camera_shift_horiz->setAddMode(camera_shift_add); + camera_shift_vert->setAddMode(camera_shift_add); + camera_yaw->setAddMode(camera_angle_add); + projection_pitch->setAddMode(projection_angle_add); + projection_rotate->setAddMode(projection_rotate_add); + projection_shift_horiz->setAddMode(projection_shift_add); + projection_shift_vert->setAddMode(projection_shift_add); + projection_yaw->setAddMode(projection_angle_add); +} + +void PerspCorrection::setMetadata (const rtengine::FramesMetaData* metadata) +{ + this->metadata = metadata; } void PerspCorrection::trimValues (rtengine::procparams::ProcParams* pp) @@ -110,6 +464,18 @@ void PerspCorrection::trimValues (rtengine::procparams::ProcParams* pp) horiz->trimValue(pp->perspective.horizontal); vert->trimValue(pp->perspective.vertical); + camera_crop_factor->trimValue(pp->perspective.camera_crop_factor); + camera_focal_length->trimValue(pp->perspective.camera_focal_length); + camera_pitch->trimValue(pp->perspective.camera_pitch); + camera_roll->trimValue(pp->perspective.camera_roll); + camera_shift_horiz->trimValue(pp->perspective.camera_shift_horiz); + camera_shift_vert->trimValue(pp->perspective.camera_shift_vert); + camera_yaw->trimValue(pp->perspective.camera_yaw); + projection_pitch->trimValue(pp->perspective.projection_pitch); + projection_rotate->trimValue(pp->perspective.projection_rotate); + projection_shift_horiz->trimValue(pp->perspective.projection_shift_horiz); + projection_shift_vert->trimValue(pp->perspective.projection_shift_vert); + projection_yaw->trimValue(pp->perspective.projection_yaw); } void PerspCorrection::setBatchMode (bool batchMode) @@ -118,4 +484,68 @@ void PerspCorrection::setBatchMode (bool batchMode) ToolPanel::setBatchMode (batchMode); horiz->showEditedCB (); vert->showEditedCB (); + camera_crop_factor->showEditedCB (); + camera_focal_length->showEditedCB (); + camera_pitch->showEditedCB (); + camera_roll->showEditedCB (); + camera_shift_horiz->showEditedCB (); + camera_shift_vert->showEditedCB (); + camera_yaw->showEditedCB (); + projection_pitch->showEditedCB (); + projection_rotate->showEditedCB (); + projection_shift_horiz->showEditedCB (); + projection_shift_vert->showEditedCB (); + projection_yaw->showEditedCB (); + + auto_pitch->set_sensitive(false); + auto_yaw->set_sensitive(false); + auto_pitch_yaw->set_sensitive(false); + + method->append (M("GENERAL_UNCHANGED")); +} + +void PerspCorrection::setFocalLengthValue (const ProcParams* pparams, const FramesMetaData* metadata) +{ + const double pp_crop_factor = pparams->perspective.camera_crop_factor; + const double pp_focal_length = pparams->perspective.camera_focal_length; + double default_crop_factor = 1.0; + double default_focal_length = 24.0; + + // Defaults from metadata. + if (metadata && (pp_crop_factor <= 0 || pp_focal_length <= 0)) { + const double fl = metadata->getFocalLen(); + const double fl35 = metadata->getFocalLen35mm(); + + if (fl <= 0) { + if (fl35 <= 0) { + // No focal length data. + } else { + // 35mm focal length available. + default_focal_length = fl35; + } + } else { + if (fl35 <= 0) { + // Focal length available. + default_focal_length = fl; + } else { + // Focal length and 35mm equivalent available. + default_focal_length = fl; + default_crop_factor = fl35 / fl; + } + } + } + + // Change value if those from the ProcParams are invalid. + if (pp_crop_factor > 0) { + camera_crop_factor->setValue(pparams->perspective.camera_crop_factor); + } else { + camera_crop_factor->setDefault(default_crop_factor); + camera_crop_factor->setValue(default_crop_factor); + } + if (pp_focal_length > 0) { + camera_focal_length->setValue(pparams->perspective.camera_focal_length); + } else { + camera_focal_length->setDefault(default_focal_length); + camera_focal_length->setValue(default_focal_length); + } } diff --git a/rtgui/perspective.h b/rtgui/perspective.h index 0564479de..1be392f7c 100644 --- a/rtgui/perspective.h +++ b/rtgui/perspective.h @@ -21,6 +21,7 @@ #include #include "adjuster.h" +#include "lensgeomlistener.h" #include "toolpanel.h" class PerspCorrection final : @@ -30,8 +31,37 @@ class PerspCorrection final : { protected: + MyComboBoxText* method; + Gtk::VBox* simple; Adjuster* horiz; Adjuster* vert; + Gtk::Button* auto_pitch; + Gtk::Button* auto_yaw; + Gtk::Button* auto_pitch_yaw; + Gtk::VBox* camera_based; + Adjuster* camera_crop_factor; + Adjuster* camera_focal_length; + Adjuster* camera_pitch; + Adjuster* camera_roll; + Adjuster* camera_shift_horiz; + Adjuster* camera_shift_vert; + Adjuster* camera_yaw; + Adjuster* projection_pitch; + Adjuster* projection_rotate; + Adjuster* projection_shift_horiz; + Adjuster* projection_shift_vert; + Adjuster* projection_yaw; + rtengine::ProcEvent EvPerspCamFocalLength; + rtengine::ProcEvent EvPerspCamShift; + rtengine::ProcEvent EvPerspCamAngle; + rtengine::ProcEvent EvPerspMethod; + rtengine::ProcEvent EvPerspProjShift; + rtengine::ProcEvent EvPerspProjRotate; + rtengine::ProcEvent EvPerspProjAngle; + LensGeomListener* lens_geom_listener; + const rtengine::FramesMetaData* metadata; + + void setFocalLengthValue (const rtengine::procparams::ProcParams* pparams, const rtengine::FramesMetaData* metadata); public: @@ -43,6 +73,13 @@ public: void setBatchMode (bool batchMode) override; void adjusterChanged (Adjuster* a, double newval) override; - void setAdjusterBehavior (bool badd); + void autoCorrectionPressed (Gtk::Button* b); + void methodChanged (void); + void setAdjusterBehavior (bool badd, bool camera_focal_length_add, bool camera_shift_add, bool camera_angle_add, bool projection_angle_add, bool projection_shift_add, bool projection_rotate_add); + void setLensGeomListener (LensGeomListener* listener) + { + lens_geom_listener = listener; + } + void setMetadata (const rtengine::FramesMetaData* metadata); void trimValues (rtengine::procparams::ProcParams* pp) override; }; diff --git a/rtgui/ppversion.h b/rtgui/ppversion.h index 6a670733b..5aef01806 100644 --- a/rtgui/ppversion.h +++ b/rtgui/ppversion.h @@ -1,11 +1,13 @@ #pragma once // This number has to be incremented whenever the PP3 file format is modified or the behaviour of a tool changes -#define PPVERSION 347 +#define PPVERSION 348 #define PPVERSION_AEXP 301 //value of PPVERSION when auto exposure algorithm was modified /* Log of version changes + 348 2018-09-25 + Added Locallab tool parameters 347 2019-11-17 added special values in filmNegative for backwards compatibility with previous channel scaling method 346 2019-01-01 diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 5268948cc..aba30fbf2 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -56,49 +56,49 @@ extern Glib::ustring argv0; Glib::RefPtr themecss; Glib::RefPtr fontcss; -Preferences::Preferences (RTWindow *rtwindow) - : Gtk::Dialog (M ("MAIN_BUTTON_PREFERENCES"), *rtwindow, true) +Preferences::Preferences(RTWindow *rtwindow) + : Gtk::Dialog(M("MAIN_BUTTON_PREFERENCES"), *rtwindow, true) , regex(Glib::Regex::create (THEMEREGEXSTR, Glib::RegexCompileFlags::REGEX_CASELESS)) - , splash (nullptr) - , rprofiles (nullptr) - , iprofiles (nullptr) - , parent (rtwindow) - , newFont (false) - , newCPFont (false) + , splash(nullptr) + , rprofiles(nullptr) + , iprofiles(nullptr) + , parent(rtwindow) + , newFont(false) + , newCPFont(false) { - moptions.copyFrom (&options); + moptions.copyFrom(&options); - set_size_request (650, -1); - set_default_size (options.preferencesWidth, options.preferencesHeight); + set_size_request(650, -1); + set_default_size(options.preferencesWidth, options.preferencesHeight); - Pango::FontDescription defaultFont = get_style_context ()->get_font(); - initialFontFamily = defaultFont.get_family (); - initialFontSize = defaultFont.get_size () / Pango::SCALE; + Pango::FontDescription defaultFont = get_style_context()->get_font(); + initialFontFamily = defaultFont.get_family(); + initialFontSize = defaultFont.get_size() / Pango::SCALE; - Gtk::Box* mainBox = get_content_area (); + Gtk::Box* mainBox = get_content_area(); //GTK318 #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 20 - mainBox->set_spacing (8); + mainBox->set_spacing(8); #endif //GTK318 - Gtk::Notebook* nb = Gtk::manage (new Gtk::Notebook ()); + Gtk::Notebook* nb = Gtk::manage(new Gtk::Notebook()); nb->set_scrollable(true); - nb->set_name ("PrefNotebook"); - mainBox->pack_start (*nb); + nb->set_name("PrefNotebook"); + mainBox->pack_start(*nb); - Gtk::Button* about = Gtk::manage (new Gtk::Button (M ("GENERAL_ABOUT"))); - Gtk::Button* ok = Gtk::manage (new Gtk::Button (M ("GENERAL_OK"))); - Gtk::Button* cancel = Gtk::manage (new Gtk::Button (M ("GENERAL_CANCEL"))); + Gtk::Button* about = Gtk::manage(new Gtk::Button(M("GENERAL_ABOUT"))); + Gtk::Button* ok = Gtk::manage(new Gtk::Button(M("GENERAL_OK"))); + Gtk::Button* cancel = Gtk::manage(new Gtk::Button(M("GENERAL_CANCEL"))); - about->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::aboutPressed) ); - ok->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::okPressed) ); - cancel->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::cancelPressed) ); + about->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::aboutPressed)); + ok->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::okPressed)); + cancel->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::cancelPressed)); - get_action_area()->pack_start (*about); - get_action_area()->pack_end (*ok); - get_action_area()->pack_end (*cancel); + get_action_area()->pack_start(*about); + get_action_area()->pack_end(*ok); + get_action_area()->pack_end(*cancel); nb->append_page(*getGeneralPanel(), M("PREFERENCES_TAB_GENERAL")); nb->append_page(*getImageProcessingPanel(), M("PREFERENCES_TAB_IMPROC")); @@ -111,29 +111,29 @@ Preferences::Preferences (RTWindow *rtwindow) #if defined(WIN32) || defined(__linux__) nb->append_page(*getSoundsPanel(), M("PREFERENCES_TAB_SOUND")); #endif - nb->set_current_page (0); + nb->set_current_page(0); - ProfileStore::getInstance()->addListener (this); + ProfileStore::getInstance()->addListener(this); - fillPreferences (); + fillPreferences(); - show_all_children (); + show_all_children(); } -Preferences::~Preferences () +Preferences::~Preferences() { - ProfileStore::getInstance()->removeListener (this); - get_size (options.preferencesWidth, options.preferencesHeight); + ProfileStore::getInstance()->removeListener(this); + get_size(options.preferencesWidth, options.preferencesHeight); } int Preferences::getThemeRowNumber (const Glib::ustring& longThemeFName) { - if (regex->match (longThemeFName + ".css", matchInfo)) { + if (regex->match(longThemeFName + ".css", matchInfo)) { for (size_t i = 0 ; i < themeFNames.size(); ++i) { - if (themeFNames.at (i).longFName == longThemeFName) { + if (themeFNames.at(i).longFName == longThemeFName) { return (int)i; } } @@ -142,43 +142,43 @@ int Preferences::getThemeRowNumber (const Glib::ustring& longThemeFName) return -1; } -Gtk::Widget* Preferences::getBatchProcPanel () +Gtk::Widget* Preferences::getBatchProcPanel() { swBatchProc = Gtk::manage(new Gtk::ScrolledWindow()); swBatchProc->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); Gtk::VBox* vbBatchProc = Gtk::manage (new Gtk::VBox ()); - Gtk::ScrolledWindow* behscrollw = Gtk::manage (new Gtk::ScrolledWindow ()); - behscrollw->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - behscrollw->set_size_request (-1, 60); - Gtk::VBox* vbbeh = Gtk::manage ( new Gtk::VBox () ); - vbbeh->pack_start (*behscrollw, Gtk::PACK_EXPAND_WIDGET); - Gtk::Frame* behFrame = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_BEHAVIOR"))); - behFrame->add (*vbbeh); + Gtk::ScrolledWindow* behscrollw = Gtk::manage(new Gtk::ScrolledWindow()); + behscrollw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + behscrollw->set_size_request(-1, 60); + Gtk::VBox* vbbeh = Gtk::manage(new Gtk::VBox()); + vbbeh->pack_start(*behscrollw, Gtk::PACK_EXPAND_WIDGET); + Gtk::Frame* behFrame = Gtk::manage(new Gtk::Frame(M("PREFERENCES_BEHAVIOR"))); + behFrame->add(*vbbeh); vbBatchProc->pack_start (*behFrame, Gtk::PACK_EXPAND_WIDGET, 4); - Gtk::TreeView* behTreeView = Gtk::manage (new Gtk::TreeView ()); - behscrollw->add (*behTreeView); + Gtk::TreeView* behTreeView = Gtk::manage(new Gtk::TreeView()); + behscrollw->add(*behTreeView); - behModel = Gtk::TreeStore::create (behavColumns); - behTreeView->set_model (behModel); + behModel = Gtk::TreeStore::create(behavColumns); + behTreeView->set_model(behModel); - behTreeView->append_column (M ("PREFERENCES_PROPERTY"), behavColumns.label); - behTreeView->append_column_editable (M ("PREFERENCES_ADD"), behavColumns.badd); - behTreeView->append_column_editable (M ("PREFERENCES_SET"), behavColumns.bset); + behTreeView->append_column(M("PREFERENCES_PROPERTY"), behavColumns.label); + behTreeView->append_column_editable(M("PREFERENCES_ADD"), behavColumns.badd); + behTreeView->append_column_editable(M("PREFERENCES_SET"), behavColumns.bset); - Gtk::CellRendererToggle* cr_add = static_cast (behTreeView->get_column (1)->get_first_cell()); - Gtk::CellRendererToggle* cr_set = static_cast (behTreeView->get_column (2)->get_first_cell()); + Gtk::CellRendererToggle* cr_add = static_cast(behTreeView->get_column(1)->get_first_cell()); + Gtk::CellRendererToggle* cr_set = static_cast(behTreeView->get_column(2)->get_first_cell()); - cr_add->set_radio (true); - cr_add->set_property ("xalign", 0.0f); - sigc::connection addc = cr_add->signal_toggled().connect (sigc::mem_fun (*this, &Preferences::behAddRadioToggled)); - cr_set->set_radio (true); - cr_set->set_property ("xalign", 0.0f); - sigc::connection setc = cr_set->signal_toggled().connect (sigc::mem_fun (*this, &Preferences::behSetRadioToggled)); + cr_add->set_radio(true); + cr_add->set_property("xalign", 0.0f); + sigc::connection addc = cr_add->signal_toggled().connect(sigc::mem_fun(*this, &Preferences::behAddRadioToggled)); + cr_set->set_radio(true); + cr_set->set_property("xalign", 0.0f); + sigc::connection setc = cr_set->signal_toggled().connect(sigc::mem_fun(*this, &Preferences::behSetRadioToggled)); - behTreeView->get_column (1)->add_attribute (*cr_add, "visible", behavColumns.visible); - behTreeView->get_column (2)->add_attribute (*cr_set, "visible", behavColumns.visible); + behTreeView->get_column(1)->add_attribute(*cr_add, "visible", behavColumns.visible); + behTreeView->get_column(2)->add_attribute(*cr_set, "visible", behavColumns.visible); // fill model Gtk::TreeModel::iterator mi, ci; @@ -186,62 +186,62 @@ Gtk::Widget* Preferences::getBatchProcPanel () /* * The TRUE/FALSE values of appendBehavList are replaced by the one defined in options.cc, */ - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_EXPOSURE_LABEL")); - appendBehavList (mi, M ("TP_EXPOSURE_EXPCOMP"), ADDSET_TC_EXPCOMP, false); - appendBehavList (mi, M ("TP_EXPOSURE_COMPRHIGHLIGHTS"), ADDSET_TC_HLCOMPAMOUNT, false); - appendBehavList (mi, M ("TP_EXPOSURE_COMPRHIGHLIGHTSTHRESHOLD"), ADDSET_TC_HLCOMPTHRESH, false); - appendBehavList (mi, M ("TP_EXPOSURE_BLACKLEVEL"), ADDSET_TC_BLACKLEVEL, false); - appendBehavList (mi, M ("TP_EXPOSURE_COMPRSHADOWS"), ADDSET_TC_SHCOMP, false); - appendBehavList (mi, M ("TP_EXPOSURE_BRIGHTNESS"), ADDSET_TC_BRIGHTNESS, false); - appendBehavList (mi, M ("TP_EXPOSURE_CONTRAST"), ADDSET_TC_CONTRAST, false); - appendBehavList (mi, M ("TP_EXPOSURE_SATURATION"), ADDSET_TC_SATURATION, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_EXPOSURE_LABEL")); + appendBehavList(mi, M("TP_EXPOSURE_EXPCOMP"), ADDSET_TC_EXPCOMP, false); + appendBehavList(mi, M("TP_EXPOSURE_COMPRHIGHLIGHTS"), ADDSET_TC_HLCOMPAMOUNT, false); + appendBehavList(mi, M("TP_EXPOSURE_COMPRHIGHLIGHTSTHRESHOLD"), ADDSET_TC_HLCOMPTHRESH, false); + appendBehavList(mi, M("TP_EXPOSURE_BLACKLEVEL"), ADDSET_TC_BLACKLEVEL, false); + appendBehavList(mi, M("TP_EXPOSURE_COMPRSHADOWS"), ADDSET_TC_SHCOMP, false); + appendBehavList(mi, M("TP_EXPOSURE_BRIGHTNESS"), ADDSET_TC_BRIGHTNESS, false); + appendBehavList(mi, M("TP_EXPOSURE_CONTRAST"), ADDSET_TC_CONTRAST, false); + appendBehavList(mi, M("TP_EXPOSURE_SATURATION"), ADDSET_TC_SATURATION, false); mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_EPD_LABEL")); - appendBehavList (mi, M ("TP_EPD_STRENGTH"), ADDSET_EPD_STRENGTH, false); - appendBehavList (mi, M ("TP_EPD_GAMMA"), ADDSET_EPD_GAMMA, false); - appendBehavList (mi, M ("TP_EPD_EDGESTOPPING"), ADDSET_EPD_EDGESTOPPING, false); - appendBehavList (mi, M ("TP_EPD_SCALE"), ADDSET_EPD_SCALE, false); - appendBehavList (mi, M ("TP_EPD_REWEIGHTINGITERATES"), ADDSET_EPD_REWEIGHTINGITERATES, false); + mi->set_value(behavColumns.label, M("TP_EPD_LABEL")); + appendBehavList(mi, M("TP_EPD_STRENGTH"), ADDSET_EPD_STRENGTH, false); + appendBehavList(mi, M("TP_EPD_GAMMA"), ADDSET_EPD_GAMMA, false); + appendBehavList(mi, M("TP_EPD_EDGESTOPPING"), ADDSET_EPD_EDGESTOPPING, false); + appendBehavList(mi, M("TP_EPD_SCALE"), ADDSET_EPD_SCALE, false); + appendBehavList(mi, M("TP_EPD_REWEIGHTINGITERATES"), ADDSET_EPD_REWEIGHTINGITERATES, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_TM_FATTAL_LABEL")); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_TM_FATTAL_LABEL")); appendBehavList (mi, M ("TP_TM_FATTAL_AMOUNT"), ADDSET_FATTAL_AMOUNT, false); appendBehavList (mi, M ("TP_TM_FATTAL_THRESHOLD"), ADDSET_FATTAL_THRESHOLD, false); appendBehavList (mi, M ("TP_TM_FATTAL_ANCHOR"), ADDSET_FATTAL_ANCHOR, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_RETINEX_LABEL")); - appendBehavList (mi, M ("TP_RETINEX_STRENGTH"), ADDSET_RETI_STR, false); - appendBehavList (mi, M ("TP_RETINEX_NEIGHBOR"), ADDSET_RETI_NEIGH, false); - appendBehavList (mi, M ("TP_RETINEX_VARIANCE"), ADDSET_RETI_VART, false); - appendBehavList (mi, M ("TP_RETINEX_GAMMA"), ADDSET_RETI_GAM, false); - appendBehavList (mi, M ("TP_RETINEX_SLOPE"), ADDSET_RETI_SLO, false); - appendBehavList (mi, M ("TP_RETINEX_GAIN"), ADDSET_RETI_GAIN, false); - appendBehavList (mi, M ("TP_RETINEX_OFFSET"), ADDSET_RETI_OFFS, false); - appendBehavList (mi, M ("TP_RETINEX_THRESHOLD"), ADDSET_RETI_LIMD, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_RETINEX_LABEL")); + appendBehavList(mi, M("TP_RETINEX_STRENGTH"), ADDSET_RETI_STR, false); + appendBehavList(mi, M("TP_RETINEX_NEIGHBOR"), ADDSET_RETI_NEIGH, false); + appendBehavList(mi, M("TP_RETINEX_VARIANCE"), ADDSET_RETI_VART, false); + appendBehavList(mi, M("TP_RETINEX_GAMMA"), ADDSET_RETI_GAM, false); + appendBehavList(mi, M("TP_RETINEX_SLOPE"), ADDSET_RETI_SLO, false); + appendBehavList(mi, M("TP_RETINEX_GAIN"), ADDSET_RETI_GAIN, false); + appendBehavList(mi, M("TP_RETINEX_OFFSET"), ADDSET_RETI_OFFS, false); + appendBehavList(mi, M("TP_RETINEX_THRESHOLD"), ADDSET_RETI_LIMD, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_SHADOWSHLIGHTS_LABEL")); - appendBehavList (mi, M ("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), ADDSET_SH_HIGHLIGHTS, false); - appendBehavList (mi, M ("TP_SHADOWSHLIGHTS_SHADOWS"), ADDSET_SH_SHADOWS, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_SHADOWSHLIGHTS_LABEL")); + appendBehavList(mi, M("TP_SHADOWSHLIGHTS_HIGHLIGHTS"), ADDSET_SH_HIGHLIGHTS, false); + appendBehavList(mi, M("TP_SHADOWSHLIGHTS_SHADOWS"), ADDSET_SH_SHADOWS, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_LABCURVE_LABEL")); - appendBehavList (mi, M ("TP_LABCURVE_BRIGHTNESS"), ADDSET_LC_BRIGHTNESS, false); - appendBehavList (mi, M ("TP_LABCURVE_CONTRAST"), ADDSET_LC_CONTRAST, false); - appendBehavList (mi, M ("TP_LABCURVE_CHROMATICITY"), ADDSET_LC_CHROMATICITY, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_LABCURVE_LABEL")); + appendBehavList(mi, M("TP_LABCURVE_BRIGHTNESS"), ADDSET_LC_BRIGHTNESS, false); + appendBehavList(mi, M("TP_LABCURVE_CONTRAST"), ADDSET_LC_CONTRAST, false); + appendBehavList(mi, M("TP_LABCURVE_CHROMATICITY"), ADDSET_LC_CHROMATICITY, false); - mi = behModel->append (); // Used for both Resize and Post-Resize sharpening - mi->set_value (behavColumns.label, M ("TP_SHARPENING_LABEL")); + mi = behModel->append(); // Used for both Resize and Post-Resize sharpening + mi->set_value(behavColumns.label, M("TP_SHARPENING_LABEL")); appendBehavList (mi, M ("TP_SHARPENING_CONTRAST"), ADDSET_SHARP_CONTRAST, false); - appendBehavList (mi, M ("TP_SHARPENING_RADIUS"), ADDSET_SHARP_RADIUS, false); - appendBehavList (mi, M ("TP_SHARPENING_AMOUNT"), ADDSET_SHARP_AMOUNT, false); - appendBehavList (mi, M ("TP_SHARPENING_RLD_DAMPING"), ADDSET_SHARP_DAMPING, false); - appendBehavList (mi, M ("TP_SHARPENING_RLD_ITERATIONS"), ADDSET_SHARP_ITER, false); - appendBehavList (mi, M ("TP_SHARPENING_EDTOLERANCE"), ADDSET_SHARP_EDGETOL, false); - appendBehavList (mi, M ("TP_SHARPENING_HALOCONTROL"), ADDSET_SHARP_HALOCTRL, false); + appendBehavList(mi, M("TP_SHARPENING_RADIUS"), ADDSET_SHARP_RADIUS, false); + appendBehavList(mi, M("TP_SHARPENING_AMOUNT"), ADDSET_SHARP_AMOUNT, false); + appendBehavList(mi, M("TP_SHARPENING_RLD_DAMPING"), ADDSET_SHARP_DAMPING, false); + appendBehavList(mi, M("TP_SHARPENING_RLD_ITERATIONS"), ADDSET_SHARP_ITER, false); + appendBehavList(mi, M("TP_SHARPENING_EDTOLERANCE"), ADDSET_SHARP_EDGETOL, false); + appendBehavList(mi, M("TP_SHARPENING_HALOCONTROL"), ADDSET_SHARP_HALOCTRL, false); mi = behModel->append(); mi->set_value(behavColumns.label, M("TP_LOCALCONTRAST_LABEL")); @@ -250,19 +250,19 @@ Gtk::Widget* Preferences::getBatchProcPanel () appendBehavList(mi, M("TP_LOCALCONTRAST_DARKNESS"), ADDSET_LOCALCONTRAST_DARKNESS, false); appendBehavList(mi, M("TP_LOCALCONTRAST_LIGHTNESS"), ADDSET_LOCALCONTRAST_LIGHTNESS, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_SHARPENEDGE_LABEL")); - appendBehavList (mi, M ("TP_SHARPENEDGE_PASSES"), ADDSET_SHARPENEDGE_PASS, false); - appendBehavList (mi, M ("TP_SHARPENEDGE_AMOUNT"), ADDSET_SHARPENEDGE_AMOUNT, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_SHARPENEDGE_LABEL")); + appendBehavList(mi, M("TP_SHARPENEDGE_PASSES"), ADDSET_SHARPENEDGE_PASS, false); + appendBehavList(mi, M("TP_SHARPENEDGE_AMOUNT"), ADDSET_SHARPENEDGE_AMOUNT, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_SHARPENMICRO_LABEL")); - appendBehavList (mi, M ("TP_SHARPENMICRO_AMOUNT"), ADDSET_SHARPENMICRO_AMOUNT, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_SHARPENMICRO_LABEL")); + appendBehavList(mi, M("TP_SHARPENMICRO_AMOUNT"), ADDSET_SHARPENMICRO_AMOUNT, false); appendBehavList (mi, M ("TP_SHARPENMICRO_CONTRAST"), ADDSET_SHARPENMICRO_CONTRAST, false); - appendBehavList (mi, M ("TP_SHARPENMICRO_UNIFORMITY"), ADDSET_SHARPENMICRO_UNIFORMITY, false); + appendBehavList(mi, M("TP_SHARPENMICRO_UNIFORMITY"), ADDSET_SHARPENMICRO_UNIFORMITY, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_DIRPYRDENOISE_LABEL")); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_DIRPYRDENOISE_LABEL")); appendBehavList (mi, M ("TP_DIRPYRDENOISE_LUMINANCE_SMOOTHING"), ADDSET_DIRPYRDN_LUMA, true); appendBehavList (mi, M ("TP_DIRPYRDENOISE_LUMINANCE_DETAIL"), ADDSET_DIRPYRDN_LUMDET, true); appendBehavList (mi, M ("TP_DIRPYRDENOISE_CHROMINANCE_MASTER"), ADDSET_DIRPYRDN_CHROMA, true); @@ -271,136 +271,142 @@ Gtk::Widget* Preferences::getBatchProcPanel () appendBehavList (mi, M ("TP_DIRPYRDENOISE_MAIN_GAMMA"), ADDSET_DIRPYRDN_GAMMA, true); appendBehavList (mi, M ("TP_DIRPYRDENOISE_MEDIAN_PASSES"), ADDSET_DIRPYRDN_PASSES, true); - mi = behModel->append (); + mi = behModel->append(); mi->set_value ( behavColumns.label, M ("TP_DEHAZE_LABEL") ); appendBehavList ( mi, M ( "TP_DEHAZE_STRENGTH" ), ADDSET_DEHAZE_STRENGTH, true ); mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_WBALANCE_LABEL")); - appendBehavList (mi, M ("TP_WBALANCE_TEMPERATURE"), ADDSET_WB_TEMPERATURE, true); - appendBehavList (mi, M ("TP_WBALANCE_GREEN"), ADDSET_WB_GREEN, true); - appendBehavList (mi, M ("TP_WBALANCE_EQBLUERED"), ADDSET_WB_EQUAL, true); - appendBehavList (mi, M ("TP_WBALANCE_TEMPBIAS"), ADDSET_WB_TEMPBIAS, true); + mi->set_value(behavColumns.label, M("TP_WBALANCE_LABEL")); + appendBehavList(mi, M("TP_WBALANCE_TEMPERATURE"), ADDSET_WB_TEMPERATURE, true); + appendBehavList(mi, M("TP_WBALANCE_GREEN"), ADDSET_WB_GREEN, true); + appendBehavList(mi, M("TP_WBALANCE_EQBLUERED"), ADDSET_WB_EQUAL, true); + appendBehavList(mi, M("TP_WBALANCE_TEMPBIAS"), ADDSET_WB_TEMPBIAS, true); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_COLORAPP_LABEL")); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_COLORAPP_LABEL")); appendBehavList (mi, M("TP_COLORAPP_LABEL_SCENE") + " - " + M("TP_COLORAPP_ABSOLUTELUMINANCE"), ADDSET_CAT_ADAPTSCENE, true); appendBehavList (mi, M("TP_COLORAPP_LABEL_VIEWING") + " - " + M("TP_COLORAPP_ABSOLUTELUMINANCE"), ADDSET_CAT_ADAPTVIEWING, true); - appendBehavList (mi, M ("TP_COLORAPP_LIGHT"), ADDSET_CAT_LIGHT, true); - appendBehavList (mi, M ("TP_COLORAPP_BRIGHT"), ADDSET_CAT_BRIGHT, true); - appendBehavList (mi, M ("TP_COLORAPP_CHROMA"), ADDSET_CAT_CHROMA, true); + appendBehavList(mi, M("TP_COLORAPP_LIGHT"), ADDSET_CAT_LIGHT, true); + appendBehavList(mi, M("TP_COLORAPP_BRIGHT"), ADDSET_CAT_BRIGHT, true); + appendBehavList(mi, M("TP_COLORAPP_CHROMA"), ADDSET_CAT_CHROMA, true); appendBehavList (mi, M ("TP_COLORAPP_CHROMA_S"), ADDSET_CAT_CHROMA_S, true); appendBehavList (mi, M ("TP_COLORAPP_CHROMA_M"), ADDSET_CAT_CHROMA_M, true); - appendBehavList (mi, M ("TP_COLORAPP_RSTPRO"), ADDSET_CAT_RSTPRO, true); - appendBehavList (mi, M ("TP_COLORAPP_CONTRAST"), ADDSET_CAT_CONTRAST, true); - appendBehavList (mi, M ("TP_COLORAPP_CONTRAST_Q"), ADDSET_CAT_CONTRAST_Q, true); - appendBehavList (mi, M ("TP_COLORAPP_HUE"), ADDSET_CAT_HUE, true); - appendBehavList (mi, M ("TP_COLORAPP_BADPIXSL"), ADDSET_CAT_BADPIX, true); + appendBehavList(mi, M("TP_COLORAPP_RSTPRO"), ADDSET_CAT_RSTPRO, true); + appendBehavList(mi, M("TP_COLORAPP_CONTRAST"), ADDSET_CAT_CONTRAST, true); + appendBehavList(mi, M("TP_COLORAPP_CONTRAST_Q"), ADDSET_CAT_CONTRAST_Q, true); + appendBehavList(mi, M("TP_COLORAPP_HUE"), ADDSET_CAT_HUE, true); + appendBehavList(mi, M("TP_COLORAPP_BADPIXSL"), ADDSET_CAT_BADPIX, true); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_VIBRANCE_LABEL")); - appendBehavList (mi, M ("TP_VIBRANCE_PASTELS"), ADDSET_VIBRANCE_PASTELS, false); - appendBehavList (mi, M ("TP_VIBRANCE_SATURATED"), ADDSET_VIBRANCE_SATURATED, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_VIBRANCE_LABEL")); + appendBehavList(mi, M("TP_VIBRANCE_PASTELS"), ADDSET_VIBRANCE_PASTELS, false); + appendBehavList(mi, M("TP_VIBRANCE_SATURATED"), ADDSET_VIBRANCE_SATURATED, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_CHMIXER_LABEL")); - appendBehavList (mi, M ("TP_CHMIXER_RED") + ", " + M ("TP_CHMIXER_GREEN") + ", " + M ("TP_CHMIXER_BLUE"), ADDSET_CHMIXER, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_CHMIXER_LABEL")); + appendBehavList(mi, M("TP_CHMIXER_RED") + ", " + M("TP_CHMIXER_GREEN") + ", " + M("TP_CHMIXER_BLUE"), ADDSET_CHMIXER, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_BWMIX_LABEL")); - appendBehavList (mi, M ("TP_BWMIX_MIXC"), ADDSET_BLACKWHITE_HUES, false); - appendBehavList (mi, M ("TP_BWMIX_GAMMA"), ADDSET_BLACKWHITE_GAMMA, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_BWMIX_LABEL")); + appendBehavList(mi, M("TP_BWMIX_MIXC"), ADDSET_BLACKWHITE_HUES, false); + appendBehavList(mi, M("TP_BWMIX_GAMMA"), ADDSET_BLACKWHITE_GAMMA, false); - mi = behModel->append (); - mi->set_value ( behavColumns.label, M ("TP_FILMSIMULATION_LABEL") ); - appendBehavList ( mi, M ( "TP_FILMSIMULATION_STRENGTH" ), ADDSET_FILMSIMULATION_STRENGTH, true ); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_FILMSIMULATION_LABEL")); + appendBehavList(mi, M("TP_FILMSIMULATION_STRENGTH"), ADDSET_FILMSIMULATION_STRENGTH, true); - mi = behModel->append (); + mi = behModel->append(); mi->set_value ( behavColumns.label, M ("TP_SOFTLIGHT_LABEL") ); appendBehavList ( mi, M ( "TP_SOFTLIGHT_STRENGTH" ), ADDSET_SOFTLIGHT_STRENGTH, true ); mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_COLORTONING_LABEL")); - appendBehavList (mi, M ("TP_COLORTONING_SPLITCOCO"), ADDSET_COLORTONING_SPLIT, true); - appendBehavList (mi, M ("TP_COLORTONING_SATURATIONTHRESHOLD"), ADDSET_COLORTONING_SATTHRESHOLD, true); - appendBehavList (mi, M ("TP_COLORTONING_SATURATEDOPACITY"), ADDSET_COLORTONING_SATOPACITY, true); - appendBehavList (mi, M ("TP_COLORTONING_BALANCE"), ADDSET_COLORTONING_BALANCE, true); - appendBehavList (mi, M ("TP_COLORTONING_STRENGTH"), ADDSET_COLORTONING_STRENGTH, true); + mi->set_value(behavColumns.label, M("TP_COLORTONING_LABEL")); + appendBehavList(mi, M("TP_COLORTONING_SPLITCOCO"), ADDSET_COLORTONING_SPLIT, true); + appendBehavList(mi, M("TP_COLORTONING_SATURATIONTHRESHOLD"), ADDSET_COLORTONING_SATTHRESHOLD, true); + appendBehavList(mi, M("TP_COLORTONING_SATURATEDOPACITY"), ADDSET_COLORTONING_SATOPACITY, true); + appendBehavList(mi, M("TP_COLORTONING_BALANCE"), ADDSET_COLORTONING_BALANCE, true); + appendBehavList(mi, M("TP_COLORTONING_STRENGTH"), ADDSET_COLORTONING_STRENGTH, true); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_ROTATE_LABEL")); - appendBehavList (mi, M ("TP_ROTATE_DEGREE"), ADDSET_ROTATE_DEGREE, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_ROTATE_LABEL")); + appendBehavList(mi, M("TP_ROTATE_DEGREE"), ADDSET_ROTATE_DEGREE, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_RESIZE_LABEL")); - appendBehavList (mi, M ("TP_RESIZE_SCALE"), ADDSET_RESIZE_SCALE, true); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_RESIZE_LABEL")); + appendBehavList(mi, M("TP_RESIZE_SCALE"), ADDSET_RESIZE_SCALE, true); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_DISTORTION_LABEL")); - appendBehavList (mi, M ("TP_DISTORTION_AMOUNT"), ADDSET_DIST_AMOUNT, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_DISTORTION_LABEL")); + appendBehavList(mi, M("TP_DISTORTION_AMOUNT"), ADDSET_DIST_AMOUNT, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_PERSPECTIVE_LABEL")); - appendBehavList (mi, M ("TP_PERSPECTIVE_HORIZONTAL") + ", " + M ("TP_PERSPECTIVE_VERTICAL"), ADDSET_PERSPECTIVE, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_PERSPECTIVE_LABEL")); + appendBehavList(mi, M("TP_PERSPECTIVE_METHOD_SIMPLE") + " - " + M("TP_PERSPECTIVE_HORIZONTAL") + ", " + M("TP_PERSPECTIVE_VERTICAL"), ADDSET_PERSPECTIVE, false); + appendBehavList(mi, M("TP_PERSPECTIVE_CAMERA_FOCAL_LENGTH") + ", " + M("TP_PERSPECTIVE_CAMERA_CROP_FACTOR"), ADDSET_PERSP_CAM_FOCAL_LENGTH, false); + appendBehavList(mi, M("TP_PERSPECTIVE_CAMERA_FRAME") + " - " + M("TP_PERSPECTIVE_CAMERA_SHIFT_HORIZONTAL") + ", " + M("TP_PERSPECTIVE_CAMERA_SHIFT_VERTICAL"), ADDSET_PERSP_CAM_SHIFT, false); + appendBehavList(mi, M("TP_PERSPECTIVE_CAMERA_FRAME") + " - " + M("TP_PERSPECTIVE_CAMERA_ROLL") + ", " + M("TP_PERSPECTIVE_CAMERA_YAW") + ", " + M("TP_PERSPECTIVE_CAMERA_PITCH"), ADDSET_PERSP_CAM_ANGLE, false); + appendBehavList(mi, M("TP_PERSPECTIVE_POST_CORRECTION_ADJUSTMENT_FRAME") + " - " + M("TP_PERSPECTIVE_PROJECTION_SHIFT_HORIZONTAL") + ", " + M("TP_PERSPECTIVE_PROJECTION_SHIFT_VERTICAL"), ADDSET_PERSP_PROJ_SHIFT, false); + appendBehavList(mi, M("TP_PERSPECTIVE_PROJECTION_ROTATE"), ADDSET_PERSP_PROJ_ROTATE, false); + appendBehavList(mi, M("TP_PERSPECTIVE_RECOVERY_FRAME") + " - " + M("TP_PERSPECTIVE_PROJECTION_YAW") + ", " + M("TP_PERSPECTIVE_PROJECTION_PITCH"), ADDSET_PERSP_PROJ_ANGLE, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_GRADIENT_LABEL")); - appendBehavList (mi, M ("TP_GRADIENT_DEGREE"), ADDSET_GRADIENT_DEGREE, false); - appendBehavList (mi, M ("TP_GRADIENT_FEATHER"), ADDSET_GRADIENT_FEATHER, false); - appendBehavList (mi, M ("TP_GRADIENT_STRENGTH"), ADDSET_GRADIENT_STRENGTH, false); - appendBehavList (mi, M ("TP_GRADIENT_CENTER_X") + ", " + M ("TP_GRADIENT_CENTER_Y"), ADDSET_GRADIENT_CENTER, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_GRADIENT_LABEL")); + appendBehavList(mi, M("TP_GRADIENT_DEGREE"), ADDSET_GRADIENT_DEGREE, false); + appendBehavList(mi, M("TP_GRADIENT_FEATHER"), ADDSET_GRADIENT_FEATHER, false); + appendBehavList(mi, M("TP_GRADIENT_STRENGTH"), ADDSET_GRADIENT_STRENGTH, false); + appendBehavList(mi, M("TP_GRADIENT_CENTER_X") + ", " + M("TP_GRADIENT_CENTER_Y"), ADDSET_GRADIENT_CENTER, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_PCVIGNETTE_LABEL")); - appendBehavList (mi, M ("TP_PCVIGNETTE_STRENGTH"), ADDSET_PCVIGNETTE_STRENGTH, false); - appendBehavList (mi, M ("TP_PCVIGNETTE_FEATHER"), ADDSET_PCVIGNETTE_FEATHER, false); - appendBehavList (mi, M ("TP_PCVIGNETTE_ROUNDNESS"), ADDSET_PCVIGNETTE_ROUNDNESS, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_PCVIGNETTE_LABEL")); + appendBehavList(mi, M("TP_PCVIGNETTE_STRENGTH"), ADDSET_PCVIGNETTE_STRENGTH, false); + appendBehavList(mi, M("TP_PCVIGNETTE_FEATHER"), ADDSET_PCVIGNETTE_FEATHER, false); + appendBehavList(mi, M("TP_PCVIGNETTE_ROUNDNESS"), ADDSET_PCVIGNETTE_ROUNDNESS, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_CACORRECTION_LABEL")); - appendBehavList (mi, M ("TP_CACORRECTION_BLUE") + ", " + M ("TP_CACORRECTION_RED"), ADDSET_CA, true); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_CACORRECTION_LABEL")); + appendBehavList(mi, M("TP_CACORRECTION_BLUE") + ", " + M("TP_CACORRECTION_RED"), ADDSET_CA, true); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_VIGNETTING_LABEL")); - appendBehavList (mi, M ("TP_VIGNETTING_AMOUNT"), ADDSET_VIGN_AMOUNT, false); - appendBehavList (mi, M ("TP_VIGNETTING_RADIUS"), ADDSET_VIGN_RADIUS, false); - appendBehavList (mi, M ("TP_VIGNETTING_STRENGTH"), ADDSET_VIGN_STRENGTH, false); - appendBehavList (mi, M ("TP_VIGNETTING_CENTER_X") + ", " + M ("TP_VIGNETTING_CENTER_Y"), ADDSET_VIGN_CENTER, false); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_VIGNETTING_LABEL")); + appendBehavList(mi, M("TP_VIGNETTING_AMOUNT"), ADDSET_VIGN_AMOUNT, false); + appendBehavList(mi, M("TP_VIGNETTING_RADIUS"), ADDSET_VIGN_RADIUS, false); + appendBehavList(mi, M("TP_VIGNETTING_STRENGTH"), ADDSET_VIGN_STRENGTH, false); + appendBehavList(mi, M("TP_VIGNETTING_CENTER_X") + ", " + M("TP_VIGNETTING_CENTER_Y"), ADDSET_VIGN_CENTER, false); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_DIRPYREQUALIZER_LABEL")); - appendBehavList (mi, M ("TP_EXPOSURE_CONTRAST"), ADDSET_DIRPYREQ, true); - appendBehavList (mi, M ("TP_DIRPYREQUALIZER_THRESHOLD"), ADDSET_DIRPYREQ_THRESHOLD, true); - appendBehavList (mi, M ("TP_DIRPYREQUALIZER_SKIN"), ADDSET_DIRPYREQ_SKINPROTECT, true); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_DIRPYREQUALIZER_LABEL")); + appendBehavList(mi, M("TP_EXPOSURE_CONTRAST"), ADDSET_DIRPYREQ, true); + appendBehavList(mi, M("TP_DIRPYREQUALIZER_THRESHOLD"), ADDSET_DIRPYREQ_THRESHOLD, true); + appendBehavList(mi, M("TP_DIRPYREQUALIZER_SKIN"), ADDSET_DIRPYREQ_SKINPROTECT, true); - mi = behModel->append (); - mi->set_value (behavColumns.label, M ("TP_WAVELET_LABEL")); - appendBehavList (mi, M ("TP_WAVELET_LEVELS"), ADDSET_WA_THRES, true); - appendBehavList (mi, M ("TP_WAVELET_THRESHOLD"), ADDSET_WA_THRESHOLD, true); - appendBehavList (mi, M ("TP_WAVELET_THRESHOLD2"), ADDSET_WA_THRESHOLD2, true); - appendBehavList (mi, M ("TP_WAVELET_CHRO"), ADDSET_WA_CHRO, true); - appendBehavList (mi, M ("TP_WAVELET_CHR"), ADDSET_WA_CHROMA, true); - appendBehavList (mi, M ("TP_WAVELET_SKIN"), ADDSET_WA_SKINPROTECT, true); - appendBehavList (mi, M ("TP_WAVELET_EDRAD"), ADDSET_WA_EDGRAD, true); - appendBehavList (mi, M ("TP_WAVELET_EDVAL"), ADDSET_WA_EDGVAL, true); - appendBehavList (mi, M ("TP_WAVELET_RESCON"), ADDSET_WA_RESCON, true); - appendBehavList (mi, M ("TP_WAVELET_THR"), ADDSET_WA_THRR, true); - appendBehavList (mi, M ("TP_WAVELET_RESCONH"), ADDSET_WA_RESCONH, true); - appendBehavList (mi, M ("TP_WAVELET_THRH"), ADDSET_WA_THRRH, true); + mi = behModel->append(); + mi->set_value(behavColumns.label, M("TP_WAVELET_LABEL")); + appendBehavList(mi, M("TP_WAVELET_LEVELS"), ADDSET_WA_THRES, true); + appendBehavList(mi, M("TP_WAVELET_THRESHOLD"), ADDSET_WA_THRESHOLD, true); + appendBehavList(mi, M("TP_WAVELET_THRESHOLD2"), ADDSET_WA_THRESHOLD2, true); + appendBehavList(mi, M("TP_WAVELET_CHRO"), ADDSET_WA_CHRO, true); + appendBehavList(mi, M("TP_WAVELET_CHR"), ADDSET_WA_CHROMA, true); + appendBehavList(mi, M("TP_WAVELET_SKIN"), ADDSET_WA_SKINPROTECT, true); + appendBehavList(mi, M("TP_WAVELET_EDRAD"), ADDSET_WA_EDGRAD, true); + appendBehavList(mi, M("TP_WAVELET_EDVAL"), ADDSET_WA_EDGVAL, true); + appendBehavList(mi, M("TP_WAVELET_RESCON"), ADDSET_WA_RESCON, true); + appendBehavList(mi, M("TP_WAVELET_THR"), ADDSET_WA_THRR, true); + appendBehavList(mi, M("TP_WAVELET_RESCONH"), ADDSET_WA_RESCONH, true); + appendBehavList(mi, M("TP_WAVELET_THRH"), ADDSET_WA_THRRH, true); appendBehavList (mi, M ("TP_WAVELET_RADIUS"), ADDSET_WA_RADIUS, true); - appendBehavList (mi, M ("TP_WAVELET_RESCHRO"), ADDSET_WA_RESCHRO, true); - appendBehavList (mi, M ("TP_WAVELET_TMSTRENGTH"), ADDSET_WA_TMRS, true); + appendBehavList(mi, M("TP_WAVELET_RESCHRO"), ADDSET_WA_RESCHRO, true); + appendBehavList(mi, M("TP_WAVELET_TMSTRENGTH"), ADDSET_WA_TMRS, true); appendBehavList (mi, M ("TP_WAVELET_TMEDGS"), ADDSET_WA_EDGS, true); appendBehavList (mi, M ("TP_WAVELET_TMSCALE"), ADDSET_WA_SCALE, true); - appendBehavList (mi, M ("TP_WAVELET_SKY"), ADDSET_WA_SKYPROTECT, true); - appendBehavList (mi, M ("TP_WAVELET_CONTRA"), ADDSET_WA_CONTRAST, true); - appendBehavList (mi, M ("TP_WAVELET_STRENGTH"), ADDSET_WA_STRENGTH, true); - appendBehavList (mi, M ("TP_WAVELET_COMPGAMMA"), ADDSET_WA_GAMMA, true); - appendBehavList (mi, M ("TP_WAVELET_EDGEDETECT"), ADDSET_WA_EDGEDETECT, true); - appendBehavList (mi, M ("TP_WAVELET_EDGEDETECTTHR"), ADDSET_WA_EDGEDETECTTHR, true); - appendBehavList (mi, M ("TP_WAVELET_EDGEDETECTTHR2"), ADDSET_WA_EDGEDETECTTHR2, true); + appendBehavList(mi, M("TP_WAVELET_SKY"), ADDSET_WA_SKYPROTECT, true); + appendBehavList(mi, M("TP_WAVELET_CONTRA"), ADDSET_WA_CONTRAST, true); + appendBehavList(mi, M("TP_WAVELET_STRENGTH"), ADDSET_WA_STRENGTH, true); + appendBehavList(mi, M("TP_WAVELET_COMPGAMMA"), ADDSET_WA_GAMMA, true); + appendBehavList(mi, M("TP_WAVELET_EDGEDETECT"), ADDSET_WA_EDGEDETECT, true); + appendBehavList(mi, M("TP_WAVELET_EDGEDETECTTHR"), ADDSET_WA_EDGEDETECTTHR, true); + appendBehavList(mi, M("TP_WAVELET_EDGEDETECTTHR2"), ADDSET_WA_EDGEDETECTTHR2, true); mi = behModel->append (); mi->set_value (behavColumns.label, M("MAIN_TAB_RAW") + " - " + M("TP_RAW_SENSOR_BAYER_LABEL")); @@ -436,52 +442,52 @@ Gtk::Widget* Preferences::getBatchProcPanel () mi->set_value (behavColumns.label, M("MAIN_TAB_RAW") + " - " + M("TP_RAWCACORR_LABEL")); appendBehavList (mi, M ("TP_RAWCACORR_CARED") + ", " + M ("TP_RAWCACORR_CABLUE"), ADDSET_RAWCACORR, true); - behTreeView->expand_all (); + behTreeView->expand_all(); - behAddAll = Gtk::manage ( new Gtk::Button (M ("PREFERENCES_BEHADDALL")) ); - behSetAll = Gtk::manage ( new Gtk::Button (M ("PREFERENCES_BEHSETALL")) ); - behAddAll->set_tooltip_markup (M ("PREFERENCES_BEHADDALLHINT")); - behSetAll->set_tooltip_markup (M ("PREFERENCES_BEHSETALLHINT")); + behAddAll = Gtk::manage(new Gtk::Button(M("PREFERENCES_BEHADDALL"))); + behSetAll = Gtk::manage(new Gtk::Button(M("PREFERENCES_BEHSETALL"))); + behAddAll->set_tooltip_markup(M("PREFERENCES_BEHADDALLHINT")); + behSetAll->set_tooltip_markup(M("PREFERENCES_BEHSETALLHINT")); - behAddAll->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::behAddAllPressed) ); - behSetAll->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::behSetAllPressed) ); + behAddAll->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::behAddAllPressed)); + behSetAll->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::behSetAllPressed)); - Gtk::HBox* buttonpanel1 = Gtk::manage (new Gtk::HBox ()); - buttonpanel1->pack_end (*behSetAll, Gtk::PACK_SHRINK, 4); - buttonpanel1->pack_end (*behAddAll, Gtk::PACK_SHRINK, 4); - vbbeh->pack_start (*buttonpanel1, Gtk::PACK_SHRINK, 4); + Gtk::HBox* buttonpanel1 = Gtk::manage(new Gtk::HBox()); + buttonpanel1->pack_end(*behSetAll, Gtk::PACK_SHRINK, 4); + buttonpanel1->pack_end(*behAddAll, Gtk::PACK_SHRINK, 4); + vbbeh->pack_start(*buttonpanel1, Gtk::PACK_SHRINK, 4); - chOverwriteOutputFile = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_OVERWRITEOUTPUTFILE")) ); + chOverwriteOutputFile = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_OVERWRITEOUTPUTFILE"))); vbBatchProc->pack_start (*chOverwriteOutputFile, Gtk::PACK_SHRINK, 4); swBatchProc->add(*vbBatchProc); return swBatchProc; } -void Preferences::appendBehavList (Gtk::TreeModel::iterator& parent, Glib::ustring label, int id, bool set) +void Preferences::appendBehavList(Gtk::TreeModel::iterator& parent, Glib::ustring label, int id, bool set) { - Gtk::TreeModel::iterator ci = behModel->append (parent->children()); - ci->set_value (behavColumns.label, label); - ci->set_value (behavColumns.visible, true); - ci->set_value (behavColumns.badd, !set); - ci->set_value (behavColumns.bset, set); - ci->set_value (behavColumns.addsetid, id); + Gtk::TreeModel::iterator ci = behModel->append(parent->children()); + ci->set_value(behavColumns.label, label); + ci->set_value(behavColumns.visible, true); + ci->set_value(behavColumns.badd, !set); + ci->set_value(behavColumns.bset, set); + ci->set_value(behavColumns.addsetid, id); } -void Preferences::behAddSetRadioToggled (const Glib::ustring& path, bool add) +void Preferences::behAddSetRadioToggled(const Glib::ustring& path, bool add) { - Gtk::TreeModel::iterator iter = behModel->get_iter (path); + Gtk::TreeModel::iterator iter = behModel->get_iter(path); iter->set_value(behavColumns.badd, add); iter->set_value(behavColumns.bset, !add); } -void Preferences::behAddRadioToggled (const Glib::ustring& path) +void Preferences::behAddRadioToggled(const Glib::ustring& path) { behAddSetRadioToggled(path, true); } -void Preferences::behSetRadioToggled (const Glib::ustring& path) +void Preferences::behSetRadioToggled(const Glib::ustring& path) { behAddSetRadioToggled(path, false); } @@ -492,7 +498,7 @@ Gtk::Widget *Preferences::getDynamicProfilePanel() swDynamicProfile = Gtk::manage(new Gtk::ScrolledWindow()); swDynamicProfile->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - dynProfilePanel = Gtk::manage (new DynamicProfilePanel()); + dynProfilePanel = Gtk::manage(new DynamicProfilePanel()); swDynamicProfile->add(*dynProfilePanel); return swDynamicProfile; @@ -506,79 +512,82 @@ Gtk::Widget* Preferences::getImageProcessingPanel () Gtk::VBox* vbImageProcessing = Gtk::manage (new Gtk::VBox ()); - Gtk::Frame* fpp = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_IMPROCPARAMS"))); - Gtk::VBox* vbpp = Gtk::manage (new Gtk::VBox ()); - Gtk::Label* drlab = Gtk::manage (new Gtk::Label (M ("PREFERENCES_FORRAW") + ":", Gtk::ALIGN_START)); - rprofiles = Gtk::manage (new ProfileStoreComboBox ()); + Gtk::Frame* fpp = Gtk::manage(new Gtk::Frame(M("PREFERENCES_IMPROCPARAMS"))); + Gtk::VBox* vbpp = Gtk::manage(new Gtk::VBox()); + Gtk::Label* drlab = Gtk::manage(new Gtk::Label(M("PREFERENCES_FORRAW") + ":", Gtk::ALIGN_START)); + rprofiles = Gtk::manage(new ProfileStoreComboBox()); const ProfileStoreEntry* dynpse = ProfileStore::getInstance()->getInternalDynamicPSE(); - rprofiles->addRow (dynpse); - setExpandAlignProperties (rprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - rprofiles->set_size_request (50, -1); - rpconn = rprofiles->signal_changed().connect ( sigc::mem_fun (*this, &Preferences::forRAWComboChanged) ); - Gtk::Label* drimg = Gtk::manage (new Gtk::Label (M ("PREFERENCES_FORIMAGE") + ":", Gtk::ALIGN_START)); - iprofiles = Gtk::manage (new ProfileStoreComboBox ()); - iprofiles->addRow (dynpse); - iprofiles->set_size_request (50, -1); - setExpandAlignProperties (iprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - ipconn = iprofiles->signal_changed().connect ( sigc::mem_fun (*this, &Preferences::forImageComboChanged) ); - Gtk::Table* defpt = Gtk::manage (new Gtk::Table (2, 2)); - defpt->attach (*drlab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); - defpt->attach (*rprofiles, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); - defpt->attach (*drimg, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); - defpt->attach (*iprofiles, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); - vbpp->pack_start (*defpt, Gtk::PACK_SHRINK, 4); - useBundledProfiles = Gtk::manage (new Gtk::CheckButton (M ("PREFERENCES_USEBUNDLEDPROFILES"))); - bpconn = useBundledProfiles->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::bundledProfilesChanged) ); - vbpp->pack_start (*useBundledProfiles, Gtk::PACK_SHRINK, 4); - fpp->add (*vbpp); + rprofiles->addRow(dynpse); + setExpandAlignProperties(rprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + rprofiles->set_size_request(50, -1); + rpconn = rprofiles->signal_changed().connect(sigc::mem_fun(*this, &Preferences::forRAWComboChanged)); + Gtk::Label* drimg = Gtk::manage(new Gtk::Label(M("PREFERENCES_FORIMAGE") + ":", Gtk::ALIGN_START)); + iprofiles = Gtk::manage(new ProfileStoreComboBox()); + iprofiles->addRow(dynpse); + iprofiles->set_size_request(50, -1); + setExpandAlignProperties(iprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + ipconn = iprofiles->signal_changed().connect(sigc::mem_fun(*this, &Preferences::forImageComboChanged)); + Gtk::Table* defpt = Gtk::manage(new Gtk::Table(2, 2)); + defpt->attach(*drlab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); + defpt->attach(*rprofiles, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); + defpt->attach(*drimg, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); + defpt->attach(*iprofiles, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); + vbpp->pack_start(*defpt, Gtk::PACK_SHRINK, 4); + useBundledProfiles = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_USEBUNDLEDPROFILES"))); + bpconn = useBundledProfiles->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::bundledProfilesChanged)); + vbpp->pack_start(*useBundledProfiles, Gtk::PACK_SHRINK, 4); + fpp->add(*vbpp); vbImageProcessing->pack_start (*fpp, Gtk::PACK_SHRINK, 4); // Custom profile builder box - Gtk::Frame* cpfrm = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_CUSTPROFBUILD")) ); - Gtk::Label* cplab = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CUSTPROFBUILDPATH") + ":", Gtk::ALIGN_START) ); - txtCustProfBuilderPath = Gtk::manage ( new Gtk::Entry () ); - txtCustProfBuilderPath->set_tooltip_markup (M ("PREFERENCES_CUSTPROFBUILDHINT")); - Gtk::Label* cpltypelab = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_CUSTPROFBUILDKEYFORMAT") + ":", Gtk::ALIGN_START) ); - custProfBuilderLabelType = Gtk::manage (new Gtk::ComboBoxText ()); - custProfBuilderLabelType->append (M ("PREFERENCES_CUSTPROFBUILDKEYFORMAT_TID")); - custProfBuilderLabelType->append (M ("PREFERENCES_CUSTPROFBUILDKEYFORMAT_NAME")); - custProfBuilderLabelType->append (M ("PREFERENCES_CUSTPROFBUILDKEYFORMAT_TID") + "_" + M ("PREFERENCES_CUSTPROFBUILDKEYFORMAT_NAME")); - Gtk::Table* cpbt = Gtk::manage (new Gtk::Table (2, 2)); - cpbt->attach (*cplab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); - cpbt->attach (*txtCustProfBuilderPath, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); - cpbt->attach (*cpltypelab, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); - cpbt->attach (*custProfBuilderLabelType, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); - cpfrm->add (*cpbt); + Gtk::Frame* cpfrm = Gtk::manage(new Gtk::Frame(M("PREFERENCES_CUSTPROFBUILD"))); + Gtk::Label* cplab = Gtk::manage(new Gtk::Label(M("PREFERENCES_CUSTPROFBUILDPATH") + ":", Gtk::ALIGN_START)); + txtCustProfBuilderPath = Gtk::manage(new Gtk::Entry()); + txtCustProfBuilderPath->set_tooltip_markup(M("PREFERENCES_CUSTPROFBUILDHINT")); + Gtk::Label* cpltypelab = Gtk::manage(new Gtk::Label(M("PREFERENCES_CUSTPROFBUILDKEYFORMAT") + ":", Gtk::ALIGN_START)); + custProfBuilderLabelType = Gtk::manage(new Gtk::ComboBoxText()); + custProfBuilderLabelType->append(M("PREFERENCES_CUSTPROFBUILDKEYFORMAT_TID")); + custProfBuilderLabelType->append(M("PREFERENCES_CUSTPROFBUILDKEYFORMAT_NAME")); + custProfBuilderLabelType->append(M("PREFERENCES_CUSTPROFBUILDKEYFORMAT_TID") + "_" + M("PREFERENCES_CUSTPROFBUILDKEYFORMAT_NAME")); + Gtk::Table* cpbt = Gtk::manage(new Gtk::Table(2, 2)); + cpbt->attach(*cplab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); + cpbt->attach(*txtCustProfBuilderPath, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); + cpbt->attach(*cpltypelab, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); + cpbt->attach(*custProfBuilderLabelType, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); + cpfrm->add(*cpbt); vbImageProcessing->pack_start (*cpfrm, Gtk::PACK_SHRINK, 4); - Gtk::Frame* fdp = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_PROFILEHANDLING"))); - Gtk::Table* vbdp = Gtk::manage (new Gtk::Table (2, 2)); - saveParamsPreference = Gtk::manage (new Gtk::ComboBoxText ()); - saveParamsPreference->append (M ("PREFERENCES_PROFILESAVEINPUT")); - saveParamsPreference->append (M ("PREFERENCES_PROFILESAVECACHE")); - saveParamsPreference->append (M ("PREFERENCES_PROFILESAVEBOTH")); + Gtk::Frame* fdp = Gtk::manage(new Gtk::Frame(M("PREFERENCES_PROFILEHANDLING"))); + Gtk::Table* vbdp = Gtk::manage(new Gtk::Table(2, 2)); + saveParamsPreference = Gtk::manage(new Gtk::ComboBoxText()); + saveParamsPreference->append(M("PREFERENCES_PROFILESAVEINPUT")); + saveParamsPreference->append(M("PREFERENCES_PROFILESAVECACHE")); + saveParamsPreference->append(M("PREFERENCES_PROFILESAVEBOTH")); Gtk::Label *splab = Gtk::manage (new Gtk::Label (M ("PREFERENCES_PROFILESAVELOCATION") + ":", Gtk::ALIGN_START)); - vbdp->attach (*splab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); - vbdp->attach (*saveParamsPreference, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); + vbdp->attach(*splab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); + vbdp->attach(*saveParamsPreference, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); Gtk::Label* lplab = Gtk::manage (new Gtk::Label (M ("PREFERENCES_PROFILELOADPR") + ":", Gtk::ALIGN_START)); - loadParamsPreference = Gtk::manage (new Gtk::ComboBoxText ()); - loadParamsPreference->append (M ("PREFERENCES_PROFILEPRCACHE")); - loadParamsPreference->append (M ("PREFERENCES_PROFILEPRFILE")); - vbdp->attach (*lplab, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); - vbdp->attach (*loadParamsPreference, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); - fdp->add (*vbdp); + loadParamsPreference = Gtk::manage(new Gtk::ComboBoxText()); + loadParamsPreference->append(M("PREFERENCES_PROFILEPRCACHE")); + loadParamsPreference->append(M("PREFERENCES_PROFILEPRFILE")); + vbdp->attach(*lplab, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 2); + vbdp->attach(*loadParamsPreference, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); + fdp->add(*vbdp); vbImageProcessing->pack_start (*fdp, Gtk::PACK_SHRINK, 4); +// Gtk::Frame* fdf = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_DARKFRAME")) ); +// Gtk::HBox* hb42 = Gtk::manage (new Gtk::HBox ()); +// darkFrameDir = Gtk::manage (new Gtk::FileChooserButton (M ("PREFERENCES_DIRDARKFRAMES"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); // Directories - Gtk::Frame* cdf = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_DIRECTORIES")) ); - Gtk::Grid* dirgrid = Gtk::manage (new Gtk::Grid ()); + Gtk::Frame* cdf = Gtk::manage(new Gtk::Frame(M("PREFERENCES_DIRECTORIES"))); + Gtk::Grid* dirgrid = Gtk::manage(new Gtk::Grid()); setExpandAlignProperties(dirgrid, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label *dfLab = Gtk::manage (new Gtk::Label (M ("PREFERENCES_DIRDARKFRAMES") + ":")); + Gtk::Label *dfLab = Gtk::manage(new Gtk::Label(M("PREFERENCES_DIRDARKFRAMES") + ":")); setExpandAlignProperties(dfLab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - darkFrameDir = Gtk::manage (new MyFileChooserButton (M ("PREFERENCES_DIRDARKFRAMES"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + darkFrameDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_DIRDARKFRAMES"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); setExpandAlignProperties(darkFrameDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - dfLabel = Gtk::manage (new Gtk::Label ("Found:")); + dfLabel = Gtk::manage(new Gtk::Label("Found:")); setExpandAlignProperties(dfLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); dirgrid->attach_next_to(*dfLab, Gtk::POS_TOP, 1, 1); @@ -588,11 +597,11 @@ Gtk::Widget* Preferences::getImageProcessingPanel () dfconn = darkFrameDir->signal_selection_changed().connect ( sigc::mem_fun (*this, &Preferences::darkFrameChanged)); // FLATFIELD - Gtk::Label *ffLab = Gtk::manage (new Gtk::Label (M ("PREFERENCES_FLATFIELDSDIR") + ":")); + Gtk::Label *ffLab = Gtk::manage(new Gtk::Label(M("PREFERENCES_FLATFIELDSDIR") + ":")); setExpandAlignProperties(ffLab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - flatFieldDir = Gtk::manage (new MyFileChooserButton (M ("PREFERENCES_FLATFIELDSDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + flatFieldDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_FLATFIELDSDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); setExpandAlignProperties(flatFieldDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - ffLabel = Gtk::manage (new Gtk::Label ("Found:")); + ffLabel = Gtk::manage(new Gtk::Label("Found:")); setExpandAlignProperties(ffLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); dirgrid->attach_next_to(*ffLab, *dfLab, Gtk::POS_BOTTOM, 1, 1); @@ -602,11 +611,11 @@ Gtk::Widget* Preferences::getImageProcessingPanel () ffconn = flatFieldDir->signal_selection_changed().connect ( sigc::mem_fun (*this, &Preferences::flatFieldChanged)); //Cluts Dir - Gtk::Label *clutsDirLabel = Gtk::manage (new Gtk::Label (M ("PREFERENCES_CLUTSDIR") + ":")); + Gtk::Label *clutsDirLabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_CLUTSDIR") + ":")); setExpandAlignProperties(clutsDirLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - clutsDir = Gtk::manage (new MyFileChooserButton (M ("PREFERENCES_CLUTSDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + clutsDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_CLUTSDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); setExpandAlignProperties(clutsDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label* clutsRestartNeeded = Gtk::manage ( new Gtk::Label (Glib::ustring (" (") + M ("PREFERENCES_APPLNEXTSTARTUP") + ")") ); + Gtk::Label* clutsRestartNeeded = Gtk::manage(new Gtk::Label(Glib::ustring(" (") + M("PREFERENCES_APPLNEXTSTARTUP") + ")")); setExpandAlignProperties(clutsRestartNeeded, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); dirgrid->attach_next_to(*clutsDirLabel, *ffLab, Gtk::POS_BOTTOM, 1, 1); @@ -639,7 +648,7 @@ Gtk::Widget* Preferences::getImageProcessingPanel () return swImageProcessing; } -Gtk::Widget* Preferences::getPerformancePanel () +Gtk::Widget* Preferences::getPerformancePanel() { swPerformance = Gtk::manage(new Gtk::ScrolledWindow()); swPerformance->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); @@ -647,27 +656,27 @@ Gtk::Widget* Preferences::getPerformancePanel () Gtk::VBox* vbPerformance = Gtk::manage ( new Gtk::VBox () ); vbPerformance->set_spacing (4); - Gtk::Frame* fprevdemo = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_PREVDEMO"))); - Gtk::HBox* hbprevdemo = Gtk::manage (new Gtk::HBox (false, 4)); + Gtk::Frame* fprevdemo = Gtk::manage(new Gtk::Frame(M("PREFERENCES_PREVDEMO"))); + Gtk::HBox* hbprevdemo = Gtk::manage(new Gtk::HBox(false, 4)); Gtk::Label* lprevdemo = Gtk::manage (new Gtk::Label (M("PREFERENCES_PREVDEMO_LABEL"), Gtk::ALIGN_START)); - cprevdemo = Gtk::manage (new Gtk::ComboBoxText ()); - cprevdemo->append (M ("PREFERENCES_PREVDEMO_FAST")); - cprevdemo->append (M ("PREFERENCES_PREVDEMO_SIDECAR")); - cprevdemo->set_active (1); - hbprevdemo->pack_start (*lprevdemo, Gtk::PACK_SHRINK); - hbprevdemo->pack_start (*cprevdemo); - fprevdemo->add (*hbprevdemo); + cprevdemo = Gtk::manage(new Gtk::ComboBoxText()); + cprevdemo->append(M("PREFERENCES_PREVDEMO_FAST")); + cprevdemo->append(M("PREFERENCES_PREVDEMO_SIDECAR")); + cprevdemo->set_active(1); + hbprevdemo->pack_start(*lprevdemo, Gtk::PACK_SHRINK); + hbprevdemo->pack_start(*cprevdemo); + fprevdemo->add(*hbprevdemo); vbPerformance->pack_start (*fprevdemo, Gtk::PACK_SHRINK, 4); - Gtk::Frame* ftiffserialize = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_SERIALIZE_TIFF_READ"))); - Gtk::HBox* htiffserialize = Gtk::manage (new Gtk::HBox (false, 4)); - ctiffserialize = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_SERIALIZE_TIFF_READ_LABEL")) ); - ctiffserialize->set_tooltip_text (M ("PREFERENCES_SERIALIZE_TIFF_READ_TOOLTIP")); - htiffserialize->pack_start (*ctiffserialize); - ftiffserialize->add (*htiffserialize); + Gtk::Frame* ftiffserialize = Gtk::manage(new Gtk::Frame(M("PREFERENCES_SERIALIZE_TIFF_READ"))); + Gtk::HBox* htiffserialize = Gtk::manage(new Gtk::HBox(false, 4)); + ctiffserialize = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_SERIALIZE_TIFF_READ_LABEL"))); + ctiffserialize->set_tooltip_text(M("PREFERENCES_SERIALIZE_TIFF_READ_TOOLTIP")); + htiffserialize->pack_start(*ctiffserialize); + ftiffserialize->add(*htiffserialize); vbPerformance->pack_start (*ftiffserialize, Gtk::PACK_SHRINK, 4); - Gtk::Frame* fclut = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_CLUTSCACHE")) ); + Gtk::Frame* fclut = Gtk::manage(new Gtk::Frame(M("PREFERENCES_CLUTSCACHE"))); #ifdef _OPENMP placeSpinBox(fclut, clutCacheSizeSB, "PREFERENCES_CLUTSCACHE_LABEL", 0, 1, 5, 2, 1, 3 * omp_get_num_procs()); #else @@ -719,6 +728,7 @@ Gtk::Widget* Preferences::getPerformancePanel () int maxThreadNumber = 10; #endif + placeSpinBox(threadsVBox, threadsSpinBtn, "PREFERENCES_PERFORMANCE_THREADS_LABEL", 0, 1, 5, 2, 0, maxThreadNumber); threadsFrame->add (*threadsVBox); @@ -737,46 +747,46 @@ Gtk::Widget* Preferences::getColorManPanel () Gtk::VBox* vbColorMan = Gtk::manage (new Gtk::VBox ()); vbColorMan->set_spacing (4); - iccDir = Gtk::manage (new MyFileChooserButton (M ("PREFERENCES_ICCDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); - setExpandAlignProperties (iccDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label* pdlabel = Gtk::manage (new Gtk::Label (M ("PREFERENCES_ICCDIR") + ":", Gtk::ALIGN_START)); - setExpandAlignProperties (pdlabel, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + iccDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_ICCDIR"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + setExpandAlignProperties(iccDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + Gtk::Label* pdlabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_ICCDIR") + ":", Gtk::ALIGN_START)); + setExpandAlignProperties(pdlabel, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Grid* iccdgrid = Gtk::manage (new Gtk::Grid ()); - setExpandAlignProperties (iccdgrid, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - iccdgrid->set_column_spacing (4); + Gtk::Grid* iccdgrid = Gtk::manage(new Gtk::Grid()); + setExpandAlignProperties(iccdgrid, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + iccdgrid->set_column_spacing(4); Gtk::Label* monProfileRestartNeeded = Gtk::manage ( new Gtk::Label (Glib::ustring (" (") + M ("PREFERENCES_APPLNEXTSTARTUP") + ")") ); setExpandAlignProperties(monProfileRestartNeeded, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - iccdgrid->attach (*pdlabel, 0, 0, 1, 1); - iccdgrid->attach (*iccDir, 1, 0, 1, 1); + iccdgrid->attach(*pdlabel, 0, 0, 1, 1); + iccdgrid->attach(*iccDir, 1, 0, 1, 1); iccdgrid->attach (*monProfileRestartNeeded, 2, 0, 1, 1); - iccDir->signal_selection_changed ().connect (sigc::mem_fun (this, &Preferences::iccDirChanged)); + iccDir->signal_selection_changed().connect(sigc::mem_fun(this, &Preferences::iccDirChanged)); vbColorMan->pack_start (*iccdgrid, Gtk::PACK_SHRINK); //------------------------- MONITOR ---------------------- - Gtk::Frame* fmonitor = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_MONITOR")) ); - Gtk::Grid* gmonitor = Gtk::manage ( new Gtk::Grid () ); - gmonitor->set_column_spacing (4); + Gtk::Frame* fmonitor = Gtk::manage(new Gtk::Frame(M("PREFERENCES_MONITOR"))); + Gtk::Grid* gmonitor = Gtk::manage(new Gtk::Grid()); + gmonitor->set_column_spacing(4); - monProfile = Gtk::manage (new Gtk::ComboBoxText ()); - setExpandAlignProperties (monProfile, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label* mplabel = Gtk::manage (new Gtk::Label (M ("PREFERENCES_MONPROFILE") + ":", Gtk::ALIGN_START)); - setExpandAlignProperties (mplabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + monProfile = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(monProfile, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + Gtk::Label* mplabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_MONPROFILE") + ":", Gtk::ALIGN_START)); + setExpandAlignProperties(mplabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - monIntent = Gtk::manage (new Gtk::ComboBoxText ()); - setExpandAlignProperties (monIntent, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label* milabel = Gtk::manage (new Gtk::Label (M ("PREFERENCES_MONINTENT") + ":", Gtk::ALIGN_START)); - setExpandAlignProperties (milabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + monIntent = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(monIntent, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + Gtk::Label* milabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_MONINTENT") + ":", Gtk::ALIGN_START)); + setExpandAlignProperties(milabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - monProfile->append (M ("PREFERENCES_PROFILE_NONE")); - monProfile->set_active (0); + monProfile->append(M("PREFERENCES_PROFILE_NONE")); + monProfile->set_active(0); - const std::vector profiles = rtengine::ICCStore::getInstance ()->getProfiles (rtengine::ICCStore::ProfileType::MONITOR); + const std::vector profiles = rtengine::ICCStore::getInstance()->getProfiles(rtengine::ICCStore::ProfileType::MONITOR); for (const auto& profile : profiles) { if (profile.find("file:") != 0) { @@ -790,97 +800,96 @@ Gtk::Widget* Preferences::getColorManPanel () } // same order as the enum - monIntent->append (M ("PREFERENCES_INTENT_PERCEPTUAL")); - monIntent->append (M ("PREFERENCES_INTENT_RELATIVE")); - monIntent->append (M ("PREFERENCES_INTENT_ABSOLUTE")); - monIntent->set_active (1); - monIntent->set_size_request (120, -1); + monIntent->append(M("PREFERENCES_INTENT_PERCEPTUAL")); + monIntent->append(M("PREFERENCES_INTENT_RELATIVE")); + monIntent->append(M("PREFERENCES_INTENT_ABSOLUTE")); + monIntent->set_active(1); + monIntent->set_size_request(120, -1); - monBPC = Gtk::manage (new Gtk::CheckButton (M ("PREFERENCES_CMMBPC"))); - setExpandAlignProperties (monBPC, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - monBPC->set_active (true); + monBPC = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_CMMBPC"))); + setExpandAlignProperties(monBPC, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + monBPC->set_active(true); - cbAutoMonProfile = Gtk::manage (new Gtk::CheckButton (M ("PREFERENCES_AUTOMONPROFILE"))); - setExpandAlignProperties (cbAutoMonProfile, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - autoMonProfileConn = cbAutoMonProfile->signal_toggled().connect (sigc::mem_fun (*this, &Preferences::autoMonProfileToggled)); + cbAutoMonProfile = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_AUTOMONPROFILE"))); + setExpandAlignProperties(cbAutoMonProfile, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + autoMonProfileConn = cbAutoMonProfile->signal_toggled().connect(sigc::mem_fun(*this, &Preferences::autoMonProfileToggled)); int row = 0; - gmonitor->attach (*mplabel, 0, row, 1, 1); + gmonitor->attach(*mplabel, 0, row, 1, 1); #if defined(__APPLE__) // monitor profile not supported on apple - Gtk::Label *osxwarn = Gtk::manage (new Gtk::Label (M ("PREFERENCES_MONPROFILE_WARNOSX"), Gtk::ALIGN_START)); - setExpandAlignProperties (osxwarn, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); - gmonitor->attach (*osxwarn, 1, row, 1, 1); + Gtk::Label *osxwarn = Gtk::manage(new Gtk::Label(M("PREFERENCES_MONPROFILE_WARNOSX"), Gtk::ALIGN_START)); + setExpandAlignProperties(osxwarn, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); + gmonitor->attach(*osxwarn, 1, row, 1, 1); #else - gmonitor->attach (*monProfile, 1, row, 1, 1); + gmonitor->attach(*monProfile, 1, row, 1, 1); #endif ++row; - gmonitor->attach (*cbAutoMonProfile, 1, row, 1, 1); + gmonitor->attach(*cbAutoMonProfile, 1, row, 1, 1); ++row; - gmonitor->attach (*milabel, 0, row, 1, 1); - gmonitor->attach (*monIntent, 1, row, 1, 1); + gmonitor->attach(*milabel, 0, row, 1, 1); + gmonitor->attach(*monIntent, 1, row, 1, 1); ++row; - gmonitor->attach (*monBPC, 0, row, 2, 1); + gmonitor->attach(*monBPC, 0, row, 2, 1); autoMonProfileToggled(); - fmonitor->add (*gmonitor); + fmonitor->add(*gmonitor); vbColorMan->pack_start (*fmonitor, Gtk::PACK_SHRINK); //------------------------- PRINTER ---------------------- - Gtk::Frame* fprinter = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_PRINTER")) ); - Gtk::Grid* gprinter = Gtk::manage ( new Gtk::Grid () ); - gprinter->set_column_spacing (4); - prtProfile = Gtk::manage (new Gtk::ComboBoxText ()); - setExpandAlignProperties (prtProfile, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label* pplabel = Gtk::manage (new Gtk::Label (M ("PREFERENCES_PRTPROFILE") + ":")); - setExpandAlignProperties (pplabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + Gtk::Frame* fprinter = Gtk::manage(new Gtk::Frame(M("PREFERENCES_PRINTER"))); + Gtk::Grid* gprinter = Gtk::manage(new Gtk::Grid()); + gprinter->set_column_spacing(4); + prtProfile = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(prtProfile, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + Gtk::Label* pplabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_PRTPROFILE") + ":")); + setExpandAlignProperties(pplabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - prtIntent = Gtk::manage (new Gtk::ComboBoxText ()); - setExpandAlignProperties (prtIntent, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - Gtk::Label* pilabel = Gtk::manage (new Gtk::Label (M ("PREFERENCES_PRTINTENT") + ":")); - setExpandAlignProperties (pilabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + prtIntent = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(prtIntent, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + Gtk::Label* pilabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_PRTINTENT") + ":")); + setExpandAlignProperties(pilabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - prtProfile->append (M ("PREFERENCES_PROFILE_NONE")); - prtProfile->set_active (0); + prtProfile->append(M("PREFERENCES_PROFILE_NONE")); + prtProfile->set_active(0); - const std::vector prtprofiles = rtengine::ICCStore::getInstance ()->getProfiles (rtengine::ICCStore::ProfileType::PRINTER); + const std::vector prtprofiles = rtengine::ICCStore::getInstance()->getProfiles(rtengine::ICCStore::ProfileType::PRINTER); for (const auto& prtprofile : prtprofiles) { - prtProfile->append (prtprofile); + prtProfile->append(prtprofile); } // same order as the enum - prtIntent->append (M ("PREFERENCES_INTENT_PERCEPTUAL")); - prtIntent->append (M ("PREFERENCES_INTENT_RELATIVE")); - prtIntent->append (M ("PREFERENCES_INTENT_ABSOLUTE")); - prtIntent->set_active (1); + prtIntent->append(M("PREFERENCES_INTENT_PERCEPTUAL")); + prtIntent->append(M("PREFERENCES_INTENT_RELATIVE")); + prtIntent->append(M("PREFERENCES_INTENT_ABSOLUTE")); + prtIntent->set_active(1); - prtBPC = Gtk::manage (new Gtk::CheckButton (M ("PREFERENCES_CMMBPC"))); - setExpandAlignProperties (prtBPC, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - prtBPC->set_active (true); + prtBPC = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_CMMBPC"))); + setExpandAlignProperties(prtBPC, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + prtBPC->set_active(true); row = 0; - gprinter->attach (*pplabel, 0, row, 1, 1); - gprinter->attach (*prtProfile, 1, row, 1, 1); + gprinter->attach(*pplabel, 0, row, 1, 1); + gprinter->attach(*prtProfile, 1, row, 1, 1); ++row; - gprinter->attach (*pilabel, 0, row, 1, 1); - gprinter->attach (*prtIntent, 1, row, 1, 1); + gprinter->attach(*pilabel, 0, row, 1, 1); + gprinter->attach(*prtIntent, 1, row, 1, 1); ++row; - gprinter->attach (*prtBPC, 0, row, 2, 1); + gprinter->attach(*prtBPC, 0, row, 2, 1); autoMonProfileToggled(); - fprinter->add (*gprinter); + fprinter->add(*gprinter); vbColorMan->pack_start (*fprinter, Gtk::PACK_SHRINK); - swColorMan->add(*vbColorMan); return swColorMan; } -Gtk::Widget* Preferences::getGeneralPanel () +Gtk::Widget* Preferences::getGeneralPanel() { swGeneral = Gtk::manage(new Gtk::ScrolledWindow()); swGeneral->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); @@ -889,114 +898,128 @@ Gtk::Widget* Preferences::getGeneralPanel () vbGeneral->set_column_spacing (4); vbGeneral->set_row_spacing (4); - Gtk::Frame* fworklflow = Gtk::manage (new Gtk::Frame (M ("PREFERENCES_WORKFLOW"))); - setExpandAlignProperties (fworklflow, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); - Gtk::Grid* workflowGrid = Gtk::manage (new Gtk::Grid()); - workflowGrid->set_column_spacing (4); - workflowGrid->set_row_spacing (4); - setExpandAlignProperties (workflowGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + Gtk::Frame* fworklflow = Gtk::manage(new Gtk::Frame(M("PREFERENCES_WORKFLOW"))); + setExpandAlignProperties(fworklflow, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + Gtk::Grid* workflowGrid = Gtk::manage(new Gtk::Grid()); + workflowGrid->set_column_spacing(4); + workflowGrid->set_row_spacing(4); + setExpandAlignProperties(workflowGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - Gtk::Label* flayoutlab = Gtk::manage (new Gtk::Label (M ("PREFERENCES_EDITORLAYOUT") + ":")); - setExpandAlignProperties (flayoutlab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + Gtk::Label* flayoutlab = Gtk::manage(new Gtk::Label(M("PREFERENCES_EDITORLAYOUT") + ":")); + setExpandAlignProperties(flayoutlab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); editorLayout = Gtk::manage (new MyComboBoxText ()); - setExpandAlignProperties (editorLayout, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); - editorLayout->append (M ("PREFERENCES_SINGLETAB")); - editorLayout->append (M ("PREFERENCES_SINGLETABVERTAB")); - editorLayout->append (M ("PREFERENCES_MULTITAB")); - editorLayout->append (M ("PREFERENCES_MULTITABDUALMON")); - editorLayout->set_active (2); - Gtk::CellRendererText* cellRenderer = dynamic_cast (editorLayout->get_first_cell()); + setExpandAlignProperties(editorLayout, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); + editorLayout->append(M("PREFERENCES_SINGLETAB")); + editorLayout->append(M("PREFERENCES_SINGLETABVERTAB")); + editorLayout->append(M("PREFERENCES_MULTITAB")); + editorLayout->append(M("PREFERENCES_MULTITABDUALMON")); + editorLayout->set_active(2); + Gtk::CellRendererText* cellRenderer = dynamic_cast(editorLayout->get_first_cell()); cellRenderer->property_ellipsize() = Pango::ELLIPSIZE_MIDDLE; cellRenderer->property_ellipsize_set() = true; - editorLayout->signal_changed().connect (sigc::mem_fun (*this, &Preferences::layoutComboChanged)); + editorLayout->signal_changed().connect(sigc::mem_fun(*this, &Preferences::layoutComboChanged)); layoutComboChanged(); // update the tooltip - Gtk::Label* lNextStart = Gtk::manage ( new Gtk::Label (Glib::ustring ("(") + M ("PREFERENCES_APPLNEXTSTARTUP") + ")") ); - setExpandAlignProperties (lNextStart, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - workflowGrid->attach_next_to (*flayoutlab, Gtk::POS_LEFT, 1, 1); - workflowGrid->attach_next_to (*editorLayout, *flayoutlab, Gtk::POS_RIGHT, 1, 1); - workflowGrid->attach_next_to (*lNextStart, *editorLayout, Gtk::POS_RIGHT, 1, 1); + Gtk::Label* lNextStart = Gtk::manage(new Gtk::Label(Glib::ustring("(") + M("PREFERENCES_APPLNEXTSTARTUP") + ")")); + setExpandAlignProperties(lNextStart, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + workflowGrid->attach_next_to(*flayoutlab, Gtk::POS_LEFT, 1, 1); + workflowGrid->attach_next_to(*editorLayout, *flayoutlab, Gtk::POS_RIGHT, 1, 1); + workflowGrid->attach_next_to(*lNextStart, *editorLayout, Gtk::POS_RIGHT, 1, 1); - Gtk::Label* curveBBoxPosL = Gtk::manage (new Gtk::Label (M ("PREFERENCES_CURVEBBOXPOS") + ":")); - setExpandAlignProperties (curveBBoxPosL, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - curveBBoxPosC = Gtk::manage (new Gtk::ComboBoxText ()); - setExpandAlignProperties (curveBBoxPosC, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); - curveBBoxPosC->append (M ("PREFERENCES_CURVEBBOXPOS_ABOVE")); - curveBBoxPosC->append (M ("PREFERENCES_CURVEBBOXPOS_RIGHT")); - curveBBoxPosC->append (M ("PREFERENCES_CURVEBBOXPOS_BELOW")); - curveBBoxPosC->append (M ("PREFERENCES_CURVEBBOXPOS_LEFT")); - curveBBoxPosC->set_active (1); - Gtk::Label* curveBBoxPosRestartL = Gtk::manage (new Gtk::Label (Glib::ustring ("(") + M ("PREFERENCES_APPLNEXTSTARTUP") + ")")); - setExpandAlignProperties (curveBBoxPosRestartL, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - workflowGrid->attach_next_to (*curveBBoxPosL, *flayoutlab, Gtk::POS_BOTTOM, 1, 1); - workflowGrid->attach_next_to (*curveBBoxPosC, *editorLayout, Gtk::POS_BOTTOM, 1, 1); - workflowGrid->attach_next_to (*curveBBoxPosRestartL, *lNextStart, Gtk::POS_BOTTOM, 1, 1); + Gtk::Label* curveBBoxPosL = Gtk::manage(new Gtk::Label(M("PREFERENCES_CURVEBBOXPOS") + ":")); + setExpandAlignProperties(curveBBoxPosL, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + curveBBoxPosC = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(curveBBoxPosC, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); + curveBBoxPosC->append(M("PREFERENCES_CURVEBBOXPOS_ABOVE")); + curveBBoxPosC->append(M("PREFERENCES_CURVEBBOXPOS_RIGHT")); + curveBBoxPosC->append(M("PREFERENCES_CURVEBBOXPOS_BELOW")); + curveBBoxPosC->append(M("PREFERENCES_CURVEBBOXPOS_LEFT")); + curveBBoxPosC->set_active(1); + Gtk::Label* curveBBoxPosRestartL = Gtk::manage(new Gtk::Label(Glib::ustring("(") + M("PREFERENCES_APPLNEXTSTARTUP") + ")")); + setExpandAlignProperties(curveBBoxPosRestartL, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + workflowGrid->attach_next_to(*curveBBoxPosL, *flayoutlab, Gtk::POS_BOTTOM, 1, 1); + workflowGrid->attach_next_to(*curveBBoxPosC, *editorLayout, Gtk::POS_BOTTOM, 1, 1); + workflowGrid->attach_next_to(*curveBBoxPosRestartL, *lNextStart, Gtk::POS_BOTTOM, 1, 1); - ckbHistogramPositionLeft = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_HISTOGRAMPOSITIONLEFT")) ); - setExpandAlignProperties (ckbHistogramPositionLeft, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - workflowGrid->attach_next_to (*ckbHistogramPositionLeft, *curveBBoxPosL, Gtk::POS_BOTTOM, 1, 1); + Gtk::Label* complexityL = Gtk::manage(new Gtk::Label(M("PREFERENCES_COMPLEXITYLOC") + ":")); + setExpandAlignProperties(complexityL, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + complexitylocal = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(complexitylocal, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); + complexitylocal->append(M("PREFERENCES_COMPLEXITY_EXP")); + complexitylocal->append(M("PREFERENCES_COMPLEXITY_NORM")); + complexitylocal->set_active(1); + workflowGrid->attach_next_to(*complexityL, *curveBBoxPosL, Gtk::POS_BOTTOM, 1, 1); + workflowGrid->attach_next_to(*complexitylocal, *curveBBoxPosC, Gtk::POS_BOTTOM, 1, 1); - ckbFileBrowserToolbarSingleRow = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_FILEBROWSERTOOLBARSINGLEROW")) ); - setExpandAlignProperties (ckbFileBrowserToolbarSingleRow, false, false, Gtk::ALIGN_START, Gtk::ALIGN_START); - ckbShowFilmStripToolBar = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_SHOWFILMSTRIPTOOLBAR")) ); - setExpandAlignProperties (ckbShowFilmStripToolBar, false, false, Gtk::ALIGN_START, Gtk::ALIGN_START); - workflowGrid->attach_next_to (*ckbFileBrowserToolbarSingleRow, *ckbHistogramPositionLeft, Gtk::POS_BOTTOM, 1, 1); - workflowGrid->attach_next_to (*ckbShowFilmStripToolBar, *curveBBoxPosC, Gtk::POS_BOTTOM, 2, 1); + ckbHistogramPositionLeft = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_HISTOGRAMPOSITIONLEFT"))); + setExpandAlignProperties(ckbHistogramPositionLeft, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + workflowGrid->attach_next_to(*ckbHistogramPositionLeft, *complexityL, Gtk::POS_BOTTOM, 1, 1); - Gtk::Label* hb4label = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_TP_LABEL")) ); - setExpandAlignProperties (hb4label, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - ckbHideTPVScrollbar = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_TP_VSCROLLBAR")) ); - setExpandAlignProperties (ckbHideTPVScrollbar, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - workflowGrid->attach_next_to (*hb4label, *ckbFileBrowserToolbarSingleRow, Gtk::POS_BOTTOM, 1, 1); - workflowGrid->attach_next_to (*ckbHideTPVScrollbar, *hb4label, Gtk::POS_RIGHT, 1, 1); - ckbAutoSaveTpOpen = Gtk::manage (new Gtk::CheckButton (M ("PREFERENCES_AUTOSAVE_TP_OPEN"))); - workflowGrid->attach_next_to (*ckbAutoSaveTpOpen, *hb4label, Gtk::POS_BOTTOM, 1, 1); - btnSaveTpOpenNow = Gtk::manage (new Gtk::Button (M ("PREFERENCES_SAVE_TP_OPEN_NOW"))); - setExpandAlignProperties (btnSaveTpOpenNow, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - workflowGrid->attach_next_to (*btnSaveTpOpenNow, *ckbAutoSaveTpOpen, Gtk::POS_RIGHT, 1, 1); + ckbFileBrowserToolbarSingleRow = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_FILEBROWSERTOOLBARSINGLEROW"))); + setExpandAlignProperties(ckbFileBrowserToolbarSingleRow, false, false, Gtk::ALIGN_START, Gtk::ALIGN_START); + ckbShowFilmStripToolBar = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_SHOWFILMSTRIPTOOLBAR"))); + setExpandAlignProperties(ckbShowFilmStripToolBar, false, false, Gtk::ALIGN_START, Gtk::ALIGN_START); + workflowGrid->attach_next_to(*ckbFileBrowserToolbarSingleRow, *ckbHistogramPositionLeft, Gtk::POS_BOTTOM, 1, 1); + workflowGrid->attach_next_to(*ckbShowFilmStripToolBar, *complexitylocal, Gtk::POS_BOTTOM, 2, 1); + + Gtk::Label* hb4label = Gtk::manage(new Gtk::Label(M("PREFERENCES_TP_LABEL"))); + setExpandAlignProperties(hb4label, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + ckbHideTPVScrollbar = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_TP_VSCROLLBAR"))); + setExpandAlignProperties(ckbHideTPVScrollbar, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + workflowGrid->attach_next_to(*hb4label, *ckbFileBrowserToolbarSingleRow, Gtk::POS_BOTTOM, 1, 1); + workflowGrid->attach_next_to(*ckbHideTPVScrollbar, *hb4label, Gtk::POS_RIGHT, 1, 1); + ckbAutoSaveTpOpen = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_AUTOSAVE_TP_OPEN"))); + workflowGrid->attach_next_to(*ckbAutoSaveTpOpen, *hb4label, Gtk::POS_BOTTOM, 1, 1); + btnSaveTpOpenNow = Gtk::manage(new Gtk::Button(M("PREFERENCES_SAVE_TP_OPEN_NOW"))); + setExpandAlignProperties(btnSaveTpOpenNow, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + workflowGrid->attach_next_to(*btnSaveTpOpenNow, *ckbAutoSaveTpOpen, Gtk::POS_RIGHT, 1, 1); auto save_tp_open_now = [&]() -> void { - parent->writeToolExpandedStatus (moptions.tpOpen); + parent->writeToolExpandedStatus(moptions.tpOpen); }; - btnSaveTpOpenNow->signal_clicked().connect (save_tp_open_now); + btnSaveTpOpenNow->signal_clicked().connect(save_tp_open_now); - fworklflow->add (*workflowGrid); + ckbshowtooltiplocallab = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_SHOWTOOLTIP"))); + setExpandAlignProperties(ckbshowtooltiplocallab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_START); + workflowGrid->attach_next_to(*ckbshowtooltiplocallab, *ckbFileBrowserToolbarSingleRow, Gtk::POS_RIGHT, 1, 1); + + fworklflow->add(*workflowGrid); vbGeneral->attach_next_to (*fworklflow, Gtk::POS_TOP, 2, 1); // --------------------------------------------- - Gtk::Frame* flang = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_LANG")) ); - setExpandAlignProperties (flang, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); - Gtk::Grid* langGrid = Gtk::manage ( new Gtk::Grid() ); - langGrid->set_column_spacing (4); - langGrid->set_row_spacing (4); - setExpandAlignProperties (langGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); + Gtk::Frame* flang = Gtk::manage(new Gtk::Frame(M("PREFERENCES_LANG"))); + setExpandAlignProperties(flang, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); + Gtk::Grid* langGrid = Gtk::manage(new Gtk::Grid()); + langGrid->set_column_spacing(4); + langGrid->set_row_spacing(4); + setExpandAlignProperties(langGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); - ckbLangAutoDetect = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_LANGAUTODETECT")) ); - setExpandAlignProperties (ckbLangAutoDetect, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + ckbLangAutoDetect = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_LANGAUTODETECT"))); + setExpandAlignProperties(ckbLangAutoDetect, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - Gtk::Label* langlab = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_SELECTLANG") + ":") ); - setExpandAlignProperties (langlab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - languages = Gtk::manage ( new Gtk::ComboBoxText () ); - setExpandAlignProperties (languages, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + Gtk::Label* langlab = Gtk::manage(new Gtk::Label(M("PREFERENCES_SELECTLANG") + ":")); + setExpandAlignProperties(langlab, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + languages = Gtk::manage(new Gtk::ComboBoxText()); + setExpandAlignProperties(languages, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); std::vector langs; - parseDir (argv0 + "/languages", langs, ""); + parseDir(argv0 + "/languages", langs, ""); for (size_t i = 0; i < langs.size(); i++) { if ("default" != langs[i] && "README" != langs[i] && "LICENSE" != langs[i]) { - languages->append (langs[i]); + languages->append(langs[i]); } } - Gtk::Label* langw = Gtk::manage ( new Gtk::Label (Glib::ustring (" (") + M ("PREFERENCES_APPLNEXTSTARTUP") + ")") ); - setExpandAlignProperties (langw, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - langGrid->attach_next_to (*ckbLangAutoDetect, Gtk::POS_LEFT, 3, 1); - langGrid->attach_next_to (*langlab, *ckbLangAutoDetect, Gtk::POS_BOTTOM, 1, 1); - langGrid->attach_next_to (*languages, *langlab, Gtk::POS_RIGHT, 1, 1); - langGrid->attach_next_to (*langw, *languages, Gtk::POS_RIGHT, 1, 1); - flang->add (*langGrid); + Gtk::Label* langw = Gtk::manage(new Gtk::Label(Glib::ustring(" (") + M("PREFERENCES_APPLNEXTSTARTUP") + ")")); + setExpandAlignProperties(langw, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + langGrid->attach_next_to(*ckbLangAutoDetect, Gtk::POS_LEFT, 3, 1); + langGrid->attach_next_to(*langlab, *ckbLangAutoDetect, Gtk::POS_BOTTOM, 1, 1); + langGrid->attach_next_to(*languages, *langlab, Gtk::POS_RIGHT, 1, 1); + langGrid->attach_next_to(*langw, *languages, Gtk::POS_RIGHT, 1, 1); + flang->add(*langGrid); vbGeneral->attach_next_to (*flang, *fworklflow, Gtk::POS_BOTTOM, 2, 1); // Appearance --------------------------------------------- @@ -1057,6 +1080,7 @@ Gtk::Widget* Preferences::getGeneralPanel () setExpandAlignProperties(pseudoHiDPI, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); Gtk::VSeparator *vSep = Gtk::manage(new Gtk::VSeparator()); + appearanceGrid->attach(*themeLbl, 0, 0, 1, 1); appearanceGrid->attach(*themeCBT, 1, 0, 1, 1); @@ -1077,129 +1101,128 @@ Gtk::Widget* Preferences::getGeneralPanel () // --------------------------------------------- - Gtk::Frame* fclip = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_CLIPPINGIND"))); - setExpandAlignProperties (fclip, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - Gtk::Grid* clipGrid = Gtk::manage ( new Gtk::Grid() ); - clipGrid->set_column_spacing (4); - clipGrid->set_row_spacing (4); - setExpandAlignProperties (clipGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + Gtk::Frame* fclip = Gtk::manage(new Gtk::Frame(M("PREFERENCES_CLIPPINGIND"))); + setExpandAlignProperties(fclip, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + Gtk::Grid* clipGrid = Gtk::manage(new Gtk::Grid()); + clipGrid->set_column_spacing(4); + clipGrid->set_row_spacing(4); + setExpandAlignProperties(clipGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - Gtk::Label* hll = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_HLTHRESHOLD") + ": ")); - setExpandAlignProperties (hll, true, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - hlThresh = Gtk::manage ( new Gtk::SpinButton () ); - setExpandAlignProperties (hlThresh, false, false, Gtk::ALIGN_END, Gtk::ALIGN_BASELINE); - hlThresh->set_digits (0); - hlThresh->set_increments (1, 10); - hlThresh->set_range (0, 255); - clipGrid->attach_next_to (*hll, Gtk::POS_LEFT, 1, 1); - clipGrid->attach_next_to (*hlThresh, *hll, Gtk::POS_RIGHT, 1, 1); + Gtk::Label* hll = Gtk::manage(new Gtk::Label(M("PREFERENCES_HLTHRESHOLD") + ": ")); + setExpandAlignProperties(hll, true, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + hlThresh = Gtk::manage(new Gtk::SpinButton()); + setExpandAlignProperties(hlThresh, false, false, Gtk::ALIGN_END, Gtk::ALIGN_BASELINE); + hlThresh->set_digits(0); + hlThresh->set_increments(1, 10); + hlThresh->set_range(0, 255); + clipGrid->attach_next_to(*hll, Gtk::POS_LEFT, 1, 1); + clipGrid->attach_next_to(*hlThresh, *hll, Gtk::POS_RIGHT, 1, 1); - Gtk::Label* shl = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_SHTHRESHOLD") + ": ") ); - setExpandAlignProperties (shl, true, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - shThresh = Gtk::manage ( new Gtk::SpinButton () ); - setExpandAlignProperties (shThresh, false, false, Gtk::ALIGN_END, Gtk::ALIGN_BASELINE); - shThresh->show (); - shThresh->set_digits (0); - shThresh->set_increments (1, 10); - shThresh->set_range (0, 255); - clipGrid->attach_next_to (*shl, *hll, Gtk::POS_BOTTOM, 1, 1); - clipGrid->attach_next_to (*shThresh, *shl, Gtk::POS_RIGHT, 1, 1); + Gtk::Label* shl = Gtk::manage(new Gtk::Label(M("PREFERENCES_SHTHRESHOLD") + ": ")); + setExpandAlignProperties(shl, true, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + shThresh = Gtk::manage(new Gtk::SpinButton()); + setExpandAlignProperties(shThresh, false, false, Gtk::ALIGN_END, Gtk::ALIGN_BASELINE); + shThresh->show(); + shThresh->set_digits(0); + shThresh->set_increments(1, 10); + shThresh->set_range(0, 255); + clipGrid->attach_next_to(*shl, *hll, Gtk::POS_BOTTOM, 1, 1); + clipGrid->attach_next_to(*shThresh, *shl, Gtk::POS_RIGHT, 1, 1); - fclip->add (*clipGrid); + fclip->add(*clipGrid); vbGeneral->attach_next_to (*fclip, *appearanceFrame, Gtk::POS_BOTTOM, 1, 1); // --------------------------------------------- - Gtk::Frame* fnav = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_NAVIGATIONFRAME")) ); - setExpandAlignProperties (fclip, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - Gtk::Grid* navigationGrid = Gtk::manage ( new Gtk::Grid() ); - navigationGrid->set_column_spacing (4); - navigationGrid->set_row_spacing (4); - setExpandAlignProperties (fclip, false, false, Gtk::ALIGN_START, Gtk::ALIGN_FILL); + Gtk::Frame* fnav = Gtk::manage(new Gtk::Frame(M("PREFERENCES_NAVIGATIONFRAME"))); + setExpandAlignProperties(fclip, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + Gtk::Grid* navigationGrid = Gtk::manage(new Gtk::Grid()); + navigationGrid->set_column_spacing(4); + navigationGrid->set_row_spacing(4); + setExpandAlignProperties(fclip, false, false, Gtk::ALIGN_START, Gtk::ALIGN_FILL); - Gtk::Label* panFactorLabel = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_PANFACTORLABEL") + ":", Gtk::ALIGN_START)); - setExpandAlignProperties (panFactorLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - panFactor = Gtk::manage ( new Gtk::SpinButton () ); - setExpandAlignProperties (panFactor, true, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - panFactor->set_digits (0); - panFactor->set_increments (1, 5); - panFactor->set_range (1, 10); - navigationGrid->attach_next_to (*panFactorLabel, Gtk::POS_LEFT, 1, 1); - navigationGrid->attach_next_to (*panFactor, *panFactorLabel, Gtk::POS_RIGHT, 1, 1); + Gtk::Label* panFactorLabel = Gtk::manage(new Gtk::Label(M("PREFERENCES_PANFACTORLABEL") + ":", Gtk::ALIGN_START)); + setExpandAlignProperties(panFactorLabel, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + panFactor = Gtk::manage(new Gtk::SpinButton()); + setExpandAlignProperties(panFactor, true, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + panFactor->set_digits(0); + panFactor->set_increments(1, 5); + panFactor->set_range(1, 10); + navigationGrid->attach_next_to(*panFactorLabel, Gtk::POS_LEFT, 1, 1); + navigationGrid->attach_next_to(*panFactor, *panFactorLabel, Gtk::POS_RIGHT, 1, 1); - rememberZoomPanCheckbutton = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_REMEMBERZOOMPAN")) ); - setExpandAlignProperties (rememberZoomPanCheckbutton, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); - rememberZoomPanCheckbutton->set_tooltip_text (M ("PREFERENCES_REMEMBERZOOMPAN_TOOLTIP")); + rememberZoomPanCheckbutton = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_REMEMBERZOOMPAN"))); + setExpandAlignProperties(rememberZoomPanCheckbutton, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + rememberZoomPanCheckbutton->set_tooltip_text(M("PREFERENCES_REMEMBERZOOMPAN_TOOLTIP")); - navigationGrid->attach_next_to (*rememberZoomPanCheckbutton, *panFactorLabel, Gtk::POS_BOTTOM, 2, 1); + navigationGrid->attach_next_to(*rememberZoomPanCheckbutton, *panFactorLabel, Gtk::POS_BOTTOM, 2, 1); - fnav->add (*navigationGrid); + fnav->add(*navigationGrid); vbGeneral->attach_next_to (*fnav, *fclip, Gtk::POS_RIGHT, 1, 1); // --------------------------------------------- - Gtk::Frame* fdg = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_EXTERNALEDITOR")) ); - setExpandAlignProperties (fdg, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - Gtk::Grid* externaleditorGrid = Gtk::manage ( new Gtk::Grid() ); - externaleditorGrid->set_column_spacing (4); - externaleditorGrid->set_row_spacing (4); - setExpandAlignProperties (externaleditorGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + Gtk::Frame* fdg = Gtk::manage(new Gtk::Frame(M("PREFERENCES_EXTERNALEDITOR"))); + setExpandAlignProperties(fdg, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); + Gtk::Grid* externaleditorGrid = Gtk::manage(new Gtk::Grid()); + externaleditorGrid->set_column_spacing(4); + externaleditorGrid->set_row_spacing(4); + setExpandAlignProperties(externaleditorGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); - edOther = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_EDITORCMDLINE") + ":")); - setExpandAlignProperties (edOther, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - editorToSendTo = Gtk::manage ( new Gtk::Entry () ); - setExpandAlignProperties (editorToSendTo, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); + edOther = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_EDITORCMDLINE") + ":")); + setExpandAlignProperties(edOther, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + editorToSendTo = Gtk::manage(new Gtk::Entry()); + setExpandAlignProperties(editorToSendTo, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); Gtk::RadioButton::Group ge = edOther->get_group(); #ifdef __APPLE__ - edGimp = Gtk::manage ( new Gtk::RadioButton ("GIMP") ); - setExpandAlignProperties (edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - edGimp->set_group (ge); - externaleditorGrid->attach_next_to (*edGimp, Gtk::POS_TOP, 2, 1); + edGimp = Gtk::manage(new Gtk::RadioButton("GIMP")); + setExpandAlignProperties(edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + edGimp->set_group(ge); + externaleditorGrid->attach_next_to(*edGimp, Gtk::POS_TOP, 2, 1); - edPS = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_PSPATH") + ":")); - setExpandAlignProperties (edPS, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - psDir = Gtk::manage ( new MyFileChooserButton (M ("PREFERENCES_PSPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER) ); - setExpandAlignProperties (psDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - externaleditorGrid->attach_next_to (*edPS, *edGimp, Gtk::POS_BOTTOM, 1, 1); - externaleditorGrid->attach_next_to (*psDir, *edPS, Gtk::POS_RIGHT, 1, 1); - edPS->set_group (ge); + edPS = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_PSPATH") + ":")); + setExpandAlignProperties(edPS, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + psDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_PSPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + setExpandAlignProperties(psDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + externaleditorGrid->attach_next_to(*edPS, *edGimp, Gtk::POS_BOTTOM, 1, 1); + externaleditorGrid->attach_next_to(*psDir, *edPS, Gtk::POS_RIGHT, 1, 1); + edPS->set_group(ge); - externaleditorGrid->attach_next_to (*edOther, *edPS, Gtk::POS_BOTTOM, 1, 1); - externaleditorGrid->attach_next_to (*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); + externaleditorGrid->attach_next_to(*edOther, *edPS, Gtk::POS_BOTTOM, 1, 1); + externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); #elif defined WIN32 - edGimp = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_GIMPPATH") + ":") ); - setExpandAlignProperties (edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - gimpDir = Gtk::manage ( new MyFileChooserButton (M ("PREFERENCES_GIMPPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER) ); - setExpandAlignProperties (gimpDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - externaleditorGrid->attach_next_to (*edGimp, Gtk::POS_TOP, 1, 1); - externaleditorGrid->attach_next_to (*gimpDir, *edGimp, Gtk::POS_RIGHT, 1, 1); - edGimp->set_group (ge); + edGimp = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_GIMPPATH") + ":")); + setExpandAlignProperties(edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + gimpDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_GIMPPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + setExpandAlignProperties(gimpDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + externaleditorGrid->attach_next_to(*edGimp, Gtk::POS_TOP, 1, 1); + externaleditorGrid->attach_next_to(*gimpDir, *edGimp, Gtk::POS_RIGHT, 1, 1); + edGimp->set_group(ge); - edPS = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_PSPATH") + ":") ); - setExpandAlignProperties (edPS, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - psDir = Gtk::manage ( new MyFileChooserButton (M ("PREFERENCES_PSPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER) ); - setExpandAlignProperties (psDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); - externaleditorGrid->attach_next_to (*edPS, *edGimp, Gtk::POS_BOTTOM, 1, 1); - externaleditorGrid->attach_next_to (*psDir, *edPS, Gtk::POS_RIGHT, 1, 1); - edPS->set_group (ge); + edPS = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_PSPATH") + ":")); + setExpandAlignProperties(edPS, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + psDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_PSPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER)); + setExpandAlignProperties(psDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + externaleditorGrid->attach_next_to(*edPS, *edGimp, Gtk::POS_BOTTOM, 1, 1); + externaleditorGrid->attach_next_to(*psDir, *edPS, Gtk::POS_RIGHT, 1, 1); + edPS->set_group(ge); - externaleditorGrid->attach_next_to (*edOther, *edPS, Gtk::POS_BOTTOM, 1, 1); - externaleditorGrid->attach_next_to (*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); + externaleditorGrid->attach_next_to(*edOther, *edPS, Gtk::POS_BOTTOM, 1, 1); + externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); #else - edGimp = Gtk::manage ( new Gtk::RadioButton ("GIMP") ); - setExpandAlignProperties (edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - externaleditorGrid->attach_next_to (*edGimp, Gtk::POS_TOP, 2, 1); - edGimp->set_group (ge); + edGimp = Gtk::manage(new Gtk::RadioButton("GIMP")); + setExpandAlignProperties(edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + externaleditorGrid->attach_next_to(*edGimp, Gtk::POS_TOP, 2, 1); + edGimp->set_group(ge); - externaleditorGrid->attach_next_to (*edOther, *edGimp, Gtk::POS_BOTTOM, 1, 1); - externaleditorGrid->attach_next_to (*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); + externaleditorGrid->attach_next_to(*edOther, *edGimp, Gtk::POS_BOTTOM, 1, 1); + externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); #endif - fdg->add (*externaleditorGrid); + fdg->add(*externaleditorGrid); vbGeneral->attach_next_to (*fdg, *fclip, Gtk::POS_BOTTOM, 2, 1); - - langAutoDetectConn = ckbLangAutoDetect->signal_toggled().connect (sigc::mem_fun (*this, &Preferences::langAutoDetectToggled)); + langAutoDetectConn = ckbLangAutoDetect->signal_toggled().connect(sigc::mem_fun(*this, &Preferences::langAutoDetectToggled)); tconn = themeCBT->signal_changed().connect ( sigc::mem_fun (*this, &Preferences::themeChanged) ); fconn = mainFontFB->signal_font_set().connect ( sigc::mem_fun (*this, &Preferences::fontChanged) ); cpfconn = colorPickerFontFB->signal_font_set().connect ( sigc::mem_fun (*this, &Preferences::cpFontChanged) ); @@ -1208,103 +1231,103 @@ Gtk::Widget* Preferences::getGeneralPanel () return swGeneral; } -Gtk::Widget* Preferences::getFileBrowserPanel () +Gtk::Widget* Preferences::getFileBrowserPanel() { swFileBrowser = Gtk::manage(new Gtk::ScrolledWindow()); swFileBrowser->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); Gtk::VBox* vbFileBrowser = Gtk::manage ( new Gtk::VBox () ); - Gtk::Frame* fsd = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_STARTUPIMDIR")) ); + Gtk::Frame* fsd = Gtk::manage(new Gtk::Frame(M("PREFERENCES_STARTUPIMDIR"))); - sdcurrent = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_DIRSOFTWARE")) ); - sdlast = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_DIRLAST")) ); - sdhome = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_DIRHOME")) ); - sdother = Gtk::manage ( new Gtk::RadioButton (M ("PREFERENCES_DIROTHER") + ": ") ); - startupdir = Gtk::manage ( new Gtk::Entry () ); + sdcurrent = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIRSOFTWARE"))); + sdlast = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIRLAST"))); + sdhome = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIRHOME"))); + sdother = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_DIROTHER") + ": ")); + startupdir = Gtk::manage(new Gtk::Entry()); - Gtk::Button* sdselect = Gtk::manage ( new Gtk::Button () ); + Gtk::Button* sdselect = Gtk::manage(new Gtk::Button()); sdselect->set_image (*Gtk::manage (new RTImage ("folder-open-small.png"))); Gtk::RadioButton::Group opts = sdcurrent->get_group(); - sdlast->set_group (opts); - sdhome->set_group (opts); - sdother->set_group (opts); + sdlast->set_group(opts); + sdhome->set_group(opts); + sdother->set_group(opts); - Gtk::VBox* vbsd = Gtk::manage ( new Gtk::VBox () ); - vbsd->pack_start (*sdcurrent, Gtk::PACK_SHRINK, 0); - vbsd->pack_start (*sdlast, Gtk::PACK_SHRINK, 0); - vbsd->pack_start (*sdhome, Gtk::PACK_SHRINK, 0); - Gtk::HBox* otherbox = Gtk::manage ( new Gtk::HBox () ); - otherbox->pack_start (*sdother, Gtk::PACK_SHRINK); - otherbox->pack_start (*startupdir); - otherbox->pack_end (*sdselect, Gtk::PACK_SHRINK, 4); - vbsd->pack_start (*otherbox, Gtk::PACK_SHRINK, 0); + Gtk::VBox* vbsd = Gtk::manage(new Gtk::VBox()); + vbsd->pack_start(*sdcurrent, Gtk::PACK_SHRINK, 0); + vbsd->pack_start(*sdlast, Gtk::PACK_SHRINK, 0); + vbsd->pack_start(*sdhome, Gtk::PACK_SHRINK, 0); + Gtk::HBox* otherbox = Gtk::manage(new Gtk::HBox()); + otherbox->pack_start(*sdother, Gtk::PACK_SHRINK); + otherbox->pack_start(*startupdir); + otherbox->pack_end(*sdselect, Gtk::PACK_SHRINK, 4); + vbsd->pack_start(*otherbox, Gtk::PACK_SHRINK, 0); - fsd->add (*vbsd); + fsd->add(*vbsd); vbFileBrowser->pack_start (*fsd, Gtk::PACK_SHRINK, 4); - sdselect->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::selectStartupDir) ); + sdselect->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::selectStartupDir)); //--- - Gtk::Frame* fro = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_FBROWSEROPTS")) ); - showDateTime = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_SHOWDATETIME")) ); - showBasicExif = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_SHOWBASICEXIF")) ); - showExpComp = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_SHOWEXPOSURECOMPENSATION")) ); - Gtk::VBox* vbro = Gtk::manage ( new Gtk::VBox () ); - Gtk::HBox* hbro1 = Gtk::manage ( new Gtk::HBox () ); - Gtk::HBox* hbro0 = Gtk::manage ( new Gtk::HBox () ); - overlayedFileNames = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_OVERLAY_FILENAMES")) ); - filmStripOverlayedFileNames = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_OVERLAY_FILENAMES_FILMSTRIP")) ); - sameThumbSize = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT")) ); - sameThumbSize->set_tooltip_text (M ("PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT_HINT")); - ckbInternalThumbIfUntouched = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_INTERNALTHUMBIFUNTOUCHED"))); + Gtk::Frame* fro = Gtk::manage(new Gtk::Frame(M("PREFERENCES_FBROWSEROPTS"))); + showDateTime = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_SHOWDATETIME"))); + showBasicExif = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_SHOWBASICEXIF"))); + showExpComp = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_SHOWEXPOSURECOMPENSATION"))); + Gtk::VBox* vbro = Gtk::manage(new Gtk::VBox()); + Gtk::HBox* hbro1 = Gtk::manage(new Gtk::HBox()); + Gtk::HBox* hbro0 = Gtk::manage(new Gtk::HBox()); + overlayedFileNames = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_OVERLAY_FILENAMES"))); + filmStripOverlayedFileNames = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_OVERLAY_FILENAMES_FILMSTRIP"))); + sameThumbSize = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT"))); + sameThumbSize->set_tooltip_text(M("PREFERENCES_FSTRIP_SAME_THUMB_HEIGHT_HINT")); + ckbInternalThumbIfUntouched = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_INTERNALTHUMBIFUNTOUCHED"))); - vbro->pack_start (*showDateTime, Gtk::PACK_SHRINK, 0); - Gtk::Label* dflab = Gtk::manage ( new Gtk::Label (M ("PREFERENCES_DATEFORMAT") + ":", Gtk::ALIGN_START)); - dateformat = Gtk::manage ( new Gtk::Entry () ); - dateformat->set_tooltip_markup (M ("PREFERENCES_DATEFORMATHINT")); - dflab->set_tooltip_markup (M ("PREFERENCES_DATEFORMATHINT")); - hbro0->pack_start (*dflab, Gtk::PACK_SHRINK, 4); - hbro0->pack_start (*dateformat, Gtk::PACK_SHRINK, 0); + vbro->pack_start(*showDateTime, Gtk::PACK_SHRINK, 0); + Gtk::Label* dflab = Gtk::manage(new Gtk::Label(M("PREFERENCES_DATEFORMAT") + ":", Gtk::ALIGN_START)); + dateformat = Gtk::manage(new Gtk::Entry()); + dateformat->set_tooltip_markup(M("PREFERENCES_DATEFORMATHINT")); + dflab->set_tooltip_markup(M("PREFERENCES_DATEFORMATHINT")); + hbro0->pack_start(*dflab, Gtk::PACK_SHRINK, 4); + hbro0->pack_start(*dateformat, Gtk::PACK_SHRINK, 0); - vbro->pack_start (*hbro0, Gtk::PACK_SHRINK, 0); - hbro1->pack_start (*showBasicExif, Gtk::PACK_SHRINK, 0); - hbro1->pack_start (*showExpComp, Gtk::PACK_SHRINK, 4); - vbro->pack_start (*hbro1, Gtk::PACK_SHRINK, 0); - vbro->pack_start (*overlayedFileNames, Gtk::PACK_SHRINK, 0); - vbro->pack_start (*filmStripOverlayedFileNames, Gtk::PACK_SHRINK, 0); - vbro->pack_start (*sameThumbSize, Gtk::PACK_SHRINK, 0); - vbro->pack_start (*ckbInternalThumbIfUntouched, Gtk::PACK_SHRINK, 0); + vbro->pack_start(*hbro0, Gtk::PACK_SHRINK, 0); + hbro1->pack_start(*showBasicExif, Gtk::PACK_SHRINK, 0); + hbro1->pack_start(*showExpComp, Gtk::PACK_SHRINK, 4); + vbro->pack_start(*hbro1, Gtk::PACK_SHRINK, 0); + vbro->pack_start(*overlayedFileNames, Gtk::PACK_SHRINK, 0); + vbro->pack_start(*filmStripOverlayedFileNames, Gtk::PACK_SHRINK, 0); + vbro->pack_start(*sameThumbSize, Gtk::PACK_SHRINK, 0); + vbro->pack_start(*ckbInternalThumbIfUntouched, Gtk::PACK_SHRINK, 0); - Gtk::HBox* hbrecent = Gtk::manage ( new Gtk::HBox () ); + Gtk::HBox* hbrecent = Gtk::manage(new Gtk::HBox()); Gtk::Label* labrecent = Gtk::manage (new Gtk::Label (M("PREFERENCES_MAXRECENTFOLDERS") + ":", Gtk::ALIGN_START)); - maxRecentFolders = Gtk::manage ( new Gtk::SpinButton () ); - hbrecent->pack_start (*labrecent, Gtk::PACK_SHRINK, 4); - hbrecent->pack_start (*maxRecentFolders, Gtk::PACK_SHRINK, 4); - maxRecentFolders->set_digits (0); - maxRecentFolders->set_increments (1, 5); - maxRecentFolders->set_range (1, 25); - vbro->pack_start (*hbrecent, Gtk::PACK_SHRINK, 4); + maxRecentFolders = Gtk::manage(new Gtk::SpinButton()); + hbrecent->pack_start(*labrecent, Gtk::PACK_SHRINK, 4); + hbrecent->pack_start(*maxRecentFolders, Gtk::PACK_SHRINK, 4); + maxRecentFolders->set_digits(0); + maxRecentFolders->set_increments(1, 5); + maxRecentFolders->set_range(1, 25); + vbro->pack_start(*hbrecent, Gtk::PACK_SHRINK, 4); - fro->add (*vbro); + fro->add(*vbro); - Gtk::Frame* frmnu = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_MENUOPTIONS")) ); + Gtk::Frame* frmnu = Gtk::manage(new Gtk::Frame(M("PREFERENCES_MENUOPTIONS"))); Gtk::Grid* menuGrid = Gtk::manage(new Gtk::Grid()); menuGrid->get_style_context()->add_class("grid-spacing"); setExpandAlignProperties(menuGrid, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - ckbmenuGroupRank = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_MENUGROUPRANK")) ); + ckbmenuGroupRank = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPRANK"))); setExpandAlignProperties(ckbmenuGroupRank, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - ckbmenuGroupLabel = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_MENUGROUPLABEL")) ); - ckbmenuGroupFileOperations = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_MENUGROUPFILEOPERATIONS")) ); + ckbmenuGroupLabel = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPLABEL"))); + ckbmenuGroupFileOperations = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPFILEOPERATIONS"))); setExpandAlignProperties(ckbmenuGroupFileOperations, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); - ckbmenuGroupProfileOperations = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_MENUGROUPPROFILEOPERATIONS")) ); - ckbmenuGroupExtProg = Gtk::manage ( new Gtk::CheckButton (M ("PREFERENCES_MENUGROUPEXTPROGS")) ); + ckbmenuGroupProfileOperations = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPPROFILEOPERATIONS"))); + ckbmenuGroupExtProg = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_MENUGROUPEXTPROGS"))); Gtk::Label* groupRestartNeeded = Gtk::manage(new Gtk::Label (Glib::ustring ("(") + M("PREFERENCES_APPLNEXTSTARTUP") + ")", Gtk::ALIGN_START)); @@ -1318,54 +1341,54 @@ Gtk::Widget* Preferences::getFileBrowserPanel () frmnu->add (*menuGrid); - Gtk::Frame* fre = Gtk::manage ( new Gtk::Frame (M ("PREFERENCES_PARSEDEXT")) ); - Gtk::VBox* vbre = Gtk::manage ( new Gtk::VBox () ); - Gtk::HBox* hb0 = Gtk::manage ( new Gtk::HBox () ); + Gtk::Frame* fre = Gtk::manage(new Gtk::Frame(M("PREFERENCES_PARSEDEXT"))); + Gtk::VBox* vbre = Gtk::manage(new Gtk::VBox()); + Gtk::HBox* hb0 = Gtk::manage(new Gtk::HBox()); Gtk::Label* elab = Gtk::manage (new Gtk::Label (M("PREFERENCES_PARSEDEXTADD") + ":", Gtk::ALIGN_START)); - hb0->pack_start (*elab, Gtk::PACK_SHRINK, 4); - extension = Gtk::manage ( new Gtk::Entry () ); - extension->set_width_chars (5); - extension->set_max_width_chars (5); - hb0->pack_start (*extension); - addExt = Gtk::manage ( new Gtk::Button () ); - delExt = Gtk::manage ( new Gtk::Button () ); - moveExtUp = Gtk::manage ( new Gtk::Button () ); - moveExtDown = Gtk::manage ( new Gtk::Button () ); - addExt->set_tooltip_text (M ("PREFERENCES_PARSEDEXTADDHINT")); - delExt->set_tooltip_text (M ("PREFERENCES_PARSEDEXTDELHINT")); - moveExtUp->set_tooltip_text (M ("PREFERENCES_PARSEDEXTUPHINT")); - moveExtDown->set_tooltip_text (M ("PREFERENCES_PARSEDEXTDOWNHINT")); + hb0->pack_start(*elab, Gtk::PACK_SHRINK, 4); + extension = Gtk::manage(new Gtk::Entry()); + extension->set_width_chars(5); + extension->set_max_width_chars(5); + hb0->pack_start(*extension); + addExt = Gtk::manage(new Gtk::Button()); + delExt = Gtk::manage(new Gtk::Button()); + moveExtUp = Gtk::manage(new Gtk::Button()); + moveExtDown = Gtk::manage(new Gtk::Button()); + addExt->set_tooltip_text(M("PREFERENCES_PARSEDEXTADDHINT")); + delExt->set_tooltip_text(M("PREFERENCES_PARSEDEXTDELHINT")); + moveExtUp->set_tooltip_text(M("PREFERENCES_PARSEDEXTUPHINT")); + moveExtDown->set_tooltip_text(M("PREFERENCES_PARSEDEXTDOWNHINT")); Gtk::Image* addExtImg = Gtk::manage ( new RTImage ("add-small.png") ); Gtk::Image* delExtImg = Gtk::manage ( new RTImage ("remove-small.png") ); - Gtk::Image* moveExtUpImg = Gtk::manage ( new RTImage ("arrow-up-small.png") ); - Gtk::Image* moveExtDownImg = Gtk::manage ( new RTImage ("arrow-down-small.png") ); - addExt->add (*addExtImg); - delExt->add (*delExtImg); - moveExtUp->set_image (*moveExtUpImg); - moveExtDown->set_image (*moveExtDownImg); - hb0->pack_end (*moveExtDown, Gtk::PACK_SHRINK, 4); - hb0->pack_end (*moveExtUp, Gtk::PACK_SHRINK, 4); - hb0->pack_end (*delExt, Gtk::PACK_SHRINK, 4); - hb0->pack_end (*addExt, Gtk::PACK_SHRINK, 4); - extensions = Gtk::manage ( new Gtk::TreeView () ); - Gtk::ScrolledWindow* hscrollw = Gtk::manage ( new Gtk::ScrolledWindow () ); - hscrollw->set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS); - hscrollw->add (*extensions); - extensionModel = Gtk::ListStore::create (extensionColumns); - extensions->set_model (extensionModel); - extensions->append_column_editable ("Enabled", extensionColumns.enabled); - extensions->append_column ("Extension", extensionColumns.ext); - extensions->set_headers_visible (false); - vbre->pack_start (*hscrollw); - vbre->pack_start (*hb0, Gtk::PACK_SHRINK, 4); + Gtk::Image* moveExtUpImg = Gtk::manage(new RTImage("arrow-up-small.png")); + Gtk::Image* moveExtDownImg = Gtk::manage(new RTImage("arrow-down-small.png")); + addExt->add(*addExtImg); + delExt->add(*delExtImg); + moveExtUp->set_image(*moveExtUpImg); + moveExtDown->set_image(*moveExtDownImg); + hb0->pack_end(*moveExtDown, Gtk::PACK_SHRINK, 4); + hb0->pack_end(*moveExtUp, Gtk::PACK_SHRINK, 4); + hb0->pack_end(*delExt, Gtk::PACK_SHRINK, 4); + hb0->pack_end(*addExt, Gtk::PACK_SHRINK, 4); + extensions = Gtk::manage(new Gtk::TreeView()); + Gtk::ScrolledWindow* hscrollw = Gtk::manage(new Gtk::ScrolledWindow()); + hscrollw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS); + hscrollw->add(*extensions); + extensionModel = Gtk::ListStore::create(extensionColumns); + extensions->set_model(extensionModel); + extensions->append_column_editable("Enabled", extensionColumns.enabled); + extensions->append_column("Extension", extensionColumns.ext); + extensions->set_headers_visible(false); + vbre->pack_start(*hscrollw); + vbre->pack_start(*hb0, Gtk::PACK_SHRINK, 4); - fre->add (*vbre); + fre->add(*vbre); // Cache Gtk::Frame* frc = Gtk::manage (new Gtk::Frame(M("PREFERENCES_CACHEOPTS"))); Gtk::VBox* vbc = Gtk::manage (new Gtk::VBox()); - frc->add (*vbc); + frc->add(*vbc); Gtk::Grid* cacheGrid = Gtk::manage(new Gtk::Grid()); cacheGrid->get_style_context()->add_class("grid-spacing"); @@ -1423,23 +1446,23 @@ Gtk::Widget* Preferences::getFileBrowserPanel () clearSafetyLbl->set_line_wrap(true); vbc->pack_start(*clearSafetyLbl, Gtk::PACK_SHRINK, 4); - Gtk::HBox* hb6 = Gtk::manage ( new Gtk::HBox () ); - Gtk::VBox* vb6 = Gtk::manage ( new Gtk::VBox () ); + Gtk::HBox* hb6 = Gtk::manage(new Gtk::HBox()); + Gtk::VBox* vb6 = Gtk::manage(new Gtk::VBox()); - vb6->pack_start (*fro); - vb6->pack_start (*frmnu); - vb6->pack_end (*frc); - hb6->pack_start (*vb6); - hb6->pack_start (*fre); - hb6->set_spacing (4); + vb6->pack_start(*fro); + vb6->pack_start(*frmnu); + vb6->pack_end(*frc); + hb6->pack_start(*vb6); + hb6->pack_start(*fre); + hb6->set_spacing(4); vbFileBrowser->pack_start (*hb6, Gtk::PACK_SHRINK, 4); - addExt->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::addExtPressed) ); - delExt->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::delExtPressed) ); - moveExtUp->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::moveExtUpPressed) ); - moveExtDown->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::moveExtDownPressed) ); - extension->signal_activate().connect ( sigc::mem_fun (*this, &Preferences::addExtPressed) ); + addExt->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::addExtPressed)); + delExt->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::delExtPressed)); + moveExtUp->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::moveExtUpPressed)); + moveExtDown->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::moveExtDownPressed)); + extension->signal_activate().connect(sigc::mem_fun(*this, &Preferences::addExtPressed)); clearThumbsBtn->signal_clicked().connect ( sigc::mem_fun (*this, &Preferences::clearThumbImagesPressed) ); if (moptions.saveParamsCache) { clearProfilesBtn->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::clearProfilesPressed)); @@ -1457,44 +1480,44 @@ Gtk::Widget* Preferences::getSoundsPanel () Gtk::VBox* vbSounds = Gtk::manage(new Gtk::VBox ()); - ckbSndEnable = Gtk::manage ( new Gtk::CheckButton (M ("GENERAL_ENABLE"))); - sndEnableConn = ckbSndEnable->signal_toggled().connect (sigc::mem_fun (*this, &Preferences::sndEnableToggled)); + ckbSndEnable = Gtk::manage(new Gtk::CheckButton(M("GENERAL_ENABLE"))); + sndEnableConn = ckbSndEnable->signal_toggled().connect(sigc::mem_fun(*this, &Preferences::sndEnableToggled)); vbSounds->pack_start (*ckbSndEnable, Gtk::PACK_SHRINK, 4); - Gtk::HBox* hblSndHelp = Gtk::manage (new Gtk::HBox ()); + Gtk::HBox* hblSndHelp = Gtk::manage(new Gtk::HBox()); Gtk::Label* lSndHelp = Gtk::manage (new Gtk::Label (M("PREFERENCES_SND_HELP"), Gtk::ALIGN_START)); - hblSndHelp->pack_start (*lSndHelp, Gtk::PACK_SHRINK, 4); + hblSndHelp->pack_start(*lSndHelp, Gtk::PACK_SHRINK, 4); vbSounds->pack_start (*hblSndHelp, Gtk::PACK_SHRINK, 4); // BatchQueueDone - Gtk::HBox* pBatchQueueDone = Gtk::manage ( new Gtk::HBox() ); + Gtk::HBox* pBatchQueueDone = Gtk::manage(new Gtk::HBox()); Gtk::Label* lSndBatchQueueDone = Gtk::manage (new Gtk::Label (M("PREFERENCES_SND_QUEUEDONE") + Glib::ustring (":"), Gtk::ALIGN_START)); pBatchQueueDone->pack_start (*lSndBatchQueueDone, Gtk::PACK_SHRINK, 4); - txtSndBatchQueueDone = Gtk::manage (new Gtk::Entry()); - pBatchQueueDone->pack_end (*txtSndBatchQueueDone, Gtk::PACK_EXPAND_WIDGET, 4); + txtSndBatchQueueDone = Gtk::manage(new Gtk::Entry()); + pBatchQueueDone->pack_end(*txtSndBatchQueueDone, Gtk::PACK_EXPAND_WIDGET, 4); vbSounds->pack_start (*pBatchQueueDone, Gtk::PACK_SHRINK, 4); // LngEditProcDone - Gtk::HBox* pSndLngEditProcDone = Gtk::manage ( new Gtk::HBox() ); + Gtk::HBox* pSndLngEditProcDone = Gtk::manage(new Gtk::HBox()); Gtk::Label* lSndLngEditProcDone = Gtk::manage (new Gtk::Label (M("PREFERENCES_SND_LNGEDITPROCDONE") + Glib::ustring (":"), Gtk::ALIGN_START)); - pSndLngEditProcDone->pack_start (*lSndLngEditProcDone, Gtk::PACK_SHRINK, 4); + pSndLngEditProcDone->pack_start(*lSndLngEditProcDone, Gtk::PACK_SHRINK, 4); - txtSndLngEditProcDone = Gtk::manage (new Gtk::Entry()); - pSndLngEditProcDone->pack_start (*txtSndLngEditProcDone, Gtk::PACK_EXPAND_WIDGET, 4); + txtSndLngEditProcDone = Gtk::manage(new Gtk::Entry()); + pSndLngEditProcDone->pack_start(*txtSndLngEditProcDone, Gtk::PACK_EXPAND_WIDGET, 4); Gtk::Label* lSndLngEditProcDoneSecs = Gtk::manage (new Gtk::Label (M("PREFERENCES_SND_THRESHOLDSECS") + Glib::ustring (":"), Gtk::ALIGN_START)); - pSndLngEditProcDone->pack_start (*lSndLngEditProcDoneSecs, Gtk::PACK_SHRINK, 12); + pSndLngEditProcDone->pack_start(*lSndLngEditProcDoneSecs, Gtk::PACK_SHRINK, 12); - spbSndLngEditProcDoneSecs = Gtk::manage ( new Gtk::SpinButton () ); - spbSndLngEditProcDoneSecs->set_digits (1); - spbSndLngEditProcDoneSecs->set_increments (0.5, 1); - spbSndLngEditProcDoneSecs->set_range (0, 10); - pSndLngEditProcDone->pack_end (*spbSndLngEditProcDoneSecs, Gtk::PACK_SHRINK, 4); + spbSndLngEditProcDoneSecs = Gtk::manage(new Gtk::SpinButton()); + spbSndLngEditProcDoneSecs->set_digits(1); + spbSndLngEditProcDoneSecs->set_increments(0.5, 1); + spbSndLngEditProcDoneSecs->set_range(0, 10); + pSndLngEditProcDone->pack_end(*spbSndLngEditProcDoneSecs, Gtk::PACK_SHRINK, 4); vbSounds->pack_start (*pSndLngEditProcDone, Gtk::PACK_SHRINK, 4); @@ -1504,7 +1527,7 @@ Gtk::Widget* Preferences::getSoundsPanel () return swSounds; } -void Preferences::parseDir (Glib::ustring dirname, std::vector& items, Glib::ustring ext) +void Preferences::parseDir(Glib::ustring dirname, std::vector& items, Glib::ustring ext) { if (dirname.empty()) { @@ -1515,26 +1538,26 @@ void Preferences::parseDir (Glib::ustring dirname, std::vector& i Glib::Dir* dir = nullptr; try { - dir = new Glib::Dir (dirname); + dir = new Glib::Dir(dirname); } catch (const Glib::Error& e) { return; } for (Glib::DirIterator i = dir->begin(); i != dir->end(); ++i) { - Glib::ustring fname = Glib::build_filename (dirname, *i); + Glib::ustring fname = Glib::build_filename(dirname, *i); Glib::ustring sname = *i; // ignore directories - if (!Glib::file_test (fname, Glib::FILE_TEST_IS_DIR) && sname.size() >= ext.size() && sname.substr (sname.size() - ext.size(), ext.size()).casefold() == ext) { - items.push_back (sname.substr (0, sname.size() - ext.size())); + if (!Glib::file_test(fname, Glib::FILE_TEST_IS_DIR) && sname.size() >= ext.size() && sname.substr(sname.size() - ext.size(), ext.size()).casefold() == ext) { + items.push_back(sname.substr(0, sname.size() - ext.size())); } } - std::sort (items.begin(), items.end()); + std::sort(items.begin(), items.end()); delete dir; } -void Preferences::parseThemeDir (Glib::ustring dirname) +void Preferences::parseThemeDir(Glib::ustring dirname) { if (dirname.empty()) { @@ -1545,24 +1568,24 @@ void Preferences::parseThemeDir (Glib::ustring dirname) Glib::Dir* dir = nullptr; try { - dir = new Glib::Dir (dirname); + dir = new Glib::Dir(dirname); } catch (const Glib::Error& e) { return; } for (Glib::DirIterator i = dir->begin(); i != dir->end(); ++i) { - Glib::ustring fname = Glib::build_filename (dirname, *i); + Glib::ustring fname = Glib::build_filename(dirname, *i); Glib::ustring sname = *i; // ignore directories and filter out unsupported theme - if (regex->match (sname, matchInfo) && !Glib::file_test (fname, Glib::FILE_TEST_IS_DIR) && sname.size() >= 4) { + if (regex->match(sname, matchInfo) && !Glib::file_test(fname, Glib::FILE_TEST_IS_DIR) && sname.size() >= 4) { bool keepIt = false; - Glib::ustring fname2 = matchInfo.fetch (1); - Glib::ustring minMinor = matchInfo.fetch (2); - Glib::ustring maxMinor = matchInfo.fetch (3); + Glib::ustring fname2 = matchInfo.fetch(1); + Glib::ustring minMinor = matchInfo.fetch(2); + Glib::ustring maxMinor = matchInfo.fetch(3); if (!minMinor.empty()) { - guint64 minMinorVal = g_ascii_strtoll (minMinor.c_str(), 0, 0); + guint64 minMinorVal = g_ascii_strtoll(minMinor.c_str(), 0, 0); if ((guint64)GTK_MINOR_VERSION >= minMinorVal) { keepIt = true; @@ -1570,7 +1593,7 @@ void Preferences::parseThemeDir (Glib::ustring dirname) } if (!maxMinor.empty()) { - guint64 maxMinorVal = g_ascii_strtoll (maxMinor.c_str(), 0, 0); + guint64 maxMinorVal = g_ascii_strtoll(maxMinor.c_str(), 0, 0); if ((guint64)GTK_MINOR_VERSION <= maxMinorVal) { keepIt = true; @@ -1578,19 +1601,19 @@ void Preferences::parseThemeDir (Glib::ustring dirname) } if (keepIt) { - themeFNames.push_back (ThemeFilename (matchInfo.fetch (1), sname.substr (0, sname.size() - 4))); + themeFNames.push_back(ThemeFilename(matchInfo.fetch(1), sname.substr(0, sname.size() - 4))); } } } - std::sort (themeFNames.begin(), themeFNames.end(), [] (const ThemeFilename & firstDir, const ThemeFilename & secondDir) { + std::sort(themeFNames.begin(), themeFNames.end(), [](const ThemeFilename & firstDir, const ThemeFilename & secondDir) { return firstDir.longFName < secondDir.longFName; }); delete dir; } -void Preferences::storePreferences () +void Preferences::storePreferences() { // With the new mechanism, we can't be sure of the availability of the DEFPROFILE_RAW & DEFPROFILE_IMG profiles, @@ -1611,18 +1634,18 @@ void Preferences::storePreferences () moptions.dateFormat = dateformat->get_text(); moptions.panAccelFactor = (int)panFactor->get_value(); moptions.rememberZoomAndPan = rememberZoomPanCheckbutton->get_active(); - moptions.fbShowDateTime = showDateTime->get_active (); - moptions.fbShowBasicExif = showBasicExif->get_active (); - moptions.fbShowExpComp = showExpComp->get_active (); + moptions.fbShowDateTime = showDateTime->get_active(); + moptions.fbShowBasicExif = showBasicExif->get_active(); + moptions.fbShowExpComp = showExpComp->get_active(); moptions.menuGroupRank = ckbmenuGroupRank->get_active(); moptions.menuGroupLabel = ckbmenuGroupLabel->get_active(); moptions.menuGroupFileOperations = ckbmenuGroupFileOperations->get_active(); moptions.menuGroupProfileOperations = ckbmenuGroupProfileOperations->get_active(); moptions.menuGroupExtProg = ckbmenuGroupExtProg->get_active(); - moptions.highlightThreshold = (int)hlThresh->get_value (); - moptions.shadowThreshold = (int)shThresh->get_value (); - moptions.language = languages->get_active_text (); - moptions.languageAutoDetect = ckbLangAutoDetect->get_active (); + moptions.highlightThreshold = (int)hlThresh->get_value(); + moptions.shadowThreshold = (int)shThresh->get_value(); + moptions.language = languages->get_active_text(); + moptions.languageAutoDetect = ckbLangAutoDetect->get_active(); moptions.theme = themeFNames.at (themeCBT->get_active_row_number ()).longFName; Gdk::RGBA cropCol = cropMaskColorCB->get_rgba(); @@ -1636,9 +1659,9 @@ void Preferences::storePreferences () moptions.navGuideBrush[1] = NavGuideCol.get_green(); moptions.navGuideBrush[2] = NavGuideCol.get_blue(); moptions.navGuideBrush[3] = navGuideColorCB->get_alpha() / 65535.0; - Pango::FontDescription fd (mainFontFB->get_font_name()); + if (newFont) { moptions.fontFamily = fd.get_family(); moptions.fontSize = fd.get_size() / Pango::SCALE; @@ -1654,42 +1677,42 @@ void Preferences::storePreferences () moptions.pseudoHiDPISupport = pseudoHiDPI->get_active(); #ifdef WIN32 - moptions.gimpDir = gimpDir->get_filename (); - moptions.psDir = psDir->get_filename (); + moptions.gimpDir = gimpDir->get_filename(); + moptions.psDir = psDir->get_filename(); #elif defined __APPLE__ - moptions.psDir = psDir->get_filename (); + moptions.psDir = psDir->get_filename(); #endif - moptions.customEditorProg = editorToSendTo->get_text (); + moptions.customEditorProg = editorToSendTo->get_text(); - if (edGimp->get_active ()) { + if (edGimp->get_active()) { moptions.editorToSendTo = 1; } #ifdef WIN32 - else if (edPS->get_active ()) { + else if (edPS->get_active()) { moptions.editorToSendTo = 2; } #elif defined __APPLE__ - else if (edPS->get_active ()) { + else if (edPS->get_active()) { moptions.editorToSendTo = 2; } #endif - else if (edOther->get_active ()) { + else if (edOther->get_active()) { moptions.editorToSendTo = 3; } moptions.CPBPath = txtCustProfBuilderPath->get_text(); - moptions.CPBKeys = CPBKeyType (custProfBuilderLabelType->get_active_row_number()); + moptions.CPBKeys = CPBKeyType(custProfBuilderLabelType->get_active_row_number()); if (!prtProfile->get_active_row_number()) { moptions.rtSettings.printerProfile = ""; } else { - moptions.rtSettings.printerProfile = prtProfile->get_active_text (); + moptions.rtSettings.printerProfile = prtProfile->get_active_text(); } - switch (prtIntent->get_active_row_number ()) { + switch (prtIntent->get_active_row_number()) { default: case 0: moptions.rtSettings.printerIntent = rtengine::RI_PERCEPTUAL; @@ -1704,17 +1727,17 @@ void Preferences::storePreferences () break; } - moptions.rtSettings.printerBPC = prtBPC->get_active (); + moptions.rtSettings.printerBPC = prtBPC->get_active(); #if !defined(__APPLE__) // monitor profile not supported on apple if (!monProfile->get_active_row_number()) { moptions.rtSettings.monitorProfile = ""; } else { - moptions.rtSettings.monitorProfile = monProfile->get_active_text (); + moptions.rtSettings.monitorProfile = monProfile->get_active_text(); } - switch (monIntent->get_active_row_number ()) { + switch (monIntent->get_active_row_number()) { default: case 0: moptions.rtSettings.monitorIntent = rtengine::RI_PERCEPTUAL; @@ -1729,59 +1752,59 @@ void Preferences::storePreferences () break; } - moptions.rtSettings.monitorBPC = monBPC->get_active (); - moptions.rtSettings.autoMonitorProfile = cbAutoMonProfile->get_active (); + moptions.rtSettings.monitorBPC = monBPC->get_active(); + moptions.rtSettings.autoMonitorProfile = cbAutoMonProfile->get_active(); #endif - moptions.rtSettings.iccDirectory = iccDir->get_filename (); + moptions.rtSettings.iccDirectory = iccDir->get_filename(); moptions.prevdemo = (prevdemo_t)cprevdemo->get_active_row_number (); moptions.serializeTiffRead = ctiffserialize->get_active(); - if (sdcurrent->get_active ()) { + if (sdcurrent->get_active()) { moptions.startupDir = STARTUPDIR_CURRENT; - } else if (sdhome->get_active ()) { + } else if (sdhome->get_active()) { moptions.startupDir = STARTUPDIR_HOME; - } else if (sdlast->get_active ()) { + } else if (sdlast->get_active()) { moptions.startupDir = STARTUPDIR_LAST; - } else if (sdother->get_active ()) { + } else if (sdother->get_active()) { moptions.startupDir = STARTUPDIR_CUSTOM; moptions.startupPath = startupdir->get_text(); } - moptions.parseExtensions.clear (); - moptions.parseExtensionsEnabled.clear (); - Gtk::TreeNodeChildren c = extensionModel->children (); + moptions.parseExtensions.clear(); + moptions.parseExtensionsEnabled.clear(); + Gtk::TreeNodeChildren c = extensionModel->children(); for (size_t i = 0; i < c.size(); i++) { - moptions.parseExtensions.push_back (c[i][extensionColumns.ext]); - moptions.parseExtensionsEnabled.push_back (c[i][extensionColumns.enabled]); + moptions.parseExtensions.push_back(c[i][extensionColumns.ext]); + moptions.parseExtensionsEnabled.push_back(c[i][extensionColumns.enabled]); } moptions.maxRecentFolders = (int)maxRecentFolders->get_value(); moptions.maxThumbnailHeight = (int)maxThumbHeightSB->get_value (); moptions.maxCacheEntries = (int)maxCacheEntriesSB->get_value (); - moptions.overlayedFileNames = overlayedFileNames->get_active (); + moptions.overlayedFileNames = overlayedFileNames->get_active(); moptions.filmStripOverlayedFileNames = filmStripOverlayedFileNames->get_active(); moptions.sameThumbSize = sameThumbSize->get_active(); - moptions.internalThumbIfUntouched = ckbInternalThumbIfUntouched->get_active (); + moptions.internalThumbIfUntouched = ckbInternalThumbIfUntouched->get_active(); auto save_where = saveParamsPreference->get_active_row_number(); moptions.saveParamsFile = save_where == 0 || save_where == 2; moptions.saveParamsCache = save_where == 1 || save_where == 2; - moptions.paramsLoadLocation = (PPLoadLocation)loadParamsPreference->get_active_row_number (); - moptions.useBundledProfiles = useBundledProfiles->get_active (); + moptions.paramsLoadLocation = (PPLoadLocation)loadParamsPreference->get_active_row_number(); + moptions.useBundledProfiles = useBundledProfiles->get_active(); moptions.rtSettings.darkFramesPath = darkFrameDir->get_filename(); moptions.rtSettings.flatFieldsPath = flatFieldDir->get_filename(); moptions.clutsDir = clutsDir->get_filename(); - moptions.baBehav.resize (ADDSET_PARAM_NUM); + moptions.baBehav.resize(ADDSET_PARAM_NUM); for (Gtk::TreeIter sections = behModel->children().begin(); sections != behModel->children().end(); sections++) for (Gtk::TreeIter adjs = sections->children().begin(); adjs != sections->children().end(); adjs++) { - moptions.baBehav[adjs->get_value (behavColumns.addsetid)] = adjs->get_value (behavColumns.badd); + moptions.baBehav[adjs->get_value(behavColumns.addsetid)] = adjs->get_value(behavColumns.badd); } int editorMode = editorLayout->get_active_row_number(); @@ -1790,11 +1813,13 @@ void Preferences::storePreferences () moptions.mainNBVertical = editorMode == 1; moptions.curvebboxpos = curveBBoxPosC->get_active_row_number(); + moptions.complexity = complexitylocal->get_active_row_number(); moptions.histogramPosition = ckbHistogramPositionLeft->get_active() ? 1 : 2; moptions.FileBrowserToolbarSingleRow = ckbFileBrowserToolbarSingleRow->get_active(); moptions.showFilmStripToolBar = ckbShowFilmStripToolBar->get_active(); moptions.hideTPVScrollbar = ckbHideTPVScrollbar->get_active(); - moptions.overwriteOutputFile = chOverwriteOutputFile->get_active (); + moptions.overwriteOutputFile = chOverwriteOutputFile->get_active(); + moptions.showtooltip = ckbshowtooltiplocallab->get_active(); moptions.autoSaveTpOpen = ckbAutoSaveTpOpen->get_active(); @@ -1811,97 +1836,96 @@ void Preferences::storePreferences () // Sounds only on Windows and Linux #if defined(WIN32) || defined(__linux__) - moptions.sndEnable = ckbSndEnable->get_active (); - moptions.sndBatchQueueDone = txtSndBatchQueueDone->get_text (); - moptions.sndLngEditProcDone = txtSndLngEditProcDone->get_text (); - moptions.sndLngEditProcDoneSecs = spbSndLngEditProcDoneSecs->get_value (); + moptions.sndEnable = ckbSndEnable->get_active(); + moptions.sndBatchQueueDone = txtSndBatchQueueDone->get_text(); + moptions.sndLngEditProcDone = txtSndLngEditProcDone->get_text(); + moptions.sndLngEditProcDoneSecs = spbSndLngEditProcDoneSecs->get_value(); #endif moptions.cropGuides = Options::CropGuidesMode(cropGuidesCombo->get_active_row_number()); moptions.cropAutoFit = cropAutoFitCB->get_active(); } -void Preferences::fillPreferences () +void Preferences::fillPreferences() { - tconn.block (true); - fconn.block (true); - cpfconn.block (true); - sconn.block (true); - dfconn.block (true); - ffconn.block (true); - rpconn.block (true); - ipconn.block (true); - bpconn.block (true); + tconn.block(true); + fconn.block(true); + cpfconn.block(true); + sconn.block(true); + dfconn.block(true); + ffconn.block(true); + rpconn.block(true); + ipconn.block(true); + bpconn.block(true); - rprofiles->setActiveRowFromFullPath (moptions.defProfRaw); + rprofiles->setActiveRowFromFullPath(moptions.defProfRaw); forRAWComboChanged(); // update the tooltip - iprofiles->setActiveRowFromFullPath (moptions.defProfImg); + iprofiles->setActiveRowFromFullPath(moptions.defProfImg); forImageComboChanged(); // update the tooltip - dateformat->set_text (moptions.dateFormat); - panFactor->set_value (moptions.panAccelFactor); - rememberZoomPanCheckbutton->set_active (moptions.rememberZoomAndPan); - ctiffserialize->set_active (moptions.serializeTiffRead); + dateformat->set_text(moptions.dateFormat); + panFactor->set_value(moptions.panAccelFactor); + rememberZoomPanCheckbutton->set_active(moptions.rememberZoomAndPan); + ctiffserialize->set_active(moptions.serializeTiffRead); - setActiveTextOrIndex (*prtProfile, moptions.rtSettings.printerProfile, 0); + setActiveTextOrIndex(*prtProfile, moptions.rtSettings.printerProfile, 0); switch (moptions.rtSettings.printerIntent) { default: case rtengine::RI_PERCEPTUAL: - prtIntent->set_active (0); + prtIntent->set_active(0); break; case rtengine::RI_RELATIVE: - prtIntent->set_active (1); + prtIntent->set_active(1); break; case rtengine::RI_ABSOLUTE: - prtIntent->set_active (2); + prtIntent->set_active(2); break; } - prtBPC->set_active (moptions.rtSettings.printerBPC); + prtBPC->set_active(moptions.rtSettings.printerBPC); #if !defined(__APPLE__) // monitor profile not supported on apple - setActiveTextOrIndex (*monProfile, moptions.rtSettings.monitorProfile, 0); + setActiveTextOrIndex(*monProfile, moptions.rtSettings.monitorProfile, 0); switch (moptions.rtSettings.monitorIntent) { default: case rtengine::RI_PERCEPTUAL: - monIntent->set_active (0); + monIntent->set_active(0); break; case rtengine::RI_RELATIVE: - monIntent->set_active (1); + monIntent->set_active(1); break; case rtengine::RI_ABSOLUTE: - monIntent->set_active (2); + monIntent->set_active(2); break; } - monBPC->set_active (moptions.rtSettings.monitorBPC); - cbAutoMonProfile->set_active (moptions.rtSettings.autoMonitorProfile); + monBPC->set_active(moptions.rtSettings.monitorBPC); + cbAutoMonProfile->set_active(moptions.rtSettings.autoMonitorProfile); #endif - if (Glib::file_test (moptions.rtSettings.iccDirectory, Glib::FILE_TEST_IS_DIR)) { - iccDir->set_current_folder (moptions.rtSettings.iccDirectory); + if (Glib::file_test(moptions.rtSettings.iccDirectory, Glib::FILE_TEST_IS_DIR)) { + iccDir->set_current_folder(moptions.rtSettings.iccDirectory); } cprevdemo->set_active (moptions.prevdemo); - - languages->set_active_text (moptions.language); - ckbLangAutoDetect->set_active (moptions.languageAutoDetect); - int themeNbr = getThemeRowNumber (moptions.theme); + languages->set_active_text(moptions.language); + ckbLangAutoDetect->set_active(moptions.languageAutoDetect); + int themeNbr = getThemeRowNumber(moptions.theme); themeCBT->set_active (themeNbr == -1 ? 0 : themeNbr); Gdk::RGBA cropCol; - cropCol.set_rgba (moptions.cutOverlayBrush[0], moptions.cutOverlayBrush[1], moptions.cutOverlayBrush[2]); + cropCol.set_rgba(moptions.cutOverlayBrush[0], moptions.cutOverlayBrush[1], moptions.cutOverlayBrush[2]); cropMaskColorCB->set_rgba (cropCol); cropMaskColorCB->set_alpha ( (unsigned short) (moptions.cutOverlayBrush[3] * 65535.0)); Gdk::RGBA NavGuideCol; - NavGuideCol.set_rgba (moptions.navGuideBrush[0], moptions.navGuideBrush[1], moptions.navGuideBrush[2]); + NavGuideCol.set_rgba(moptions.navGuideBrush[0], moptions.navGuideBrush[1], moptions.navGuideBrush[2]); navGuideColorCB->set_rgba (NavGuideCol); navGuideColorCB->set_alpha ( (unsigned short) (moptions.navGuideBrush[3] * 65535.0)); @@ -1919,63 +1943,63 @@ void Preferences::fillPreferences () pseudoHiDPI->set_active(options.pseudoHiDPISupport); - showDateTime->set_active (moptions.fbShowDateTime); - showBasicExif->set_active (moptions.fbShowBasicExif); - showExpComp->set_active (moptions.fbShowExpComp); - ckbmenuGroupRank->set_active (moptions.menuGroupRank); - ckbmenuGroupLabel->set_active (moptions.menuGroupLabel); - ckbmenuGroupFileOperations->set_active (moptions.menuGroupFileOperations); - ckbmenuGroupProfileOperations->set_active (moptions.menuGroupProfileOperations); - ckbmenuGroupExtProg->set_active (moptions.menuGroupExtProg); + showDateTime->set_active(moptions.fbShowDateTime); + showBasicExif->set_active(moptions.fbShowBasicExif); + showExpComp->set_active(moptions.fbShowExpComp); + ckbmenuGroupRank->set_active(moptions.menuGroupRank); + ckbmenuGroupLabel->set_active(moptions.menuGroupLabel); + ckbmenuGroupFileOperations->set_active(moptions.menuGroupFileOperations); + ckbmenuGroupProfileOperations->set_active(moptions.menuGroupProfileOperations); + ckbmenuGroupExtProg->set_active(moptions.menuGroupExtProg); - hlThresh->set_value (moptions.highlightThreshold); - shThresh->set_value (moptions.shadowThreshold); + hlThresh->set_value(moptions.highlightThreshold); + shThresh->set_value(moptions.shadowThreshold); - edGimp->set_active (moptions.editorToSendTo == 1); - edOther->set_active (moptions.editorToSendTo == 3); + edGimp->set_active(moptions.editorToSendTo == 1); + edOther->set_active(moptions.editorToSendTo == 3); #ifdef WIN32 - edPS->set_active (moptions.editorToSendTo == 2); + edPS->set_active(moptions.editorToSendTo == 2); - if (Glib::file_test (moptions.gimpDir, Glib::FILE_TEST_IS_DIR)) { - gimpDir->set_current_folder (moptions.gimpDir); + if (Glib::file_test(moptions.gimpDir, Glib::FILE_TEST_IS_DIR)) { + gimpDir->set_current_folder(moptions.gimpDir); } else { - gimpDir->set_current_folder (Glib::get_home_dir()); + gimpDir->set_current_folder(Glib::get_home_dir()); } - if (Glib::file_test (moptions.psDir, Glib::FILE_TEST_IS_DIR)) { - psDir->set_current_folder (moptions.psDir); + if (Glib::file_test(moptions.psDir, Glib::FILE_TEST_IS_DIR)) { + psDir->set_current_folder(moptions.psDir); } else { - psDir->set_current_folder (Glib::get_home_dir()); + psDir->set_current_folder(Glib::get_home_dir()); } #elif defined __APPLE__ - edPS->set_active (moptions.editorToSendTo == 2); + edPS->set_active(moptions.editorToSendTo == 2); - if (Glib::file_test (moptions.psDir, Glib::FILE_TEST_IS_DIR)) { - psDir->set_current_folder (moptions.psDir); + if (Glib::file_test(moptions.psDir, Glib::FILE_TEST_IS_DIR)) { + psDir->set_current_folder(moptions.psDir); } else { - psDir->set_current_folder (Glib::get_home_dir()); + psDir->set_current_folder(Glib::get_home_dir()); } #endif - editorToSendTo->set_text (moptions.customEditorProg); + editorToSendTo->set_text(moptions.customEditorProg); - txtCustProfBuilderPath->set_text (moptions.CPBPath); - custProfBuilderLabelType->set_active (moptions.CPBKeys); + txtCustProfBuilderPath->set_text(moptions.CPBPath); + custProfBuilderLabelType->set_active(moptions.CPBKeys); if (moptions.startupDir == STARTUPDIR_CURRENT) { - sdcurrent->set_active (); + sdcurrent->set_active(); } else if (moptions.startupDir == STARTUPDIR_LAST) { - sdlast->set_active (); + sdlast->set_active(); } else if (moptions.startupDir == STARTUPDIR_HOME) { - sdhome->set_active (); + sdhome->set_active(); } else if (moptions.startupDir == STARTUPDIR_CUSTOM) { - sdother->set_active (); - startupdir->set_text (moptions.startupPath); + sdother->set_active(); + startupdir->set_text(moptions.startupPath); } - extensionModel->clear (); + extensionModel->clear(); for (size_t i = 0; i < moptions.parseExtensions.size(); i++) { Gtk::TreeRow row = * (extensionModel->append()); @@ -1983,32 +2007,34 @@ void Preferences::fillPreferences () row[extensionColumns.ext] = moptions.parseExtensions[i]; } - maxRecentFolders->set_value (moptions.maxRecentFolders); + maxRecentFolders->set_value(moptions.maxRecentFolders); maxThumbHeightSB->set_value (moptions.maxThumbnailHeight); maxCacheEntriesSB->set_value (moptions.maxCacheEntries); - overlayedFileNames->set_active (moptions.overlayedFileNames); - filmStripOverlayedFileNames->set_active (moptions.filmStripOverlayedFileNames); - sameThumbSize->set_active (moptions.sameThumbSize); - ckbInternalThumbIfUntouched->set_active (moptions.internalThumbIfUntouched); + overlayedFileNames->set_active(moptions.overlayedFileNames); + filmStripOverlayedFileNames->set_active(moptions.filmStripOverlayedFileNames); + sameThumbSize->set_active(moptions.sameThumbSize); + ckbInternalThumbIfUntouched->set_active(moptions.internalThumbIfUntouched); - saveParamsPreference->set_active (moptions.saveParamsFile ? (moptions.saveParamsCache ? 2 : 0) : 1); + saveParamsPreference->set_active(moptions.saveParamsFile ? (moptions.saveParamsCache ? 2 : 0) : 1); - loadParamsPreference->set_active (moptions.paramsLoadLocation); - useBundledProfiles->set_active (moptions.useBundledProfiles); + loadParamsPreference->set_active(moptions.paramsLoadLocation); + useBundledProfiles->set_active(moptions.useBundledProfiles); if (!moptions.tabbedUI) { - editorLayout->set_active (moptions.mainNBVertical ? 1 : 0); + editorLayout->set_active(moptions.mainNBVertical ? 1 : 0); } else { - editorLayout->set_active (moptions.multiDisplayMode ? 3 : 2); + editorLayout->set_active(moptions.multiDisplayMode ? 3 : 2); } - curveBBoxPosC->set_active (moptions.curvebboxpos); - ckbHistogramPositionLeft->set_active (moptions.histogramPosition == 1); - ckbFileBrowserToolbarSingleRow->set_active (moptions.FileBrowserToolbarSingleRow); - ckbShowFilmStripToolBar->set_active (moptions.showFilmStripToolBar); - ckbHideTPVScrollbar->set_active (moptions.hideTPVScrollbar); + curveBBoxPosC->set_active(moptions.curvebboxpos); + complexitylocal->set_active(moptions.complexity); - ckbAutoSaveTpOpen->set_active (moptions.autoSaveTpOpen); + ckbHistogramPositionLeft->set_active(moptions.histogramPosition == 1); + ckbFileBrowserToolbarSingleRow->set_active(moptions.FileBrowserToolbarSingleRow); + ckbShowFilmStripToolBar->set_active(moptions.showFilmStripToolBar); + ckbHideTPVScrollbar->set_active(moptions.hideTPVScrollbar); + ckbshowtooltiplocallab->set_active(moptions.showtooltip); + ckbAutoSaveTpOpen->set_active(moptions.autoSaveTpOpen); threadsSpinBtn->set_value (moptions.rgbDenoiseThreadLimit); clutCacheSizeSB->set_value (moptions.clutCacheSize); @@ -2021,50 +2047,50 @@ void Preferences::fillPreferences () maxInspectorBuffersSB->set_value (moptions.maxInspectorBuffers); thumbnailInspectorMode->set_active(int(moptions.rtSettings.thumbnail_inspector_mode)); - darkFrameDir->set_current_folder ( moptions.rtSettings.darkFramesPath ); - darkFrameChanged (); + darkFrameDir->set_current_folder(moptions.rtSettings.darkFramesPath); + darkFrameChanged(); - flatFieldDir->set_current_folder ( moptions.rtSettings.flatFieldsPath ); - flatFieldChanged (); + flatFieldDir->set_current_folder(moptions.rtSettings.flatFieldsPath); + flatFieldChanged(); - clutsDir->set_current_folder ( moptions.clutsDir ); + clutsDir->set_current_folder(moptions.clutsDir); - addc.block (true); - setc.block (true); + addc.block(true); + setc.block(true); - moptions.baBehav.resize (ADDSET_PARAM_NUM); + moptions.baBehav.resize(ADDSET_PARAM_NUM); for (Gtk::TreeIter sections = behModel->children().begin(); sections != behModel->children().end(); ++sections) { for (Gtk::TreeIter adjs = sections->children().begin(); adjs != sections->children().end(); ++adjs) { const bool add = moptions.baBehav[adjs->get_value(behavColumns.addsetid)]; - adjs->set_value (behavColumns.badd, add); - adjs->set_value (behavColumns.bset, !add); + adjs->set_value(behavColumns.badd, add); + adjs->set_value(behavColumns.bset, !add); } } cropGuidesCombo->set_active(moptions.cropGuides); cropAutoFitCB->set_active(moptions.cropAutoFit); - addc.block (false); - setc.block (false); - cpfconn.block (false); - fconn.block (false); - tconn.block (false); - sconn.block (false); - dfconn.block (false); - ffconn.block (false); - rpconn.block (true); - ipconn.block (true); - bpconn.block (false); + addc.block(false); + setc.block(false); + cpfconn.block(false); + fconn.block(false); + tconn.block(false); + sconn.block(false); + dfconn.block(false); + ffconn.block(false); + rpconn.block(true); + ipconn.block(true); + bpconn.block(false); - chOverwriteOutputFile->set_active (moptions.overwriteOutputFile); + chOverwriteOutputFile->set_active(moptions.overwriteOutputFile); // Sounds only on Windows and Linux #if defined(WIN32) || defined(__linux__) - ckbSndEnable->set_active (moptions.sndEnable); - txtSndBatchQueueDone->set_text (moptions.sndBatchQueueDone); - txtSndLngEditProcDone->set_text (moptions.sndLngEditProcDone); - spbSndLngEditProcDoneSecs->set_value (moptions.sndLngEditProcDoneSecs); + ckbSndEnable->set_active(moptions.sndEnable); + txtSndBatchQueueDone->set_text(moptions.sndBatchQueueDone); + txtSndLngEditProcDone->set_text(moptions.sndLngEditProcDone); + spbSndLngEditProcDoneSecs->set_value(moptions.sndLngEditProcDoneSecs); #endif } @@ -2083,9 +2109,9 @@ void Preferences::savePressed () { } */ -void Preferences::autoMonProfileToggled () +void Preferences::autoMonProfileToggled() { - monProfile->set_sensitive (!cbAutoMonProfile->get_active()); + monProfile->set_sensitive(!cbAutoMonProfile->get_active()); } /* @@ -2094,43 +2120,43 @@ void Preferences::autocielabToggled () { } */ -void Preferences::sndEnableToggled () +void Preferences::sndEnableToggled() { - txtSndBatchQueueDone->set_sensitive (ckbSndEnable->get_active()); - txtSndLngEditProcDone->set_sensitive (ckbSndEnable->get_active()); - spbSndLngEditProcDoneSecs->set_sensitive (ckbSndEnable->get_active()); + txtSndBatchQueueDone->set_sensitive(ckbSndEnable->get_active()); + txtSndLngEditProcDone->set_sensitive(ckbSndEnable->get_active()); + spbSndLngEditProcDoneSecs->set_sensitive(ckbSndEnable->get_active()); } -void Preferences::langAutoDetectToggled () +void Preferences::langAutoDetectToggled() { - languages->set_sensitive (!ckbLangAutoDetect->get_active()); + languages->set_sensitive(!ckbLangAutoDetect->get_active()); } -void Preferences::okPressed () +void Preferences::okPressed() { - storePreferences (); + storePreferences(); workflowUpdate(); - options.copyFrom (&moptions); + options.copyFrom(&moptions); options.filterOutParsedExtensions(); try { - Options::save (); + Options::save(); } catch (Options::Error &e) { - Gtk::MessageDialog msgd (getToplevelWindow (this), e.get_msg(), true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true); + Gtk::MessageDialog msgd(getToplevelWindow(this), e.get_msg(), true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_CLOSE, true); msgd.run(); } dynProfilePanel->save(); - hide (); + hide(); } -void Preferences::cancelPressed () +void Preferences::cancelPressed() { // set the initial theme back if (themeFNames.at (themeCBT->get_active_row_number ()).longFName != options.theme) { RTImage::updateImages(); - switchThemeTo (options.theme); + switchThemeTo(options.theme); } // set the initial font back @@ -2138,59 +2164,59 @@ void Preferences::cancelPressed () if (fd.get_family() != options.fontFamily && (fd.get_size() / Pango::SCALE) != options.fontSize) { if (options.fontFamily == "default") { - switchFontTo (initialFontFamily, initialFontSize); + switchFontTo(initialFontFamily, initialFontSize); } else { - switchFontTo (options.fontFamily, options.fontSize); + switchFontTo(options.fontFamily, options.fontSize); } } // update the profileStore - if (useBundledProfiles->get_active () != options.useBundledProfiles) { + if (useBundledProfiles->get_active() != options.useBundledProfiles) { // we have to rescan with the old value - bpconn.block (true); - useBundledProfiles->set_active (false); + bpconn.block(true); + useBundledProfiles->set_active(false); bundledProfilesChanged(); - bpconn.block (false); + bpconn.block(false); } - hide (); + hide(); } -void Preferences::selectStartupDir () +void Preferences::selectStartupDir() { - Gtk::FileChooserDialog dialog (getToplevelWindow (this), M ("PREFERENCES_DIRSELECTDLG"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); + Gtk::FileChooserDialog dialog(getToplevelWindow(this), M("PREFERENCES_DIRSELECTDLG"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); //dialog.set_transient_for(*this); //Add response buttons to the dialog: - dialog.add_button (M ("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL); - dialog.add_button (M ("GENERAL_OPEN"), Gtk::RESPONSE_OK); + dialog.add_button(M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL); + dialog.add_button(M("GENERAL_OPEN"), Gtk::RESPONSE_OK); int result = dialog.run(); if (result == Gtk::RESPONSE_OK) { - startupdir->set_text (dialog.get_filename()); + startupdir->set_text(dialog.get_filename()); } } -void Preferences::aboutPressed () +void Preferences::aboutPressed() { - splash = new Splash (*this); - splash->set_transient_for (*this); - splash->signal_delete_event().connect ( sigc::mem_fun (*this, &Preferences::splashClosed) ); - splash->show (); + splash = new Splash(*this); + splash->set_transient_for(*this); + splash->signal_delete_event().connect(sigc::mem_fun(*this, &Preferences::splashClosed)); + splash->show(); } -void Preferences::themeChanged () +void Preferences::themeChanged() { moptions.theme = themeFNames.at (themeCBT->get_active_row_number ()).longFName; RTImage::updateImages(); - switchThemeTo (moptions.theme); + switchThemeTo(moptions.theme); } -void Preferences::forRAWComboChanged () +void Preferences::forRAWComboChanged() { if (!rprofiles) { return; @@ -2203,17 +2229,17 @@ void Preferences::forRAWComboChanged () } if (selectedEntry->type == PSET_FOLDER) { - rpconn.block (true); - rprofiles->set_active (currRawRow); - rpconn.block (false); + rpconn.block(true); + rprofiles->set_active(currRawRow); + rpconn.block(false); } else { currRawRow = rprofiles->get_active(); } - rprofiles->set_tooltip_text (selectedEntry->label); + rprofiles->set_tooltip_text(selectedEntry->label); } -void Preferences::forImageComboChanged () +void Preferences::forImageComboChanged() { if (!iprofiles) { return; @@ -2226,29 +2252,29 @@ void Preferences::forImageComboChanged () } if (selectedEntry->type == PSET_FOLDER) { - ipconn.block (true); - iprofiles->set_active (currImgRow); - ipconn.block (false); + ipconn.block(true); + iprofiles->set_active(currImgRow); + ipconn.block(false); } else { currImgRow = rprofiles->get_active(); } - iprofiles->set_tooltip_text (iprofiles->getSelectedEntry()->label); + iprofiles->set_tooltip_text(iprofiles->getSelectedEntry()->label); } -void Preferences::layoutComboChanged () +void Preferences::layoutComboChanged() { - editorLayout->set_tooltip_text (editorLayout->get_active_text()); + editorLayout->set_tooltip_text(editorLayout->get_active_text()); } -void Preferences::bundledProfilesChanged () +void Preferences::bundledProfilesChanged() { - rpconn.block (true); - ipconn.block (true); + rpconn.block(true); + ipconn.block(true); // parseProfiles does use options.useBundledProfiles, so we temporarily change its value bool currValue = options.useBundledProfiles; - options.useBundledProfiles = useBundledProfiles->get_active (); + options.useBundledProfiles = useBundledProfiles->get_active(); // rescan the file's tree ProfileStore::getInstance()->parseProfiles(); // This will call Preferences::updateProfileList in return @@ -2256,24 +2282,24 @@ void Preferences::bundledProfilesChanged () // restoring back the old value options.useBundledProfiles = currValue; - ipconn.block (false); - rpconn.block (false); + ipconn.block(false); + rpconn.block(false); } -void Preferences::iccDirChanged () +void Preferences::iccDirChanged() { - const auto currentSelection = monProfile->get_active_text (); - const auto profiles = rtengine::ICCStore::getInstance ()->getProfilesFromDir (iccDir->get_filename ()); + const auto currentSelection = monProfile->get_active_text(); + const auto profiles = rtengine::ICCStore::getInstance()->getProfilesFromDir(iccDir->get_filename()); monProfile->remove_all(); - monProfile->append (M ("PREFERENCES_PROFILE_NONE")); + monProfile->append(M("PREFERENCES_PROFILE_NONE")); for (const auto& profile : profiles) { - monProfile->append (profile); + monProfile->append(profile); } - setActiveTextOrIndex (*monProfile, currentSelection, 0); + setActiveTextOrIndex(*monProfile, currentSelection, 0); } void Preferences::storeCurrentValue() @@ -2288,26 +2314,26 @@ void Preferences::updateProfileList() rprofiles->updateProfileList(); iprofiles->updateProfileList(); const ProfileStoreEntry* dynpse = ProfileStore::getInstance()->getInternalDynamicPSE(); - rprofiles->addRow (dynpse); - iprofiles->addRow (dynpse); + rprofiles->addRow(dynpse); + iprofiles->addRow(dynpse); } void Preferences::restoreValue() { - if (!rprofiles->setActiveRowFromFullPath (storedValueRaw)) { + if (!rprofiles->setActiveRowFromFullPath(storedValueRaw)) { moptions.defProfRaw = DEFPROFILE_INTERNAL; - rpconn.block (true); + rpconn.block(true); rprofiles->setInternalEntry(); - rpconn.block (false); + rpconn.block(false); } currRawRow = rprofiles->get_active(); - if (!iprofiles->setActiveRowFromFullPath (storedValueImg)) { + if (!iprofiles->setActiveRowFromFullPath(storedValueImg)) { moptions.defProfImg = DEFPROFILE_INTERNAL; - ipconn.block (true); + ipconn.block(true); iprofiles->setInternalEntry(); - ipconn.block (false); + ipconn.block(false); } currImgRow = iprofiles->get_active(); @@ -2316,48 +2342,47 @@ void Preferences::restoreValue() storedValueImg = ""; } -void Preferences::switchThemeTo (Glib::ustring newTheme) +void Preferences::switchThemeTo(Glib::ustring newTheme) { - Glib::ustring filename (Glib::build_filename (argv0, "themes", newTheme + ".css")); + Glib::ustring filename(Glib::build_filename(argv0, "themes", newTheme + ".css")); if (!themecss) { themecss = Gtk::CssProvider::create(); Glib::RefPtr screen = Gdk::Screen::get_default(); - Gtk::StyleContext::add_provider_for_screen (screen, themecss, GTK_STYLE_PROVIDER_PRIORITY_USER); + Gtk::StyleContext::add_provider_for_screen(screen, themecss, GTK_STYLE_PROVIDER_PRIORITY_USER); } try { - themecss->load_from_path (filename); + themecss->load_from_path(filename); } catch (Glib::Error &err) { - printf ("Error: Can't load css file \"%s\"\nMessage: %s\n", filename.c_str(), err.what().c_str()); + printf("Error: Can't load css file \"%s\"\nMessage: %s\n", filename.c_str(), err.what().c_str()); } catch (...) { - printf ("Error: Can't load css file \"%s\"\n", filename.c_str()); + printf("Error: Can't load css file \"%s\"\n", filename.c_str()); } } -void Preferences::fontChanged () +void Preferences::fontChanged() { - newFont = true; Pango::FontDescription fd (mainFontFB->get_font_name()); - switchFontTo (fd.get_family(), fd.get_size() / Pango::SCALE); + switchFontTo(fd.get_family(), fd.get_size() / Pango::SCALE); } -void Preferences::cpFontChanged () +void Preferences::cpFontChanged() { newCPFont = true; } -void Preferences::switchFontTo (const Glib::ustring &newFontFamily, const int newFontSize) +void Preferences::switchFontTo(const Glib::ustring &newFontFamily, const int newFontSize) { if (newFontFamily != "default") { if (!fontcss) { fontcss = Gtk::CssProvider::create(); Glib::RefPtr screen = Gdk::Screen::get_default(); - Gtk::StyleContext::add_provider_for_screen (screen, fontcss, GTK_STYLE_PROVIDER_PRIORITY_USER); + Gtk::StyleContext::add_provider_for_screen(screen, fontcss, GTK_STYLE_PROVIDER_PRIORITY_USER); } try { @@ -2369,47 +2394,52 @@ void Preferences::switchFontTo (const Glib::ustring &newFontFamily, const int ne //#endif //GTK318 } catch (Glib::Error &err) { - printf ("Error: \"%s\"\n", err.what().c_str()); + printf("Error: \"%s\"\n", err.what().c_str()); } catch (...) { - printf ("Error: Can't find the font named \"%s\"\n", newFontFamily.c_str()); + printf("Error: Can't find the font named \"%s\"\n", newFontFamily.c_str()); } } else { if (fontcss) { fontcss = Gtk::CssProvider::create(); Glib::RefPtr screen = Gdk::Screen::get_default(); - Gtk::StyleContext::remove_provider_for_screen (screen, fontcss); + Gtk::StyleContext::remove_provider_for_screen(screen, fontcss); } } } -void Preferences::workflowUpdate () +void Preferences::workflowUpdate() { if (moptions.tabbedUI != options.tabbedUI) { - parent->setEditorMode (moptions.tabbedUI); + parent->setEditorMode(moptions.tabbedUI); } if (moptions.hideTPVScrollbar != options.hideTPVScrollbar) { // Update the tool panels - parent->updateTPVScrollbar (moptions.hideTPVScrollbar); + parent->updateTPVScrollbar(moptions.hideTPVScrollbar); } if (moptions.FileBrowserToolbarSingleRow != options.FileBrowserToolbarSingleRow) { // Update the position of the Query toolbar - parent->updateFBQueryTB (moptions.FileBrowserToolbarSingleRow); + parent->updateFBQueryTB(moptions.FileBrowserToolbarSingleRow); } if (moptions.showFilmStripToolBar != options.showFilmStripToolBar) { // Update the visibility of FB toolbar - parent->updateFBToolBarVisibility (moptions.showFilmStripToolBar); + parent->updateFBToolBarVisibility(moptions.showFilmStripToolBar); + } + + if (moptions.showtooltip != options.showtooltip) { + // Update the visibility of tooltip + parent->updateShowtooltipVisibility(moptions.showtooltip); } if (moptions.histogramPosition != options.histogramPosition) { // Update the position of the Histogram - parent->updateHistogramPosition (options.histogramPosition, moptions.histogramPosition); + parent->updateHistogramPosition(options.histogramPosition, moptions.histogramPosition); } - if ( moptions.rtSettings.printerProfile != options.rtSettings.printerProfile + if (moptions.rtSettings.printerProfile != options.rtSettings.printerProfile || moptions.rtSettings.printerBPC != options.rtSettings.printerBPC || moptions.rtSettings.printerIntent != options.rtSettings.printerIntent) { // Update the position of the Histogram @@ -2418,56 +2448,56 @@ void Preferences::workflowUpdate () } -void Preferences::addExtPressed () +void Preferences::addExtPressed() { - Gtk::TreeNodeChildren c = extensionModel->children (); + Gtk::TreeNodeChildren c = extensionModel->children(); for (size_t i = 0; i < c.size(); i++) - if (c[i][extensionColumns.ext] == extension->get_text ()) { + if (c[i][extensionColumns.ext] == extension->get_text()) { return; } Gtk::TreeRow row = * (extensionModel->append()); row[extensionColumns.enabled] = true; - row[extensionColumns.ext] = extension->get_text (); + row[extensionColumns.ext] = extension->get_text(); } -void Preferences::delExtPressed () +void Preferences::delExtPressed() { - extensionModel->erase (extensions->get_selection()->get_selected ()); + extensionModel->erase(extensions->get_selection()->get_selected()); } -void Preferences::moveExtUpPressed () +void Preferences::moveExtUpPressed() { - const Glib::RefPtr selection = extensions->get_selection (); + const Glib::RefPtr selection = extensions->get_selection(); if (!selection) { return; } - const Gtk::TreeModel::iterator selected = selection->get_selected (); + const Gtk::TreeModel::iterator selected = selection->get_selected(); - if (!selected || selected == extensionModel->children ().begin ()) { + if (!selected || selected == extensionModel->children().begin()) { return; } Gtk::TreeModel::iterator previous = selected; --previous; - extensionModel->iter_swap (selected, previous); + extensionModel->iter_swap(selected, previous); } -void Preferences::moveExtDownPressed () +void Preferences::moveExtDownPressed() { - const Glib::RefPtr selection = extensions->get_selection (); + const Glib::RefPtr selection = extensions->get_selection(); if (!selection) { return; } - const Gtk::TreeModel::iterator selected = selection->get_selected (); + const Gtk::TreeModel::iterator selected = selection->get_selected(); if (!selected) { return; @@ -2476,44 +2506,45 @@ void Preferences::moveExtDownPressed () Gtk::TreeModel::iterator next = selected; if (++next) { - extensionModel->iter_swap (selected, next); + extensionModel->iter_swap(selected, next); } } -void Preferences::clearProfilesPressed () +void Preferences::clearProfilesPressed() { - cacheMgr->clearProfiles (); + cacheMgr->clearProfiles(); } -void Preferences::clearThumbImagesPressed () + +void Preferences::clearThumbImagesPressed() { - cacheMgr->clearImages (); + cacheMgr->clearImages(); } -void Preferences::clearAllPressed () +void Preferences::clearAllPressed() { - cacheMgr->clearAll (); + cacheMgr->clearAll(); } -void Preferences::darkFrameChanged () +void Preferences::darkFrameChanged() { //Glib::ustring s(darkFrameDir->get_filename()); - Glib::ustring s (darkFrameDir->get_current_folder()); + Glib::ustring s(darkFrameDir->get_current_folder()); //if( s.compare( rtengine::dfm.getPathname()) !=0 ){ - rtengine::dfm.init ( s ); + rtengine::dfm.init(s); updateDFinfos(); //} } -void Preferences::flatFieldChanged () +void Preferences::flatFieldChanged() { //Glib::ustring s(flatFieldDir->get_filename()); - Glib::ustring s (flatFieldDir->get_current_folder()); + Glib::ustring s(flatFieldDir->get_current_folder()); //if( s.compare( rtengine::ffm.getPathname()) !=0 ){ - rtengine::ffm.init ( s ); + rtengine::ffm.init(s); updateFFinfos(); //} } @@ -2521,29 +2552,30 @@ void Preferences::flatFieldChanged () void Preferences::updateDFinfos() { int t1, t2; - rtengine::dfm.getStat (t1, t2); - Glib::ustring s = Glib::ustring::compose ("%1: %2 %3, %4 %5", M ("PREFERENCES_DARKFRAMEFOUND"), t1, M ("PREFERENCES_DARKFRAMESHOTS"), t2, M ("PREFERENCES_DARKFRAMETEMPLATES")); - dfLabel->set_text (s); + rtengine::dfm.getStat(t1, t2); + Glib::ustring s = Glib::ustring::compose("%1: %2 %3, %4 %5", M("PREFERENCES_DARKFRAMEFOUND"), t1, M("PREFERENCES_DARKFRAMESHOTS"), t2, M("PREFERENCES_DARKFRAMETEMPLATES")); + dfLabel->set_text(s); } void Preferences::updateFFinfos() { int t1, t2; - rtengine::ffm.getStat (t1, t2); - Glib::ustring s = Glib::ustring::compose ("%1: %2 %3, %4 %5", M ("PREFERENCES_FLATFIELDFOUND"), t1, M ("PREFERENCES_FLATFIELDSHOTS"), t2, M ("PREFERENCES_FLATFIELDTEMPLATES")); - ffLabel->set_text (s); + rtengine::ffm.getStat(t1, t2); + Glib::ustring s = Glib::ustring::compose("%1: %2 %3, %4 %5", M("PREFERENCES_FLATFIELDFOUND"), t1, M("PREFERENCES_FLATFIELDSHOTS"), t2, M("PREFERENCES_FLATFIELDTEMPLATES")); + ffLabel->set_text(s); } -bool Preferences::splashClosed (GdkEventAny* event) +bool Preferences::splashClosed(GdkEventAny* event) { delete splash; splash = nullptr; return true; } -void Preferences::behAddSetAllPressed (bool add) +void Preferences::behAddSetAllPressed(bool add) { moptions.baBehav.assign(ADDSET_PARAM_NUM, add); + for (Gtk::TreeIter sections = behModel->children().begin(); sections != behModel->children().end(); ++sections) { for (Gtk::TreeIter adjs = sections->children().begin(); adjs != sections->children().end(); ++adjs) { adjs->set_value(behavColumns.badd, add); @@ -2552,12 +2584,12 @@ void Preferences::behAddSetAllPressed (bool add) } } -void Preferences::behAddAllPressed () +void Preferences::behAddAllPressed() { behAddSetAllPressed(true); } -void Preferences::behSetAllPressed () +void Preferences::behSetAllPressed() { behAddSetAllPressed(false); } diff --git a/rtgui/preferences.h b/rtgui/preferences.h index a6cbe7939..3cf6b2043 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -146,6 +146,8 @@ class Preferences final : Gtk::CheckButton* ctiffserialize; Gtk::ComboBoxText* curveBBoxPosC; + Gtk::ComboBoxText* complexitylocal; + Gtk::ComboBoxText* themeCBT; Gtk::FontButton* mainFontFB; Gtk::FontButton* colorPickerFontFB; @@ -208,6 +210,7 @@ class Preferences final : Gtk::CheckButton* ckbFileBrowserToolbarSingleRow; Gtk::CheckButton* ckbShowFilmStripToolBar; Gtk::CheckButton* ckbHideTPVScrollbar; + Gtk::CheckButton* ckbshowtooltiplocallab; Gtk::CheckButton* ckbAutoSaveTpOpen; Gtk::Button* btnSaveTpOpenNow; diff --git a/rtgui/profilepanel.cc b/rtgui/profilepanel.cc index 4c8e30e44..c7e889b3f 100644 --- a/rtgui/profilepanel.cc +++ b/rtgui/profilepanel.cc @@ -273,11 +273,21 @@ void ProfilePanel::restoreValue () void ProfilePanel::save_clicked (GdkEventButton* event) { - if (event->button != 1) { return; } + const PartialProfile* toSave; + + if (isCustomSelected()) { + toSave = custom; + } else if (isLastSavedSelected()) { + toSave = lastsaved; + } else { + const auto entry = profiles->getSelectedEntry(); + toSave = entry ? ProfileStore::getInstance()->getProfile(entry) : nullptr; + } + // If it's a partial profile, it's more intuitive to first allow the user // to choose which parameters to save before showing the Save As dialog // #5491 @@ -288,6 +298,7 @@ void ProfilePanel::save_clicked (GdkEventButton* event) } partialProfileDlg->set_title(M("PROFILEPANEL_SAVEPPASTE")); + partialProfileDlg->updateSpotWidget(toSave->pparams); const auto response = partialProfileDlg->run(); partialProfileDlg->hide(); @@ -342,19 +353,9 @@ void ProfilePanel::save_clicked (GdkEventButton* event) lastFilename = Glib::path_get_basename(fname); - const PartialProfile* toSave; - - if (isCustomSelected()) { - toSave = custom; - } else if (isLastSavedSelected()) { - toSave = lastsaved; - } else { - const auto entry = profiles->getSelectedEntry(); - toSave = entry ? ProfileStore::getInstance()->getProfile(entry) : nullptr; - } - if (toSave) { int retCode; + if (isPartial) { // Build partial profile PartialProfile ppTemp(true); @@ -410,6 +411,7 @@ void ProfilePanel::copy_clicked (GdkEventButton* event) partialProfileDlg = new PartialPasteDlg (Glib::ustring (), parent); } partialProfileDlg->set_title(M("PROFILEPANEL_COPYPPASTE")); + partialProfileDlg->updateSpotWidget(toSave->pparams); int i = partialProfileDlg->run(); partialProfileDlg->hide(); @@ -473,20 +475,7 @@ void ProfilePanel::load_clicked (GdkEventButton* event) if (result == Gtk::RESPONSE_OK) { Glib::ustring fname = dialog.get_filename(); - - if (event->state & Gdk::CONTROL_MASK) { - // opening the partial paste dialog window - if(!partialProfileDlg) { - partialProfileDlg = new PartialPasteDlg (Glib::ustring (), parent); - } - partialProfileDlg->set_title(M("PROFILEPANEL_LOADPPASTE")); - int i = partialProfileDlg->run(); - partialProfileDlg->hide(); - - if (i != Gtk::RESPONSE_OK) { - return; - } - } + printf("fname=%s\n", fname.c_str()); bool customCreated = false; @@ -502,6 +491,9 @@ void ProfilePanel::load_clicked (GdkEventButton* event) if (!err) { if (!customCreated && fillMode->get_active()) { custom->pparams->setDefaults(); + + // Clearing all LocallabSpotEdited to be compliant with default pparams + custom->pedited->locallab.spots.clear(); } custom->set(true); @@ -521,6 +513,17 @@ void ProfilePanel::load_clicked (GdkEventButton* event) if(!partialProfileDlg) { partialProfileDlg = new PartialPasteDlg (Glib::ustring (), parent); } + + // opening the partial paste dialog window + partialProfileDlg->set_title(M("PROFILEPANEL_LOADPPASTE")); + partialProfileDlg->updateSpotWidget(&pp); + int i = partialProfileDlg->run(); + partialProfileDlg->hide(); + + if (i != Gtk::RESPONSE_OK) { + return; + } + partialProfileDlg->applyPaste (custom->pparams, !fillMode->get_active() ? custom->pedited : nullptr, &pp, &pe); } else { // custom.pparams = loadedFile.pparams filtered by ( loadedFile.pedited ) @@ -557,32 +560,27 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) return; } - if (event->state & Gdk::CONTROL_MASK) { - if(!partialProfileDlg) { - partialProfileDlg = new PartialPasteDlg (Glib::ustring (), parent); - } - partialProfileDlg->set_title(M("PROFILEPANEL_PASTEPPASTE")); - int i = partialProfileDlg->run(); - partialProfileDlg->hide(); - - if (i != Gtk::RESPONSE_OK) { - return; - } - } - bool prevState = changeconn.block(true); if (!custom) { - custom = new PartialProfile (true); + custom = new PartialProfile (true); // custom pedited is initialized to false if (isLastSavedSelected()) { *custom->pparams = *lastsaved->pparams; + + // Setting LocallabSpotEdited number coherent with spots number in lastsaved->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(false)); } else { const ProfileStoreEntry* entry = profiles->getSelectedEntry(); if (entry) { const PartialProfile* partProfile = ProfileStore::getInstance()->getProfile (entry); *custom->pparams = *partProfile->pparams; + + // Setting LocallabSpotEdited number coherent with spots number in partProfile->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(false)); } } @@ -591,15 +589,26 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) } else { if (fillMode->get_active()) { custom->pparams->setDefaults(); + + // Clear all LocallabSpotEdited to be compliant with default pparams + custom->pedited->locallab.spots.clear(); } else if (!isCustomSelected ()) { if (isLastSavedSelected()) { *custom->pparams = *lastsaved->pparams; + + // Setting LocallabSpotEdited number coherent with spots number in lastsaved->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); } else { const ProfileStoreEntry* entry = profiles->getSelectedEntry(); if (entry) { const PartialProfile* partProfile = ProfileStore::getInstance()->getProfile (entry); *custom->pparams = *partProfile->pparams; + + // Setting LocallabSpotEdited number coherent with spots number in partProfile->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); } } } @@ -626,6 +635,16 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) if(!partialProfileDlg) { partialProfileDlg = new PartialPasteDlg (Glib::ustring (), parent); } + + partialProfileDlg->set_title(M("PROFILEPANEL_PASTEPPASTE")); + partialProfileDlg->updateSpotWidget(&pp); + int i = partialProfileDlg->run(); + partialProfileDlg->hide(); + + if (i != Gtk::RESPONSE_OK) { + return; + } + partialProfileDlg->applyPaste (custom->pparams, !fillMode->get_active() ? custom->pedited : nullptr, &pp, &pe); } else { // custom.pparams = clipboard.pparams filtered by ( clipboard.pedited ) @@ -633,6 +652,10 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) if (!fillMode->get_active()) { *custom->pedited = pe; + } else { + // Setting LocallabSpotEdited number coherent with spots number in custom->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); } } } else { @@ -642,10 +665,28 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) if(!partialProfileDlg) { partialProfileDlg = new PartialPasteDlg (Glib::ustring (), parent); } + + partialProfileDlg->set_title(M("PROFILEPANEL_PASTEPPASTE")); + partialProfileDlg->updateSpotWidget(&pp); + int i = partialProfileDlg->run(); + partialProfileDlg->hide(); + + if (i != Gtk::RESPONSE_OK) { + return; + } + partialProfileDlg->applyPaste (custom->pparams, nullptr, &pp, nullptr); + + // Setting LocallabSpotEdited number coherent with spots number in custom->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); } else { // custom.pparams = clipboard.pparams non filtered *custom->pparams = pp; + + // Setting LocallabSpotEdited number coherent with spots number in custom->pparams + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(custom->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); } } @@ -693,6 +734,10 @@ void ProfilePanel::selection_changed () if (s) { if (fillMode->get_active() && s->pedited) { ParamsEdited pe(true); + + // Setting LocallabSpotEdited number coherent with spots number in s->pparams + pe.locallab.spots.resize(s->pparams->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); + PartialProfile s2(s->pparams, &pe, false); changeTo (&s2, pse->label + "+"); } else { @@ -731,6 +776,10 @@ void ProfilePanel::procParamsChanged( } *custom->pparams = *p; + + // Setting LocallabSpotEdited number coherent with spots number in p + custom->pedited->locallab.spots.clear(); + custom->pedited->locallab.spots.resize(p->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); } void ProfilePanel::clearParamChanges() @@ -766,6 +815,8 @@ void ProfilePanel::initProfile (const Glib::ustring& profileFullPath, ProcParams if (lastSaved) { ParamsEdited* pe = new ParamsEdited(true); + // Setting LocallabSpotEdited number coherent with lastSaved->locallab spots number (initialized at true such as pe) + pe->locallab.spots.resize(lastSaved->locallab.spots.size(), new LocallabParamsEdited::LocallabSpotEdited(true)); // copying the provided last saved profile to ProfilePanel::lastsaved lastsaved = new PartialProfile(lastSaved, pe); } diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index fc315e1b7..e1d8c1f84 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -280,23 +280,6 @@ RTWindow::RTWindow () set_default_size (options.windowWidth, options.windowHeight); set_modal (false); - Gdk::Rectangle lMonitorRect; - get_screen()->get_monitor_geometry (std::min (options.windowMonitor, Gdk::Screen::get_default()->get_n_monitors() - 1), lMonitorRect); - - if (options.windowMaximized) { - move (lMonitorRect.get_x(), lMonitorRect.get_y()); - maximize(); - } else { - unmaximize(); - resize (options.windowWidth, options.windowHeight); - - if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height()) { - move (options.windowX, options.windowY); - } else { - move (lMonitorRect.get_x(), lMonitorRect.get_y()); - } - } - on_delete_has_run = false; is_fullscreen = false; property_destroy_with_parent().set_value (false); @@ -1081,6 +1064,17 @@ void RTWindow::updateFBToolBarVisibility (bool showFilmStripToolBar) fpanel->fileCatalog->updateFBToolBarVisibility (showFilmStripToolBar); } +void RTWindow::updateShowtooltipVisibility (bool showtooltip) +{ + if (epanel) { + epanel->updateShowtooltipVisibility (showtooltip); + } + + for (auto panel : epanels) { + panel.second->updateShowtooltipVisibility (showtooltip); + } +} + void RTWindow::updateHistogramPosition (int oldPosition, int newPosition) { if (epanel) { @@ -1100,6 +1094,44 @@ bool RTWindow::splashClosed (GdkEventAny* event) return true; } +void RTWindow::setWindowSize () +{ + Gdk::Rectangle lMonitorRect; + get_screen()->get_monitor_geometry (std::min (options.windowMonitor, Gdk::Screen::get_default()->get_n_monitors() - 1), lMonitorRect); + +#ifdef __APPLE__ + // Get macOS menu bar height + const Gdk::Rectangle lWorkAreaRect = get_screen()->get_monitor_workarea (std::min (options.windowMonitor, Gdk::Screen::get_default()->get_n_monitors() - 1)); + const int macMenuBarHeight = lWorkAreaRect.get_y(); +#endif + + if (options.windowMaximized) { +#ifdef __APPLE__ + move (lMonitorRect.get_x(), lMonitorRect.get_y() + macMenuBarHeight); +#else + move (lMonitorRect.get_x(), lMonitorRect.get_y()); +#endif + maximize(); + } else { + unmaximize(); + resize (options.windowWidth, options.windowHeight); + +#ifdef __APPLE__ + if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height() - macMenuBarHeight) { + move (options.windowX, options.windowY + macMenuBarHeight); + } else { + move (lMonitorRect.get_x(), lMonitorRect.get_y() + macMenuBarHeight); + } +#else + if (options.windowX <= lMonitorRect.get_x() + lMonitorRect.get_width() && options.windowY <= lMonitorRect.get_y() + lMonitorRect.get_height()) { + move (options.windowX, options.windowY); + } else { + move (lMonitorRect.get_x(), lMonitorRect.get_y()); + } +#endif + } +} + void RTWindow::set_title_decorated (Glib::ustring fname) { Glib::ustring subtitle; diff --git a/rtgui/rtwindow.h b/rtgui/rtwindow.h index c493c2db4..e5e180747 100644 --- a/rtgui/rtwindow.h +++ b/rtgui/rtwindow.h @@ -119,10 +119,12 @@ public: void updateHistogramPosition (int oldPosition, int newPosition); void updateFBQueryTB (bool singleRow); void updateFBToolBarVisibility (bool showFilmStripToolBar); + void updateShowtooltipVisibility (bool showtooltip); bool getIsFullscreen() { return is_fullscreen; } + void setWindowSize (); void set_title_decorated (Glib::ustring fname); void closeOpenEditors(); void setEditorMode (bool tabbedUI); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 7452c9d72..c1884edeb 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -39,6 +39,7 @@ #include "guiutils.h" #include "batchqueue.h" #include "extprog.h" +#include "md5helper.h" #include "pathutils.h" #include "paramsedited.h" #include "procparamchangers.h" @@ -450,6 +451,7 @@ void Thumbnail::setProcParams (const ProcParams& pp, ParamsEdited* pe, int whoCh const bool needsReprocessing = resetToDefault || pparams->toneCurve != pp.toneCurve + || pparams->locallab != pp.locallab || pparams->labCurve != pp.labCurve || pparams->localContrast != pp.localContrast || pparams->rgbCurves != pp.rgbCurves @@ -873,7 +875,7 @@ void Thumbnail::_loadThumbnail(bool firstTrial) if (!succ && firstTrial) { _generateThumbnailImage (); - if (cfs.supported && firstTrial) { + if (cfs.supported) { _loadThumbnail (false); } @@ -993,7 +995,7 @@ void Thumbnail::setFileName (const Glib::ustring &fn) { fname = fn; - cfs.md5 = cachemgr->getMD5 (fname); + cfs.md5 = ::getMD5 (fname); } int Thumbnail::getRank () const diff --git a/rtgui/toolbar.cc b/rtgui/toolbar.cc index 99c4196c6..e642ae51b 100644 --- a/rtgui/toolbar.cc +++ b/rtgui/toolbar.cc @@ -109,7 +109,7 @@ void ToolBar::setTool (ToolMode tool) ConnectionBlocker cropBlocker(cropConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); - stopEdit = tool == TMHand && handTool->get_active() && editingMode; + stopEdit = tool == TMHand && handTool->get_active() && editingMode && !blockEdit; handTool->set_active (false); @@ -206,7 +206,7 @@ void ToolBar::hand_pressed () ConnectionBlocker cropBlocker(cropConn); ConnectionBlocker wbWasBlocked(wbTool, wbConn), cpWasBlocked(colPickerTool, cpConn); - if (editingMode) { + if (editingMode && !blockEdit) { stopEditMode(); if (listener) { listener->editModeSwitchedOff (); @@ -291,7 +291,7 @@ void ToolBar::colPicker_pressed (GdkEventButton* event) if (current != TMColorPicker) { // Disabling all other tools, enabling the Picker tool and entering the "visible pickers" mode - if (editingMode) { + if (editingMode && !blockEdit) { stopEditMode(); if (listener) { listener->editModeSwitchedOff (); diff --git a/rtgui/toolbar.h b/rtgui/toolbar.h index 8ec6bb615..a4525019f 100644 --- a/rtgui/toolbar.h +++ b/rtgui/toolbar.h @@ -65,6 +65,7 @@ protected: ToolMode current; bool allowNoTool; bool editingMode; // true if the cursor is being used to remotely edit tool's values + bool blockEdit; // true if edit tool shouldn't be disabled when pressing hand button or h/H key sigc::connection handConn; sigc::connection wbConn; sigc::connection cpConn; @@ -99,4 +100,9 @@ public: bool handleShortcutKey (GdkEventKey* event); void setBatchMode(); + + void blockEditDeactivation(bool cond = true) + { + blockEdit = cond; + } }; diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 0237a0917..70ae232a4 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -26,12 +26,13 @@ #include "../rtengine/dfmanager.h" #include "../rtengine/ffmanager.h" #include "../rtengine/improcfun.h" +#include "../rtengine/perspectivecorrection.h" #include "../rtengine/procevents.h" #include "../rtengine/refreshmap.h" using namespace rtengine::procparams; -ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favoritePanelSW(nullptr), hasChanged (false), editDataProvider (nullptr) +ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favoritePanelSW(nullptr), hasChanged (false), editDataProvider (nullptr), photoLoadedOnce(false) { favoritePanel = Gtk::manage (new ToolVBox ()); @@ -41,6 +42,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit transformPanel = Gtk::manage (new ToolVBox ()); rawPanel = Gtk::manage (new ToolVBox ()); advancedPanel = Gtk::manage (new ToolVBox ()); + locallabPanel = Gtk::manage(new ToolVBox()); coarse = Gtk::manage (new CoarsePanel ()); toneCurve = Gtk::manage (new ToneCurve ()); @@ -51,51 +53,52 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit epd = Gtk::manage (new EdgePreservingDecompositionUI ()); sharpening = Gtk::manage (new Sharpening ()); localContrast = Gtk::manage(new LocalContrast()); - sharpenEdge = Gtk::manage (new SharpenEdge ()); - sharpenMicro = Gtk::manage (new SharpenMicro ()); - lcurve = Gtk::manage (new LCurve ()); - rgbcurves = Gtk::manage (new RGBCurves ()); - colortoning = Gtk::manage (new ColorToning ()); - lensgeom = Gtk::manage (new LensGeometry ()); - lensProf = Gtk::manage (new LensProfilePanel ()); - distortion = Gtk::manage (new Distortion ()); - rotate = Gtk::manage (new Rotate ()); - vibrance = Gtk::manage (new Vibrance ()); - colorappearance = Gtk::manage (new ColorAppearance ()); - whitebalance = Gtk::manage (new WhiteBalance ()); - vignetting = Gtk::manage (new Vignetting ()); - retinex = Gtk::manage (new Retinex ()); - gradient = Gtk::manage (new Gradient ()); - pcvignette = Gtk::manage (new PCVignette ()); - perspective = Gtk::manage (new PerspCorrection ()); - cacorrection = Gtk::manage (new CACorrection ()); - chmixer = Gtk::manage (new ChMixer ()); - blackwhite = Gtk::manage (new BlackWhite ()); - resize = Gtk::manage (new Resize ()); - prsharpening = Gtk::manage (new PrSharpening()); - crop = Gtk::manage (new Crop ()); - icm = Gtk::manage (new ICMPanel ()); + sharpenEdge = Gtk::manage(new SharpenEdge()); + sharpenMicro = Gtk::manage(new SharpenMicro()); + lcurve = Gtk::manage(new LCurve()); + rgbcurves = Gtk::manage(new RGBCurves()); + colortoning = Gtk::manage(new ColorToning()); + lensgeom = Gtk::manage(new LensGeometry()); + lensProf = Gtk::manage(new LensProfilePanel()); + distortion = Gtk::manage(new Distortion()); + rotate = Gtk::manage(new Rotate()); + vibrance = Gtk::manage(new Vibrance()); + colorappearance = Gtk::manage(new ColorAppearance()); + whitebalance = Gtk::manage(new WhiteBalance()); + vignetting = Gtk::manage(new Vignetting()); + retinex = Gtk::manage(new Retinex()); + gradient = Gtk::manage(new Gradient()); + locallab = Gtk::manage(new Locallab()); + pcvignette = Gtk::manage(new PCVignette()); + perspective = Gtk::manage(new PerspCorrection()); + cacorrection = Gtk::manage(new CACorrection()); + chmixer = Gtk::manage(new ChMixer()); + blackwhite = Gtk::manage(new BlackWhite()); + resize = Gtk::manage(new Resize()); + prsharpening = Gtk::manage(new PrSharpening()); + crop = Gtk::manage(new Crop()); + icm = Gtk::manage(new ICMPanel()); metadata = Gtk::manage(new MetaDataPanel()); - wavelet = Gtk::manage (new Wavelet ()); - dirpyrequalizer = Gtk::manage (new DirPyrEqualizer ()); - hsvequalizer = Gtk::manage (new HSVEqualizer ()); - filmSimulation = Gtk::manage (new FilmSimulation ()); + wavelet = Gtk::manage(new Wavelet()); + dirpyrequalizer = Gtk::manage(new DirPyrEqualizer()); + hsvequalizer = Gtk::manage(new HSVEqualizer()); + filmSimulation = Gtk::manage(new FilmSimulation()); softlight = Gtk::manage(new SoftLight()); dehaze = Gtk::manage(new Dehaze()); - sensorbayer = Gtk::manage (new SensorBayer ()); - sensorxtrans = Gtk::manage (new SensorXTrans ()); - bayerprocess = Gtk::manage (new BayerProcess ()); - xtransprocess = Gtk::manage (new XTransProcess ()); - bayerpreprocess = Gtk::manage (new BayerPreProcess ()); - preprocess = Gtk::manage (new PreProcess ()); - darkframe = Gtk::manage (new DarkFrame ()); - flatfield = Gtk::manage (new FlatField ()); - rawcacorrection = Gtk::manage (new RAWCACorr ()); - rawexposure = Gtk::manage (new RAWExposure ()); + sensorbayer = Gtk::manage(new SensorBayer()); + sensorxtrans = Gtk::manage(new SensorXTrans()); + bayerprocess = Gtk::manage(new BayerProcess()); + xtransprocess = Gtk::manage(new XTransProcess()); + bayerpreprocess = Gtk::manage(new BayerPreProcess()); + preprocess = Gtk::manage(new PreProcess()); + darkframe = Gtk::manage(new DarkFrame()); + flatfield = Gtk::manage(new FlatField()); + rawcacorrection = Gtk::manage(new RAWCACorr()); + rawexposure = Gtk::manage(new RAWExposure()); preprocessWB = Gtk::manage (new PreprocessWB ()); - bayerrawexposure = Gtk::manage (new BayerRAWExposure ()); - xtransrawexposure = Gtk::manage (new XTransRAWExposure ()); - fattal = Gtk::manage (new FattalToneMapping ()); + bayerrawexposure = Gtk::manage(new BayerRAWExposure()); + xtransrawexposure = Gtk::manage(new XTransRAWExposure()); + fattal = Gtk::manage(new FattalToneMapping()); filmNegative = Gtk::manage (new FilmNegative ()); pdSharpening = Gtk::manage (new PdSharpening()); // So Demosaic, Line noise filter, Green Equilibration, Ca-Correction (garder le nom de section identique!) and Black-Level will be moved in a "Bayer sensor" tool, @@ -136,6 +139,8 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit addfavoritePanel (detailsPanel, dirpyrequalizer); addfavoritePanel (detailsPanel, dehaze); addfavoritePanel (advancedPanel, wavelet); + addfavoritePanel(locallabPanel, locallab); + addfavoritePanel (transformPanel, crop); addfavoritePanel (transformPanel, resize); addPanel (resize->getPackBox(), prsharpening, 2); @@ -174,25 +179,24 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit toolPanels.push_back (coarse); toolPanels.push_back(metadata); - toolPanelNotebook = new Gtk::Notebook (); - toolPanelNotebook->set_name ("ToolPanelNotebook"); - + toolPanelNotebook = new Gtk::Notebook(); + toolPanelNotebook->set_name("ToolPanelNotebook"); exposurePanelSW = Gtk::manage (new MyScrolledWindow ()); detailsPanelSW = Gtk::manage (new MyScrolledWindow ()); colorPanelSW = Gtk::manage (new MyScrolledWindow ()); transformPanelSW = Gtk::manage (new MyScrolledWindow ()); rawPanelSW = Gtk::manage (new MyScrolledWindow ()); advancedPanelSW = Gtk::manage (new MyScrolledWindow ()); + locallabPanelSW = Gtk::manage(new MyScrolledWindow()); // load panel endings - for (int i = 0; i < 7; i++) { + for (int i = 0; i < 8; i++) { vbPanelEnd[i] = Gtk::manage (new Gtk::VBox ()); imgPanelEnd[i] = Gtk::manage (new RTImage ("ornament1.png")); - imgPanelEnd[i]->show (); - vbPanelEnd[i]->pack_start (*imgPanelEnd[i], Gtk::PACK_SHRINK); + imgPanelEnd[i]->show(); + vbPanelEnd[i]->pack_start(*imgPanelEnd[i], Gtk::PACK_SHRINK); vbPanelEnd[i]->show_all(); } - if(favoriteCount > 0) { favoritePanelSW = Gtk::manage(new MyScrolledWindow()); favoritePanelSW->add(*favoritePanel); @@ -217,6 +221,10 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit advancedPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0); advancedPanel->pack_start (*vbPanelEnd[6], Gtk::PACK_SHRINK, 0); + locallabPanelSW->add(*locallabPanel); + locallabPanel->pack_start(*Gtk::manage(new Gtk::HSeparator), Gtk::PACK_SHRINK, 0); + locallabPanel->pack_start(*vbPanelEnd[7], Gtk::PACK_SHRINK, 4); + transformPanelSW->add (*transformPanel); transformPanel->pack_start (*Gtk::manage (new Gtk::HSeparator), Gtk::PACK_SHRINK, 0); transformPanel->pack_start (*vbPanelEnd[4], Gtk::PACK_SHRINK, 4); @@ -230,10 +238,11 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit toiD = Gtk::manage (new TextOrIcon ("detail.png", M ("MAIN_TAB_DETAIL"), M ("MAIN_TAB_DETAIL_TOOLTIP"))); toiC = Gtk::manage (new TextOrIcon ("color-circles.png", M ("MAIN_TAB_COLOR"), M ("MAIN_TAB_COLOR_TOOLTIP"))); toiW = Gtk::manage (new TextOrIcon ("atom.png", M ("MAIN_TAB_ADVANCED"), M ("MAIN_TAB_ADVANCED_TOOLTIP"))); + toiL = Gtk::manage(new TextOrIcon("hand-open.png", M("MAIN_TAB_LOCALLAB"), M("MAIN_TAB_LOCALLAB_TOOLTIP"))); + toiT = Gtk::manage (new TextOrIcon ("transform.png", M ("MAIN_TAB_TRANSFORM"), M ("MAIN_TAB_TRANSFORM_TOOLTIP"))); toiR = Gtk::manage (new TextOrIcon ("bayer.png", M ("MAIN_TAB_RAW"), M ("MAIN_TAB_RAW_TOOLTIP"))); toiM = Gtk::manage (new TextOrIcon ("metadata.png", M ("MAIN_TAB_METADATA"), M ("MAIN_TAB_METADATA_TOOLTIP"))); - if (favoritePanelSW) { toolPanelNotebook->append_page (*favoritePanelSW, *toiF); } @@ -241,45 +250,78 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit toolPanelNotebook->append_page (*detailsPanelSW, *toiD); toolPanelNotebook->append_page (*colorPanelSW, *toiC); toolPanelNotebook->append_page (*advancedPanelSW, *toiW); + + // Locallab notebook is hidden in batch mode + if (!batch) { + toolPanelNotebook->append_page(*locallabPanelSW, *toiL); + } + toolPanelNotebook->append_page (*transformPanelSW, *toiT); toolPanelNotebook->append_page (*rawPanelSW, *toiR); toolPanelNotebook->append_page (*metadata, *toiM); - toolPanelNotebook->set_current_page (0); + toolPanelNotebook->set_scrollable(); + toolPanelNotebook->show_all(); - toolPanelNotebook->set_scrollable (); - toolPanelNotebook->show_all (); + notebookconn = toolPanelNotebook->signal_switch_page().connect( + sigc::mem_fun(*this, &ToolPanelCoordinator::notebookPageChanged)); - for (auto toolPanel : toolPanels) { - toolPanel->setListener (this); + // In batch mode, notebookPageChanged method is blocked because it's useless to display spots + if (batch) { + notebookconn.block(true); } - whitebalance->setWBProvider (this); - whitebalance->setSpotWBListener (this); - darkframe->setDFProvider (this); - flatfield->setFFProvider (this); - lensgeom->setLensGeomListener (this); - rotate->setLensGeomListener (this); - distortion->setLensGeomListener (this); - crop->setCropPanelListener (this); - icm->setICMPanelListener (this); + for (auto toolPanel : toolPanels) { + toolPanel->setListener(this); + } + + whitebalance->setWBProvider(this); + whitebalance->setSpotWBListener(this); + darkframe->setDFProvider(this); + flatfield->setFFProvider(this); + lensgeom->setLensGeomListener(this); + rotate->setLensGeomListener(this); + perspective->setLensGeomListener(this); + distortion->setLensGeomListener(this); + crop->setCropPanelListener(this); + icm->setICMPanelListener(this); filmNegative->setFilmNegProvider (this); - toolBar = new ToolBar (); - toolBar->setToolBarListener (this); + toolBar = new ToolBar(); + toolBar->setToolBarListener(this); + + prevPage = toolPanelNotebook->get_nth_page(0); } -void ToolPanelCoordinator::addPanel (Gtk::Box* where, FoldableToolPanel* panel, int level) +void ToolPanelCoordinator::notebookPageChanged(Gtk::Widget* page, guint page_num) +{ + // Locallab spot curves are set visible if at least one photo has been loaded (to avoid + // segfault) and locallab panel is active + if (photoLoadedOnce) { + if (page == locallabPanelSW) { + toolBar->blockEditDeactivation(); // Avoid edit tool deactivation when Locallab page is active (except if pressing other tools button) + locallab->subscribe(); + } + + if (prevPage == locallabPanelSW) { // To deactivate Locallab only when switching from Locallab page + toolBar->blockEditDeactivation(false); + locallab->unsubscribe(); + } + + prevPage = page; + } +} + +void ToolPanelCoordinator::addPanel(Gtk::Box* where, FoldableToolPanel* panel, int level) { - panel->setParent (where); - panel->setLevel (level); + panel->setParent(where); + panel->setLevel(level); - expList.push_back (panel->getExpander()); - where->pack_start (*panel->getExpander(), false, false); - toolPanels.push_back (panel); + expList.push_back(panel->getExpander()); + where->pack_start(*panel->getExpander(), false, false); + toolPanels.push_back(panel); } - void ToolPanelCoordinator::addfavoritePanel (Gtk::Box* where, FoldableToolPanel* panel, int level) { auto name = panel->getToolName(); @@ -296,13 +338,17 @@ ToolPanelCoordinator::~ToolPanelCoordinator () { idle_register.destroy(); - closeImage (); + closeImage(); + + // When deleting toolPanelNotebook, pages removal activates notebookPageChanged function + // which is responsible of segfault if listener isn't deactivated before + notebookconn.block(true); delete toolPanelNotebook; delete toolBar; } -void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXtrans, bool isMono) +void ToolPanelCoordinator::imageTypeChanged(bool isRaw, bool isBayer, bool isXtrans, bool isMono) { if (isRaw) { if (isBayer) { @@ -325,8 +371,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt return false; } ); - } - else if (isXtrans) { + } else if (isXtrans) { idle_register.add( [this]() -> bool { @@ -346,8 +391,7 @@ void ToolPanelCoordinator::imageTypeChanged (bool isRaw, bool isBayer, bool isXt return false; } ); - } - else if (isMono) { + } else if (isMono) { idle_register.add( [this]() -> bool { @@ -424,18 +468,18 @@ void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const int changeFlags = rtengine::RefreshMapper::getInstance()->getAction(event); - ProcParams* params = ipc->beginUpdateParams (); + ProcParams* params = ipc->beginUpdateParams(); for (auto toolPanel : toolPanels) { - toolPanel->write (params); + toolPanel->write(params); } // Compensate rotation on flip if (event == rtengine::EvCTHFlip || event == rtengine::EvCTVFlip) { - if (fabs (params->rotate.degree) > 0.001) { + if (fabs(params->rotate.degree) > 0.001) { params->rotate.degree *= -1; changeFlags |= rtengine::RefreshMapper::getInstance()->getAction(rtengine::EvROTDegree); - rotate->read (params); + rotate->read(params); } } @@ -453,34 +497,78 @@ void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const if (event == rtengine::EvPhotoLoaded || event == rtengine::EvProfileChanged || event == rtengine::EvHistoryBrowsed || event == rtengine::EvCTRotate) { // updating the "on preview" geometry int fw, fh; - ipc->getInitialImage()->getImageSource()->getFullSize (fw, fh, tr); - gradient->updateGeometry (params->gradient.centerX, params->gradient.centerY, params->gradient.feather, params->gradient.degree, fw, fh); + ipc->getInitialImage()->getImageSource()->getFullSize(fw, fh, tr); + gradient->updateGeometry(params->gradient.centerX, params->gradient.centerY, params->gradient.feather, params->gradient.degree, fw, fh); } // some transformations make the crop change for convenience if (event == rtengine::EvCTHFlip) { - crop->hFlipCrop (); - crop->write (params); + crop->hFlipCrop(); + crop->write(params); } else if (event == rtengine::EvCTVFlip) { - crop->vFlipCrop (); - crop->write (params); + crop->vFlipCrop(); + crop->write(params); } else if (event == rtengine::EvCTRotate) { - crop->rotateCrop (params->coarse.rotate, params->coarse.hflip, params->coarse.vflip); - crop->write (params); - resize->update (params->crop.enabled, params->crop.w, params->crop.h, ipc->getFullWidth(), ipc->getFullHeight()); - resize->write (params); + crop->rotateCrop(params->coarse.rotate, params->coarse.hflip, params->coarse.vflip); + crop->write(params); + resize->update(params->crop.enabled, params->crop.w, params->crop.h, ipc->getFullWidth(), ipc->getFullHeight()); + resize->write(params); } else if (event == rtengine::EvCrop) { - resize->update (params->crop.enabled, params->crop.w, params->crop.h); - resize->write (params); + resize->update(params->crop.enabled, params->crop.w, params->crop.h); + resize->write(params); } - ipc->endUpdateParams (changeFlags); // starts the IPC processing + /* + * Manage Locallab mask visibility: + * - Mask preview is updated when choosing a mask preview method + * - Mask preview is also updated when modifying (to avoid hiding a potentially visible mask combobox): + * - Color&Light invers + * - Exposure inversex + * - Shadow Highlight inverssh + * - Soft Light softMethod + * - Mask preview is stopped when creating, deleting or selecting a spot + * - Mask preview is also stopped when removing a spot or resetting all mask visibility + */ + if (event == rtengine::EvlocallabshowmaskMethod) { + const Locallab::llMaskVisibility maskStruc = locallab->getMaskVisibility(); + ipc->setLocallabMaskVisibility(maskStruc.previewDeltaE, maskStruc.colorMask, maskStruc.colorMaskinv, maskStruc.expMask, maskStruc.expMaskinv, + maskStruc.SHMask, maskStruc.SHMaskinv, maskStruc.vibMask, maskStruc.softMask, + maskStruc.blMask, maskStruc.tmMask, maskStruc.retiMask, maskStruc.sharMask, + maskStruc.lcMask, maskStruc.cbMask); + } else if (event == rtengine::EvLocallabSpotCreated || event == rtengine::EvLocallabSpotSelectedWithMask || + event == rtengine::EvLocallabSpotDeleted || event == rtengine::Evlocallabshowreset || + event == rtengine::EvlocallabToolRemovedWithRefresh) { + locallab->resetMaskVisibility(); + ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + + ipc->endUpdateParams(changeFlags); // starts the IPC processing hasChanged = true; for (auto paramcListener : paramcListeners) { - paramcListener->procParamsChanged (params, event, descr); + paramcListener->procParamsChanged(params, event, descr); } + + // Locallab spot curves are set visible if at least one photo has been loaded (to avoid + // segfault) and locallab panel is active + // When a new photo is loaded, Locallab spot curves need to be set visible again +const auto func = + [this]() -> bool + { + if (photoLoadedOnce && (toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()) == locallabPanelSW)) { + locallab->subscribe(); + } + + return false; + }; + +if (event == rtengine::EvPhotoLoaded) { + idle_register.add(func); +} + + photoLoadedOnce = true; + } void ToolPanelCoordinator::profileChange( @@ -497,7 +585,7 @@ void ToolPanelCoordinator::profileChange( return; } - ProcParams *params = ipc->beginUpdateParams (); + ProcParams *params = ipc->beginUpdateParams(); ProcParams *mergedParams = new ProcParams(); // Copy the current params as default values for the fusion @@ -510,17 +598,17 @@ void ToolPanelCoordinator::profileChange( } // And apply the partial profile nparams to mergedParams - nparams->applyTo (mergedParams, fromLastSave); + nparams->applyTo(mergedParams, fromLastSave); // Derive the effective changes, if it's a profile change, to prevent slow RAW rerendering if not necessary bool filterRawRefresh = false; if (event != rtengine::EvPhotoLoaded) { - ParamsEdited pe (true); - std::vector lParams (2); + ParamsEdited pe(true); + std::vector lParams(2); lParams[0] = *params; lParams[1] = *mergedParams; - pe.initFrom (lParams); + pe.initFrom(lParams); filterRawRefresh = pe.raw.isUnchanged() && pe.lensProf.isUnchanged() && pe.retinex.isUnchanged() && pe.filmNegative.isUnchanged() && pe.pdsharpening.isUnchanged(); } @@ -539,35 +627,61 @@ void ToolPanelCoordinator::profileChange( } // trimming overflowing cropped area - ipc->getInitialImage()->getImageSource()->getFullSize (fw, fh, tr); - crop->trim (params, fw, fh); + ipc->getInitialImage()->getImageSource()->getFullSize(fw, fh, tr); + crop->trim(params, fw, fh); // updating the GUI with updated values for (auto toolPanel : toolPanels) { - toolPanel->read (params); + toolPanel->read(params); if (event == rtengine::EvPhotoLoaded || event == rtengine::EvProfileChanged) { toolPanel->autoOpenCurve(); + + // For Locallab, reset tool expanders visibility only when a photo or profile is loaded + locallab->openAllTools(); } } if (event == rtengine::EvPhotoLoaded || event == rtengine::EvProfileChanged || event == rtengine::EvHistoryBrowsed || event == rtengine::EvCTRotate) { // updating the "on preview" geometry - gradient->updateGeometry (params->gradient.centerX, params->gradient.centerY, params->gradient.feather, params->gradient.degree, fw, fh); + gradient->updateGeometry(params->gradient.centerX, params->gradient.centerY, params->gradient.feather, params->gradient.degree, fw, fh); } + // Reset Locallab mask visibility + locallab->resetMaskVisibility(); + ipc->setLocallabMaskVisibility(false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + // start the IPC processing if (filterRawRefresh) { - ipc->endUpdateParams ( rtengine::RefreshMapper::getInstance()->getAction(event) & ALLNORAW ); + ipc->endUpdateParams(rtengine::RefreshMapper::getInstance()->getAction(event) & ALLNORAW); } else { - ipc->endUpdateParams (event); + ipc->endUpdateParams(event); } hasChanged = event != rtengine::EvProfileChangeNotification; for (auto paramcListener : paramcListeners) { - paramcListener->procParamsChanged (params, event, descr); + paramcListener->procParamsChanged(params, event, descr); } + + // Locallab spot curves are set visible if at least one photo has been loaded (to avoid + // segfault) and locallab panel is active + // When a new photo is loaded, Locallab spot curves need to be set visible again +const auto func = + [this]() -> bool + { + if (photoLoadedOnce && (toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()) == locallabPanelSW)) { + locallab->subscribe(); + } + + return false; + }; + +if (event == rtengine::EvPhotoLoaded) { + idle_register.add(func); +} + + photoLoadedOnce = true; } void ToolPanelCoordinator::setDefaults(const ProcParams* defparams) @@ -579,59 +693,61 @@ void ToolPanelCoordinator::setDefaults(const ProcParams* defparams) } } -CropGUIListener* ToolPanelCoordinator::getCropGUIListener () +CropGUIListener* ToolPanelCoordinator::getCropGUIListener() { return crop; } -void ToolPanelCoordinator::initImage (rtengine::StagedImageProcessor* ipc_, bool raw) +void ToolPanelCoordinator::initImage(rtengine::StagedImageProcessor* ipc_, bool raw) { ipc = ipc_; - toneCurve->disableListener (); - toneCurve->enableAll (); - toneCurve->enableListener (); + toneCurve->disableListener(); + toneCurve->enableAll(); + toneCurve->enableListener(); if (ipc) { const rtengine::FramesMetaData* pMetaData = ipc->getInitialImage()->getMetaData(); metadata->setImageData(pMetaData); - ipc->setAutoExpListener (toneCurve); - ipc->setAutoCamListener (colorappearance); - ipc->setAutoBWListener (blackwhite); - ipc->setFrameCountListener (bayerprocess); + ipc->setAutoExpListener(toneCurve); + ipc->setAutoCamListener(colorappearance); + ipc->setAutoBWListener(blackwhite); + ipc->setFrameCountListener(bayerprocess); ipc->setFlatFieldAutoClipListener (flatfield); ipc->setBayerAutoContrastListener (bayerprocess); ipc->setXtransAutoContrastListener (xtransprocess); ipc->setpdSharpenAutoContrastListener (pdSharpening); ipc->setpdSharpenAutoRadiusListener (pdSharpening); - ipc->setAutoWBListener (whitebalance); - ipc->setAutoColorTonListener (colortoning); - ipc->setAutoChromaListener (dirpyrdenoise); - ipc->setWaveletListener (wavelet); - ipc->setRetinexListener (retinex); - ipc->setSizeListener (crop); - ipc->setSizeListener (resize); - ipc->setImageTypeListener (this); + ipc->setAutoWBListener(whitebalance); + ipc->setAutoColorTonListener(colortoning); + ipc->setAutoChromaListener(dirpyrdenoise); + ipc->setWaveletListener(wavelet); + ipc->setRetinexListener(retinex); + ipc->setSizeListener(crop); + ipc->setSizeListener(resize); + ipc->setLocallabListener(locallab); + ipc->setImageTypeListener(this); ipc->setFilmNegListener (filmNegative); - flatfield->setShortcutPath (Glib::path_get_dirname (ipc->getInitialImage()->getFileName())); + flatfield->setShortcutPath(Glib::path_get_dirname(ipc->getInitialImage()->getFileName())); - icm->setRawMeta (raw, (const rtengine::FramesData*)pMetaData); - lensProf->setRawMeta (raw, pMetaData); + icm->setRawMeta(raw, (const rtengine::FramesData*)pMetaData); + lensProf->setRawMeta(raw, pMetaData); + perspective->setMetadata(pMetaData); } - toneCurve->setRaw (raw); + toneCurve->setRaw(raw); hasChanged = true; } -void ToolPanelCoordinator::closeImage () +void ToolPanelCoordinator::closeImage() { if (ipc) { - ipc->stopProcessing (); + ipc->stopProcessing(); ipc = nullptr; } } @@ -687,33 +803,40 @@ void ToolPanelCoordinator::updateToolState() } } -void ToolPanelCoordinator::readOptions () +void ToolPanelCoordinator::readOptions() { - crop->readOptions (); + crop->readOptions(); } -void ToolPanelCoordinator::writeOptions () +void ToolPanelCoordinator::writeOptions() { - crop->writeOptions (); + crop->writeOptions(); if (options.autoSaveTpOpen) { - writeToolExpandedStatus (options.tpOpen); + writeToolExpandedStatus(options.tpOpen); } } -void ToolPanelCoordinator::writeToolExpandedStatus (std::vector &tpOpen) +void ToolPanelCoordinator::writeToolExpandedStatus(std::vector &tpOpen) { - tpOpen.clear (); + tpOpen.clear(); for (size_t i = 0; i < expList.size(); i++) { - tpOpen.push_back (expList.at (i)->get_expanded ()); + tpOpen.push_back(expList.at(i)->get_expanded()); } - wavelet->writeOptions (tpOpen); - retinex->writeOptions (tpOpen); + wavelet->writeOptions(tpOpen); + retinex->writeOptions(tpOpen); + +} + + +void ToolPanelCoordinator::updateShowtooltipVisibility (bool showtooltip) +{ + locallab->updateShowtooltipVisibility(showtooltip); } @@ -724,15 +847,15 @@ void ToolPanelCoordinator::spotWBselected(int x, int y, Thumbnail* thm) } // toolBar->setTool (TOOL_HAND); - int rect = whitebalance->getSize (); + int rect = whitebalance->getSize(); int ww = ipc->getFullWidth(); int hh = ipc->getFullHeight(); if (x - rect > 0 && y - rect > 0 && x + rect < ww && y + rect < hh) { double temp; double green; - ipc->getSpotWB (x, y, rect, temp, green); - whitebalance->setWB (temp, green); + ipc->getSpotWB(x, y, rect, temp, green); + whitebalance->setWB(temp, green); } } @@ -741,7 +864,8 @@ void ToolPanelCoordinator::sharpMaskSelected(bool sharpMask) if (!ipc) { return; } - ipc->beginUpdateParams (); + + ipc->beginUpdateParams(); ipc->endUpdateParams (ipc->setSharpMask(sharpMask)); } @@ -782,7 +906,7 @@ CropGUIListener* ToolPanelCoordinator::startCropEditing(Thumbnail* thm) return crop; } -void ToolPanelCoordinator::autoCropRequested () +void ToolPanelCoordinator::autoCropRequested() { if (!ipc) { @@ -790,12 +914,12 @@ void ToolPanelCoordinator::autoCropRequested () } int x1, y1, x2, y2, w, h; - ipc->getAutoCrop (crop->getRatio(), x1, y1, w, h); + ipc->getAutoCrop(crop->getRatio(), x1, y1, w, h); x2 = x1 + w - 1; y2 = y1 + h - 1; - crop->cropInit (x1, y1, w, h); - crop->cropResized (x1, y1, x2, y2); - crop->cropManipReady (); + crop->cropInit(x1, y1, w, h); + crop->cropResized(x1, y1, x2, y2); + crop->cropManipReady(); } rtengine::RawImage* ToolPanelCoordinator::getDF() @@ -809,11 +933,11 @@ rtengine::RawImage* ToolPanelCoordinator::getDF() if (imd) { int iso = imd->getISOSpeed(); double shutter = imd->getShutterSpeed(); - std::string maker ( imd->getMake() ); - std::string model ( imd->getModel() ); + std::string maker(imd->getMake()); + std::string model(imd->getModel()); time_t timestamp = imd->getDateTimeAsTS(); - return rtengine::dfm.searchDarkFrame ( maker, model, iso, shutter, timestamp); + return rtengine::dfm.searchDarkFrame(maker, model, iso, shutter, timestamp); } return nullptr; @@ -832,12 +956,12 @@ rtengine::RawImage* ToolPanelCoordinator::getFF() // double shutter = imd->getShutterSpeed(); temporarily removed because unused double aperture = imd->getFNumber(); double focallength = imd->getFocalLen(); - std::string maker ( imd->getMake() ); - std::string model ( imd->getModel() ); - std::string lens ( imd->getLens() ); + std::string maker(imd->getMake()); + std::string model(imd->getModel()); + std::string lens(imd->getLens()); time_t timestamp = imd->getDateTimeAsTS(); - return rtengine::ffm.searchFlatField ( maker, model, lens, focallength, aperture, timestamp); + return rtengine::ffm.searchFlatField(maker, model, lens, focallength, aperture, timestamp); } return nullptr; @@ -852,49 +976,69 @@ Glib::ustring ToolPanelCoordinator::GetCurrentImageFilePath() return ipc->getInitialImage()->getFileName(); } -void ToolPanelCoordinator::straightenRequested () +void ToolPanelCoordinator::straightenRequested() { if (!ipc) { return; } - toolBar->setTool (TMStraighten); + toolBar->setTool(TMStraighten); } -double ToolPanelCoordinator::autoDistorRequested () +void ToolPanelCoordinator::autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw) +{ + if (!(ipc && (corr_pitch || corr_yaw))) { + return; + } + + rtengine::ImageSource *src = dynamic_cast(ipc->getInitialImage()); + if (!src) { + return; + } + + rtengine::procparams::ProcParams params; + ipc->getParams(¶ms); + + auto res = rtengine::PerspectiveCorrection::autocompute(src, corr_pitch, corr_yaw, ¶ms, src->getMetaData()); + rot = res.angle; + pitch = res.pitch; + yaw = res.yaw; +} + +double ToolPanelCoordinator::autoDistorRequested() { if (!ipc) { return 0.0; } - return rtengine::ImProcFunctions::getAutoDistor (ipc->getInitialImage()->getFileName(), 400); + return rtengine::ImProcFunctions::getAutoDistor(ipc->getInitialImage()->getFileName(), 400); } -void ToolPanelCoordinator::spotWBRequested (int size) +void ToolPanelCoordinator::spotWBRequested(int size) { if (!ipc) { return; } - toolBar->setTool (TMSpotWB); + toolBar->setTool(TMSpotWB); } -void ToolPanelCoordinator::cropSelectRequested () +void ToolPanelCoordinator::cropSelectRequested() { if (!ipc) { return; } - toolBar->setTool (TMCropSelect); + toolBar->setTool(TMCropSelect); } void ToolPanelCoordinator::saveInputICCReference(const Glib::ustring& fname, bool apply_wb) { if (ipc) { - ipc->saveInputICCReference (fname, apply_wb); + ipc->saveInputICCReference(fname, apply_wb); } } @@ -918,7 +1062,7 @@ void ToolPanelCoordinator::updateCurveBackgroundHistogram( retinex->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI); } -void ToolPanelCoordinator::foldAllButOne (Gtk::Box* parent, FoldableToolPanel* openedSection) +void ToolPanelCoordinator::foldAllButOne(Gtk::Box* parent, FoldableToolPanel* openedSection) { for (auto toolPanel : toolPanels) { @@ -928,10 +1072,10 @@ void ToolPanelCoordinator::foldAllButOne (Gtk::Box* parent, FoldableToolPanel* o if (currentTP->getParent() == parent) { // Section in the same tab, we unfold it if it's not the one that has been clicked if (currentTP != openedSection) { - currentTP->setExpanded (false); + currentTP->setExpanded(false); } else { if (!currentTP->getExpanded()) { - currentTP->setExpanded (true); + currentTP->setExpanded(true); } } } @@ -939,7 +1083,7 @@ void ToolPanelCoordinator::foldAllButOne (Gtk::Box* parent, FoldableToolPanel* o } } -bool ToolPanelCoordinator::handleShortcutKey (GdkEventKey* event) +bool ToolPanelCoordinator::handleShortcutKey(GdkEventKey* event) { //bool ctrl = event->state & GDK_CONTROL_MASK; temporarily removed because unused @@ -955,31 +1099,35 @@ bool ToolPanelCoordinator::handleShortcutKey (GdkEventKey* event) return true; case GDK_KEY_e: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*exposurePanelSW)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*exposurePanelSW)); return true; case GDK_KEY_d: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*detailsPanelSW)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*detailsPanelSW)); return true; case GDK_KEY_c: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*colorPanelSW)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*colorPanelSW)); return true; case GDK_KEY_t: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*transformPanelSW)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*transformPanelSW)); return true; case GDK_KEY_r: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*rawPanelSW)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*rawPanelSW)); return true; case GDK_KEY_a: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*advancedPanelSW)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*advancedPanelSW)); + return true; + + case GDK_KEY_o: + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*locallabPanelSW)); return true; case GDK_KEY_m: - toolPanelNotebook->set_current_page (toolPanelNotebook->page_num (*metadata)); + toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(*metadata)); return true; } } @@ -987,7 +1135,7 @@ bool ToolPanelCoordinator::handleShortcutKey (GdkEventKey* event) return false; } -void ToolPanelCoordinator::updateVScrollbars (bool hide) +void ToolPanelCoordinator::updateVScrollbars(bool hide) { GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected Gtk::PolicyType policy = hide ? Gtk::POLICY_NEVER : Gtk::POLICY_AUTOMATIC; @@ -1000,20 +1148,24 @@ void ToolPanelCoordinator::updateVScrollbars (bool hide) transformPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy); rawPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy); advancedPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy); + locallabPanelSW->set_policy(Gtk::POLICY_AUTOMATIC, policy); + for (auto currExp : expList) { - currExp->updateVScrollbars (hide); + currExp->updateVScrollbars(hide); } } -void ToolPanelCoordinator::updateTPVScrollbar (bool hide) + +void ToolPanelCoordinator::updateTPVScrollbar(bool hide) { - updateVScrollbars (hide); + updateVScrollbars(hide); } -void ToolPanelCoordinator::toolSelected (ToolMode tool) +void ToolPanelCoordinator::toolSelected(ToolMode tool) { GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + notebookconn.block(true); // "signal_switch_page" event is blocked to avoid unsubscribing Locallab (allows a correct behavior when switching to another tool using toolbar) auto checkFavorite = [this](FoldableToolPanel* tool) { for (auto fav : favorites) { @@ -1026,18 +1178,23 @@ void ToolPanelCoordinator::toolSelected (ToolMode tool) switch (tool) { case TMCropSelect: { + toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar crop->setExpanded(true); toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(checkFavorite(crop) ? *favoritePanelSW : *transformPanelSW)); + prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event break; } case TMSpotWB: { + toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar whitebalance->setExpanded(true); toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(checkFavorite(whitebalance) ? *favoritePanelSW : *colorPanelSW)); + prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event break; } case TMStraighten: { + toolBar->blockEditDeactivation(false); // To allow deactivating Locallab when switching to another tool using toolbar rotate->setExpanded(true); bool isFavorite = checkFavorite(rotate); if (!isFavorite) { @@ -1045,33 +1202,36 @@ void ToolPanelCoordinator::toolSelected (ToolMode tool) lensgeom->setExpanded(true); } toolPanelNotebook->set_current_page(toolPanelNotebook->page_num(isFavorite ? *favoritePanelSW : *transformPanelSW)); + prevPage = toolPanelNotebook->get_nth_page(toolPanelNotebook->get_current_page()); // Updating prevPage as "signal_switch_page" event break; } default: break; } + + notebookconn.block(false); } -void ToolPanelCoordinator::editModeSwitchedOff () +void ToolPanelCoordinator::editModeSwitchedOff() { if (editDataProvider) { editDataProvider->switchOffEditMode(); } } -void ToolPanelCoordinator::dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile) +void ToolPanelCoordinator::dirSelected(const Glib::ustring& dirname, const Glib::ustring& openfile) { - flatfield->setShortcutPath (dirname); + flatfield->setShortcutPath(dirname); } -void ToolPanelCoordinator::setEditProvider (EditDataProvider *provider) +void ToolPanelCoordinator::setEditProvider(EditDataProvider *provider) { editDataProvider = provider; for (size_t i = 0; i < toolPanels.size(); i++) { - toolPanels.at (i)->setEditProvider (provider); + toolPanels.at(i)->setEditProvider(provider); } } @@ -1083,4 +1243,4 @@ bool ToolPanelCoordinator::getFilmNegativeExponents(rtengine::Coord spotA, rteng bool ToolPanelCoordinator::getRawSpotValues(rtengine::Coord spot, int spotSize, std::array& rawValues) { return ipc && ipc->getRawSpotValues(spot.x, spot.y, spotSize, rawValues); -} \ No newline at end of file +} diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 9773a2407..cc4e17e46 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -54,6 +54,7 @@ #include "lensgeomlistener.h" #include "lensprofile.h" #include "localcontrast.h" +#include "locallab.h" #include "pcvignette.h" #include "pdsharpening.h" #include "perspective.h" @@ -111,6 +112,7 @@ protected: WhiteBalance* whitebalance; Vignetting* vignetting; Gradient* gradient; + Locallab* locallab; Retinex* retinex; PCVignette* pcvignette; LensGeometry* lensgeom; @@ -176,6 +178,7 @@ protected: ToolVBox* transformPanel; ToolVBox* rawPanel; ToolVBox* advancedPanel; + ToolVBox* locallabPanel; ToolBar* toolBar; TextOrIcon* toiF; @@ -186,9 +189,10 @@ protected: TextOrIcon* toiR; TextOrIcon* toiM; TextOrIcon* toiW; + TextOrIcon* toiL; - Gtk::Image* imgPanelEnd[7]; - Gtk::VBox* vbPanelEnd[7]; + Gtk::Image* imgPanelEnd[8]; + Gtk::VBox* vbPanelEnd[8]; Gtk::ScrolledWindow* favoritePanelSW; Gtk::ScrolledWindow* exposurePanelSW; @@ -197,27 +201,32 @@ protected: Gtk::ScrolledWindow* transformPanelSW; Gtk::ScrolledWindow* rawPanelSW; Gtk::ScrolledWindow* advancedPanelSW; + Gtk::ScrolledWindow* locallabPanelSW; std::vector expList; bool hasChanged; - void addPanel (Gtk::Box* where, FoldableToolPanel* panel, int level = 1); - void foldThemAll (GdkEventButton* event); - void updateVScrollbars (bool hide); + void addPanel(Gtk::Box* where, FoldableToolPanel* panel, int level = 1); + void foldThemAll(GdkEventButton* event); + void updateVScrollbars(bool hide); void addfavoritePanel (Gtk::Box* where, FoldableToolPanel* panel, int level = 1); + void notebookPageChanged(Gtk::Widget* page, guint page_num); private: EditDataProvider *editDataProvider; + sigc::connection notebookconn; + bool photoLoadedOnce; // Used to indicated that a photo has been loaded yet + Gtk::Widget* prevPage; public: CoarsePanel* coarse; Gtk::Notebook* toolPanelNotebook; - ToolPanelCoordinator (bool batch = false); + ToolPanelCoordinator(bool batch = false); ~ToolPanelCoordinator () override; - bool getChangedState () + bool getChangedState() { return hasChanged; } @@ -233,12 +242,12 @@ public: const LUTu& histLuma, const LUTu& histLRETI ); - void foldAllButOne (Gtk::Box* parent, FoldableToolPanel* openedSection); + void foldAllButOne(Gtk::Box* parent, FoldableToolPanel* openedSection); // multiple listeners can be added that are notified on changes (typical: profile panel and the history) - void addPParamsChangeListener (PParamsChangeListener* pp) + void addPParamsChangeListener(PParamsChangeListener* pp) { - paramcListeners.push_back (pp); + paramcListeners.push_back(pp); } // toolpanellistener interface @@ -257,36 +266,36 @@ public: void setDefaults(const rtengine::procparams::ProcParams* defparams) override; // DirSelectionListener interface - void dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile); + void dirSelected(const Glib::ustring& dirname, const Glib::ustring& openfile); // to support the GUI: - CropGUIListener* getCropGUIListener (); // through the CropGUIListener the editor area can notify the "crop" ToolPanel when the crop selection changes + CropGUIListener* getCropGUIListener(); // through the CropGUIListener the editor area can notify the "crop" ToolPanel when the crop selection changes // init the toolpanelcoordinator with an image & close it - void initImage (rtengine::StagedImageProcessor* ipc_, bool israw); - void closeImage (); + void initImage(rtengine::StagedImageProcessor* ipc_, bool israw); + void closeImage(); // update the "expanded" state of the Tools - void updateToolState (); - void openAllTools (); - void closeAllTools (); + void updateToolState(); + void openAllTools(); + void closeAllTools(); // read/write the "expanded" state of the expanders & read/write the crop panel settings (ratio, guide type, etc.) - void readOptions (); - void writeOptions (); - void writeToolExpandedStatus (std::vector &tpOpen); - + void readOptions(); + void writeOptions(); + void writeToolExpandedStatus(std::vector &tpOpen); + void updateShowtooltipVisibility (bool showtooltip); // wbprovider interface void getAutoWB (double& temp, double& green, double equal, double tempBias) override { if (ipc) { - ipc->getAutoWB (temp, green, equal, tempBias); + ipc->getAutoWB(temp, green, equal, tempBias); } } void getCamWB (double& temp, double& green) override { if (ipc) { - ipc->getCamWB (temp, green); + ipc->getCamWB(temp, green); } } @@ -304,6 +313,7 @@ public: // rotatelistener interface void straightenRequested () override; void autoCropRequested () override; + void autoPerspRequested (bool corr_pitch, bool corr_yaw, double& rot, double& pitch, double& yaw) override; double autoDistorRequested () override; // spotwblistener interface @@ -324,14 +334,14 @@ public: ToolBar* getToolBar() const final; CropGUIListener* startCropEditing(Thumbnail* thm = nullptr) override; - void updateTPVScrollbar (bool hide); - bool handleShortcutKey (GdkEventKey* event); + void updateTPVScrollbar(bool hide); + bool handleShortcutKey(GdkEventKey* event); // ToolBarListener interface void toolSelected (ToolMode tool) override; void editModeSwitchedOff () final; - void setEditProvider (EditDataProvider *provider); + void setEditProvider(EditDataProvider *provider); private: IdleRegister idle_register; diff --git a/rtgui/wavelet.cc b/rtgui/wavelet.cc index 2dc4aa05c..ac8c6100b 100644 --- a/rtgui/wavelet.cc +++ b/rtgui/wavelet.cc @@ -131,19 +131,19 @@ Wavelet::Wavelet() : level1noise(Gtk::manage(new ThresholdAdjuster(M("TP_WAVELET_LEVONE"), -30., 100., 0., M("TP_WAVELET_STREN"), 1., 0., 100., 0., M("TP_WAVELET_NOIS"), 1., nullptr, false))), level2noise(Gtk::manage(new ThresholdAdjuster(M("TP_WAVELET_LEVTWO"), -30., 100., 0., M("TP_WAVELET_STREN"), 1., 0., 100., 0., M("TP_WAVELET_NOIS"), 1., nullptr, false))), level3noise(Gtk::manage(new ThresholdAdjuster(M("TP_WAVELET_LEVTHRE"), -30., 100., 0., M("TP_WAVELET_STREN"), 1., 0., 100., 0., M("TP_WAVELET_NOIS"), 1., nullptr, false))), - threshold(Gtk::manage(new Adjuster(M("TP_WAVELET_THRESHOLD"), 1, 9, 1, 5))), + threshold(Gtk::manage(new Adjuster(M("TP_WAVELET_THRESHOLD"), 1, 9, 1, 4))), // threshold2(Gtk::manage(new Adjuster(M("TP_WAVELET_THRESHOLD2"), 1, 9, 1, 4))), - threshold2(Gtk::manage(new Adjuster(M("TP_WAVELET_THRESHOLD2"), 1, 9, 1, 5))), + threshold2(Gtk::manage(new Adjuster(M("TP_WAVELET_THRESHOLD2"), 3, 9, 1, 5))), edgedetect(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECT"), 0, 100, 1, 90))), edgedetectthr(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECTTHR"), 0, 100, 1, 20))), edgedetectthr2(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECTTHR2"), -10, 100, 1, 0))), edgesensi(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGESENSI"), 0, 100, 1, 60))), edgeampli(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEAMPLI"), 0, 100, 1, 10))), ballum(Gtk::manage(new Adjuster(M("TP_WAVELET_BALLUM"), -2., 10., 0.5, 7., Gtk::manage(new RTImage("circle-white-small.png")), Gtk::manage(new RTImage("circle-black-small.png"))))), - balchrom(Gtk::manage(new Adjuster(M("TP_WAVELET_BALCHROM"), -100., 100., 1., 0., Gtk::manage(new RTImage("circle-blue-small.png")), Gtk::manage(new RTImage("circle-red-small.png"))))), + balchrom(Gtk::manage(new Adjuster(M("TP_WAVELET_BALCHROM"), -100., 100., 1., 0., Gtk::manage(new RTImage("circle-blue-yellow-small.png")), Gtk::manage(new RTImage("circle-red-green-small.png"))))), chromfi(Gtk::manage(new Adjuster(M("TP_WAVELET_CHROMFI"), 0.0, 150., 0.01, 0.))), chromco(Gtk::manage(new Adjuster(M("TP_WAVELET_CHROMCO"), 0, 100., 0.01, 0.))), - mergeL(Gtk::manage(new Adjuster(M("TP_WAVELET_MERGEL"), -50, 100, 1, 40))), + mergeL(Gtk::manage(new Adjuster(M("TP_WAVELET_MERGEL"), -50, 100, 1, 20))), mergeC(Gtk::manage(new Adjuster(M("TP_WAVELET_MERGEC"), -50, 100, 1, 20))), softrad(Gtk::manage(new Adjuster(M("TP_WAVELET_SOFTRAD"), 0.0, 100., 0.5, 0.))), softradend(Gtk::manage(new Adjuster(M("TP_WAVELET_SOFTRAD"), 0.0, 100., 0.5, 0.))), @@ -440,10 +440,10 @@ Wavelet::Wavelet() : contrastSHVBox->pack_start(*HSmethod); contrastSHVBox->pack_start(*hllev); - // contrastSHVBox->pack_start(*threshold); + contrastSHVBox->pack_start(*threshold); contrastSHVBox->pack_start(*bllev); -// contrastSHVBox->pack_start(*threshold2); - contrastSHVBox->pack_start(*curveEditorC); + contrastSHVBox->pack_start(*threshold2); + // contrastSHVBox->pack_start(*curveEditorC); Gtk::Frame* const contrastSHFrame = Gtk::manage(new Gtk::Frame(M("TP_WAVELET_APPLYTO"))); contrastSHFrame->add(*contrastSHVBox); levBox->pack_start(*contrastSHFrame); @@ -574,7 +574,7 @@ Wavelet::Wavelet() : // Denoise and Refine ToolParamBlock* const noiseBox = Gtk::manage(new ToolParamBlock()); - linkedg->set_active(true); + linkedg->set_active(false); linkedgConn = linkedg->signal_toggled().connect(sigc::mem_fun(*this, &Wavelet::linkedgToggled)); noiseBox->pack_start(*linkedg); @@ -1161,6 +1161,24 @@ Wavelet::~Wavelet() } +void Wavelet::updateGUI() +{ + const int temp2 = threshold2->getValue(); + const int temp = threshold->getValue(); + const int maxlev = thres->getValue(); + threshold2->setLimits(temp + 1, maxlev, 1, maxlev + 1); + threshold2 ->setValue(temp2); +} + +void Wavelet::updateGUImaxlev() +{ + const int temp4 = threshold->getValue(); + const int temp3 = thres->getValue(); + threshold->setLimits(1, temp3, 1, temp3); + threshold ->setValue(temp4); +} + + void Wavelet::wavChanged(double nlevel) { if (!batchMode) { @@ -1466,7 +1484,9 @@ void Wavelet::read(const ProcParams* pp, const ParamsEdited* pedited) skinprotect->setValue(pp->wavelet.skinprotect); hueskin->setValue(pp->wavelet.hueskin); hueskin2->setValue(pp->wavelet.hueskin2); + updateGUImaxlev(); threshold->setValue(pp->wavelet.threshold); + updateGUI(); threshold2->setValue(pp->wavelet.threshold2); edgedetect->setValue(pp->wavelet.edgedetect); edgedetectthr->setValue(pp->wavelet.edgedetectthr); @@ -3063,6 +3083,9 @@ void Wavelet::adjusterChanged(Adjuster* a, double newval) listener->panelChanged(EvWavradius, radius->getTextValue()); } else if (a == threshold) { listener->panelChanged(EvWavThreshold, threshold->getTextValue()); + updateGUI(); + updateGUImaxlev(); + } else if (a == threshold2) { listener->panelChanged(EvWavThreshold2, threshold2->getTextValue()); } else if (a == edgedetect) { @@ -3108,6 +3131,8 @@ void Wavelet::adjusterChanged(Adjuster* a, double newval) } listener->panelChanged(EvWavthres, thres->getTextValue()); + updateGUImaxlev(); + updateGUI(); } else if (a == skinprotect) { listener->panelChanged(EvWavSkin, skinprotect->getTextValue()); } else if (a == strength) { @@ -3671,6 +3696,8 @@ void Wavelet::neutralPressed() correction[i]->setValue(0); adjusterChanged(correction[i], 0); } + sup->setValue(0); + adjusterChanged(sup, 0); } void Wavelet::neutralchPressed() diff --git a/rtgui/wavelet.h b/rtgui/wavelet.h index c6a0a6b85..6daabcd67 100644 --- a/rtgui/wavelet.h +++ b/rtgui/wavelet.h @@ -141,6 +141,8 @@ private: void updatewavLabel (); void wavChanged(double nlevel) override; void ushamethodChanged(); + void updateGUI(); + void updateGUImaxlev(); void HSmethodUpdateUI(); void CHmethodUpdateUI(); diff --git a/tools/osx/macosx_bundle.sh b/tools/osx/macosx_bundle.sh index bc1a2dc3b..e3dc61857 100644 --- a/tools/osx/macosx_bundle.sh +++ b/tools/osx/macosx_bundle.sh @@ -106,7 +106,7 @@ __EOS__ minimum_macos_version=${MINIMUM_SYSTEM_VERSION} -# Retreive cached values from cmake +# Retrieve cached values from cmake #In: LOCAL_PREFIX:STRING=/opt #Out: /opt