From 952feb48b2a46b4eebbcdff477bb8c7892303150 Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 4 Jul 2012 13:09:50 -0400 Subject: [PATCH] Merge with e482a17a8e1359e90e1d844d268bf005bfac1c7a --- COMPILE.txt | 247 ++++++++++++++++++----------- rtdata/languages/Francais | 5 +- rtdata/languages/default | 5 +- rtengine/PF_correct_RT.cc | 15 +- rtengine/alignedbuffer.h | 47 +++++- rtengine/dcraw.cc | 2 +- rtengine/dcraw.patch | 17 +- rtengine/dcrop.cc | 2 +- rtengine/gauss.h | 73 ++++++--- rtengine/imagefloat.cc | 15 +- rtengine/improccoordinator.cc | 26 ++- rtengine/improccoordinator.h | 2 +- rtengine/impulse_denoise.h | 15 +- rtengine/iplab2rgb.cc | 7 +- rtengine/ipsharpen.cc | 22 ++- rtengine/ipvibrance.cc | 71 +++++---- rtengine/shmap.cc | 23 ++- rtengine/simpleprocess.cc | 3 +- rtgui/rtwindow.cc | 1 + rtgui/thresholdadjuster.cc | 36 ++++- rtgui/thresholdadjuster.h | 23 ++- rtgui/thresholdselector.cc | 290 ++++++++++++++++++++++++---------- rtgui/thresholdselector.h | 93 +++++++++-- rtgui/vibrance.cc | 66 ++++++-- rtgui/vibrance.h | 5 +- 25 files changed, 790 insertions(+), 321 deletions(-) diff --git a/COMPILE.txt b/COMPILE.txt index 845a07427..026e651e2 100644 --- a/COMPILE.txt +++ b/COMPILE.txt @@ -1,9 +1,17 @@ If you have problems with the compilation, please ask on the appropriate -RawTherapee forum : http://www.rawtherapee.com/forum/viewforum.php?f=10 +RawTherapee forum: http://www.rawtherapee.com/forum/viewforum.php?f=10 -Preamble +PREAMBLE -------- + Some commands span multiple lines, each line of such a command except for the + last one will end in a backslash character, "\". + When copying code, be sure to copy all lines from the first one that ends in \ + till the last line that does not end in a \ character. + + Some parts of code contain . Replace these with whatever + value is needed, e.g. + CMake is actually used to create the makefiles (scripts that handle the build process) or specific Integrated Developement Environement (IDE) projects. It is designed to find all necessary compilation flags for your specific machine, @@ -28,18 +36,20 @@ Preamble Note for developers that handle an automated build system: Mercurial is required in order to produce the build information file, but your build system will certainly only use the source code without any access to a Mercurial - repository. To generate the needed information, run the sript - ./tools/generateReleaseInfo. After that you can delete .hg* and continue with - the normal compile instructions without the need for mercurial. + repository. To generate the needed information, run the script: + ./tools/generateReleaseInfo + After that, you can delete .hg* (the example below uses "-X *.hg*" to exclude + those files and folders instead of deleting them - the end result is the same) + and continue with the normal compile instructions without the need for + mercurial. In short, once you cd into your clone of the RawTherapee source code - repository: + repository, using version "4.0.10" as an example: 1. Check out the desired hg tag: hg update "4.0.10" 2. Run tools/generateReleaseInfo to generate ReleaseInfo.cmake: - ./tools/generateReleaseInfo - 3. Delete the mercurial stuff: rm -rvf .hg* - 4. The repository is now ready to be made into a public tarball: hg archive - "rawtherapee-4.0.10.tar" -X ".hg*" && xz -z -9e -T 8 "rawtherapee-4.0.10.tar" + ./tools/generateReleaseInfo + 3. The repository is now ready to be made into a public tarball: + ./tools/generateSourceTarball The build system especially encourages to perform out-of-source builds. This means that the CMake project is generated in a folder which is not in the @@ -55,11 +65,48 @@ Preamble configure_file. Please keep this in mind when altering the CMake files. +DEPENDENCIES +------------ -Windows + PACKAGE URL + BZIP2 bzip2>-1.0.4 http://www.bzip.org/ + EXIV2 exiv2>=0.19 http://www.exiv2.org/ + EXPAT expat>=2.1.0 http://expat.sourceforge.net/ + GLIB2 glib-2.0>=2.16 http://www.gtk.org/ + GLIBMM glibmm-2.4>=2.16 http://www.gtkmm.org + GTK+ gtk+-2.0>=2.12 http://www.gtk.org/ + GTKMM gtkmm-2.4>=2.12 http://www.gtkmm.org + JPEG libjpeg>=6b http://libjpeg-turbo.virtualgl.org/ + http://jpegclub.org/ + http://www.ijg.org/ + LCMS2 lcms>=2.0a http://www.littlecms.com/ + LIBIPTCDATA libiptcdata>=1.0.2 http://libiptcdata.sourceforge.net + PNG libpng>=1.2.44 http://www.libpng.org/ + SIGC sigc++-2.0 http://libsigc.sourceforge.net/ + TIFF libtiff>=3.9.4 http://www.remotesensing.org/libtiff/ + ZLIB zlib>=1.2.3-r1 http://www.zlib.net/ + + +WINDOWS ------- - The toolchain: + OPTIONAL: + - TortoiseHG + You can make your life a little easier by using a graphical client for + working with Mercurial. One such free and open-soure cross-platform + client is TortoiseHG, you can get it from: + http://tortoisehg.bitbucket.org/ + + - Weird compressed archive formats? + You might need to unpack archives in formats which Windows does not + handle by default. That's no problem, there are a few free programs out + there which handle many more formats than Windows does, and faster too. + We recommend: + http://www.7-zip.org/ + http://peazip.sourceforge.net/ + + + THE TOOLCHAIN: There are two methods of compiling RawTherapee in Windows, and they each rely on different and common packages. They will be referred to as METHOD 1 @@ -67,9 +114,8 @@ Windows methods explained later in this document. The rest of this document assumes that you've installed MinGW, MSYS and - gtkmm respectively to "C:\mingw", "C:\msys" and "C:\gtkmm". This tutorial - assume that you've installed the packages in paths that DO NOT CONTAIN - SPACES. + gtkmm respectively to "C:\mingw", "C:\msys" and "C:\gtkmm". These packages + must be installed in paths that DO NOT CONTAIN SPACES. METHOD 1 - MinGW64-TDM (Bundle or On-Demand installer from @@ -100,7 +146,7 @@ Windows - for 64bit: http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/ - Additional steps: + ADDITIONAL STEPS: When all the packages of your preferred method are installed: - set the GTKMM_BASEPATH user or system environment variable to the installation directory of gtkmm (the gtkmm installer can do it for you). @@ -137,29 +183,31 @@ Windows quotes). Specifying the processor frequency isof no use. - - Dependancies: + DEPENDENCIES: - RawTherapee relies on libraries that may not be part of the gtkmm or MinGW - packages. There are two ways of installing these libraries. + RawTherapee depends on libraries which are not part of the gtkmm or MinGW + packages. There are two ways of installing these libraries: - The simple way: - For your convenience, these libraries has been precompiled and are + THE SIMPLE WAY: + For your convenience, most of the required libraries has been precompiled and are downloadable at: http://www.rawtherapee.com/releases_head/windows/dependencies_for_creating_builds/ - If you don't find a suitable package for your toolchain, take the DIY way - or ask a developer to build them for you. Unpack the contents of this - archive to the base installation dir of MinGW, and everything should work - fine. + Unpack the contents of these archives to the base installation dir of + MinGW. + + NOTE: those archives contain most but not all of the required + libraries. See the dependencies list at the beginning of the document, + find the missing ones (such as LCMS2 and Expat) and see below for + instructions on where to download them from and how to install them. - The "Do It Yourself" way: + THE "DO IT YOURSELF" WAY: The MSYS package is required to build the libraries. See above for the download link. Before building them, check if they are already installed in your gtkmm and MinGW directories. There shouldn't be two versions of the same library in different places. Open an MSYS console, that will create a Linux environment, and compile - the dependancies in the following order: + the dependencies in the following order: zlib: If you have Gtkmm-2.22 installed, you should skip this zlib section. - Download: http://zlib.net/ @@ -222,14 +270,18 @@ Windows make make install - lcms: - - Download lcms1 for RawTherapee-3.0 or lcms2 for RawTherapee-3.1 and - newer http://sourceforge.net/projects/lcms/files/ + LCMS: + - Download LCMS2 for RawTherapee-3.1 and newer (or LCMS1 for RawTherapee-3.0) + http://sourceforge.net/projects/lcms/files/ - How to build (suitable for both version): ./configure --prefix=/mingw make make install + Expat: + - Download: http://expat.sourceforge.net/ + - Install + IMPORTANT: Make sure that the lcms.pc and libiptcdata.pc files located in the @@ -247,8 +299,8 @@ Windows -- Found PNG: C:/gtkmm/lib/libpng.lib -- Found TIFF: C:/mingw/lib/libtiff.dll.a - Building RT: - + BUILDING RT: + METHOD 1: Requirements: - MinGW + MSYS @@ -262,88 +314,96 @@ Windows mkdir build cd build - Type: - cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -Ccmo.txt + cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -Ccmo.txt \ + - Type: make install - You'll find the compiled program in the subdirectory named like the value of CMAKE_BUILD_TYPE ("Release" in this example). - METHOD 2: Requirements: + METHOD 2: + Requirements: - MinGW-TDM: https://sourceforge.net/projects/tdm-gcc/files/TDM-GCC%20Installer/ - CMake: http://www.cmake.org/cmake/resources/software.html - Mercurial: http://mercurial.selenic.com/wiki/WindowsInstall - gtkmm: http://ftp.se.debian.org/pub/gnome/binaries/win32/gtkmm/2.22/ - + Compile: - Start a standard DOS command prompt (WIN+R > cmd), - Enter a new empty folder outside RawTherapee's source directory: cd /C/Absolute/Path/To/RawTherapee_sourceTree mkdir build cd build - - If you want to upload a build, you should set some additional - information about your processor. There are two possibilities: - 1. You pick up a target processor from "ProcessorTargets.cmake". All - you have to do is set "-D PROC_TARGET_NUMBER:STRING=number" in - cmake's command line to the desired target number. If you choose - the 'native' solution, you have to set the processor label manually - by setting "-D PROC_LABEL:STRING=procLabel" in cmake's command - line. Please provide a short name, like "core i5" or "athlon64" - (without double quotes). Processor frequency is of no use. - 2. You don't need specific processor flags, so you'll let - PROC_TARGET_NUMBER default to 0, but you still have to set the - PROC_LABEL parameter (see point 1 righ above). - + If you want to upload a build, you should set some additional + information about your processor. There are two possibilities: + 1. You pick up a target processor from "ProcessorTargets.cmake". All + you have to do is set "-D PROC_TARGET_NUMBER:STRING=number" in + cmake's command line to the desired target number. If you choose + the 'native' solution, you have to set the processor label manually + by setting "-D PROC_LABEL:STRING=procLabel" in cmake's command + line. Please provide a short name, like "core i5" or "athlon64" + (without double quotes). Processor frequency is of no use. + 2. You don't need specific processor flags, so you'll let + PROC_TARGET_NUMBER default to 0, but you still have to set the + PROC_LABEL parameter (see point 1 righ above). - Type: mingw32-make.exe install - You'll find the compiled program in the subdirectory named like the value of CMAKE_BUILD_TYPE ("Release" in this example). -Linux +LINUX ----- - Requirements: + DEPENDENCIES: + See the list of dependencies at the beginning of this document. + + In Ubuntu/Debian the requirements can be installed by running: + sudo apt-get install build-essential cmake libbz2-dev libexiv2-dev \ + libexpat1-dev libglib2.0-dev libglibmm-2.4-dev libgtk2.0-dev \ + libgtkmm-2.4-dev libiptcdata-dev libjpeg8-dev liblcms2-dev libpng-dev \ + libsigc++-2.0-dev libtiff-dev mercurial zlib1g-dev + + In Gentoo, run: + sudo emerge -uva app-arch/bzip2 media-gfx/exiv2 dev-libs/expat \ + dev-libs/glib dev-cpp/glibmm x11-libs/gtk+ dev-cpp/gtkmm \ + media-libs/libjpeg-turbo media-libs/lcms media-libs/libiptcdata \ + media-libs/libpng dev-libs/libsigc++ media-libs/tiff sys-libs/zlib + + In Arch, run: + sudo pacman -S bzip2 exiv2 expat glib2 glibmm gtk gtkmm lcms2 \ + libiptcdata libjpeg-turbo libpng libsigc++ libtiff zlib + + COMPILE: + The Automatic Way: + The easiest way compile RawTherapee is by using the buildRT script, read + all about it in the forum: + http://rawtherapee.com/forum/viewtopic.php?p=22213#p22213 + + The Manual Way: + - Create and enter a new empty folder outside RawTherapee's source + directory: + mkdir ~/rt-build + cd ~/rt-build + - Type: + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./release \ + -DBINDIR=. -DDATADIR=. -DLIBDIR=. + Developers who want to provide a public build have to set the PROC_LABEL + to their processor name, e.g.: + cmake -DPROC_LABEL:STRING=athlon64 -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=./release -DBINDIR=. -DDATADIR=. -DLIBDIR=. \ + + - Type: + make install + - You'll find the compiled program in the subdirectory named like the + value of CMAKE_BUILD_TYPE ("Release" in this example). You can copy it + anywhere you want. By changing the cmake flags, you can change where + the release will compile to. By removing all flags it should go to the + standard system install location. + + You're done. - PACKAGE GENTOO DEBIAN/UBUNTU URL - GTK+ gtk+-2.0>=2.12 x11-libs/gtk+ libgtk2.0-dev http://www.gtk.org/ - GLIB2 glib-2.0>=2.16 dev-libs/glib libglib2.0-dev http://www.gtk.org/ - GLIBMM glibmm-2.4>=2.16 dev-cpp/glibmm libglibmm-2.4-dev http://www.gtkmm.org - GTKMM gtkmm-2.4>=2.12 dev-cpp/gtkmm libgtkmm-2.4-dev http://www.gtkmm.org - SIGC sigc++-2.0 dev-libs/libsigc++ libsigc++-2.0-dev http://libsigc.sourceforge.net/ - LIBIPTCDATA libiptcdata>=1.0.2 media-libs/libiptcdata libiptcdata-dev http://libiptcdata.sourceforge.net - LCMS2 lcms>=2.0a media-libs/lcms liblcms2-dev http://www.littlecms.com/ - JPEG libjpeg>=6b media-libs/jpeg libjpeg-dev http://jpegclub.org/ http://www.ijg.org/ - TIFF libtiff>=3.9.4 media-libs/tiff libtiff-dev http://www.remotesensing.org/libtiff/ - PNG libpng>=1.2.44 media-libs/libpng libpng-dev http://www.libpng.org/ - BZIP2 bzip2>-1.0.4 app-arch/bzip2 libbz2-dev http://www.bzip.org/ - ZLIB zlib>=1.2.3-r1 sys-libs/zlib zlib1g-dev http://www.zlib.net/ - EXIV2 exiv2>=0.19 media-gfx/exiv2 libexiv2-dev http://www.exiv2.org/ - EXPAT expat>=2.1.0 dev-libs/expat libexpat-dev http://expat.sourceforge.net/ - On Ubuntu/Debian the requirements can be installed by running: - sudo apt-get install build-essential cmake libbz2-dev libexiv2-dev libexpat1-dev libglib2.0-dev libglibmm-2.4-dev libgtk2.0-dev libgtkmm-2.4-dev libiptcdata-dev libjpeg8-dev liblcms2-dev libpng-dev libsigc++-2.0-dev libtiff-dev mercurial zlib1g-dev - - Compile: - - Create and enter a new empty folder outside RawTherapee's source directory: - mkdir ~/rt-build - cd ~/rt-build - - Type: - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./release -DBINDIR=. -DDATADIR=. -DLIBDIR=. - Developers who want to provide a public build have to set the PROC_LABEL - to their processor name, e.g.: - cmake -DPROC_LABEL:STRING=athlon64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./release -DBINDIR=. -DDATADIR=. -DLIBDIR=. - - - Type: - make install - - You'll find the compiled program in the subdirectory named like the value - of CMAKE_BUILD_TYPE ("Release" in this example). You can copy it anywhere - you want. - - By changing the cmake flags, you can change where the release will compile - to. By removing all flags it should go to the standard system install - location. - Using Eclipse under Linux: Eclipse mercurial plugin: Click "Help > Install new Software". @@ -382,7 +442,7 @@ Linux OS X ---- - Requirements: + REQUIREMENTS: - XCode Development Tools (you only need a subset of these, but it is probably easier to just install all of them) - MacPorts @@ -402,10 +462,11 @@ OS X - If you want to try OpenMP builds, run: sudo port install gcc45 - Compile: + COMPILE: - Enter the root directory of the RawTherapee source tree - To enable OpenMP, assuming you have installed gcc45), type: - cmake -D CMAKE_C_COMPILER=gcc-mp-4.5 -D CMAKE_CXX_COMPILER=g++-mp-4.5 + cmake -D CMAKE_C_COMPILER=gcc-mp-4.5 -D CMAKE_CXX_COMPILER=g++-mp-4.5 \ + OR to disable OpenMP and use the default compiler, type: cmake -D OPTION_OMP=false diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 450bd3ac6..dfa62c3df 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -1064,7 +1064,10 @@ TP_VIBRANCE_LABEL;Vibrance TP_VIBRANCE_PASTELS;Tons pastels TP_VIBRANCE_PASTSATTOG;Lier Pastels et Saturés TP_VIBRANCE_PROTECTSKINS;Protéger les tons chairs -TP_VIBRANCE_PSTHRESHOLD;Seuil différentiel entre Pastels/Saturés +TP_VIBRANCE_PSTHRESHOLD;Seuil entre tons pastels/saturés +TP_VIBRANCE_PSTHRESHOLD_SATTHRESH;Seuil de saturation +TP_VIBRANCE_PSTHRESHOLD_WEIGTHING;Pondération de la transition pastels/saturés +TP_VIBRANCE_PSTHRESHOLD_TOOLTIP;L'axe vertical represente les tons pastel en bas et le tons saturés en haut.\nL'axe horizontal represente l'échelle de la saturation. TP_VIBRANCE_SATURATED;Tons saturés TP_VIGNETTING_AMOUNT;Quantité TP_VIGNETTING_CENTER;Centre diff --git a/rtdata/languages/default b/rtdata/languages/default index aef051f2b..644002bfd 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1077,7 +1077,10 @@ TP_VIBRANCE_LABEL;Vibrance TP_VIBRANCE_PASTELS;Pastel tones TP_VIBRANCE_PASTSATTOG;Link pastel and saturated tones TP_VIBRANCE_PROTECTSKINS;Protect skin tones -TP_VIBRANCE_PSTHRESHOLD;Pastel/Saturated tone differential threshold +TP_VIBRANCE_PSTHRESHOLD;Pastel/saturated tones threshold +TP_VIBRANCE_PSTHRESHOLD_SATTHRESH;Saturation threshold +TP_VIBRANCE_PSTHRESHOLD_WEIGTHING;Pastel/saturated transition's weighting +TP_VIBRANCE_PSTHRESHOLD_TOOLTIP;The vertical axis represents pastel tones at the bottom and saturated tones at the top.\nThe horizontal axis represents the saturation range. TP_VIBRANCE_SATURATED;Saturated tones TP_VIGNETTING_AMOUNT;Amount TP_VIGNETTING_CENTER;Center diff --git a/rtengine/PF_correct_RT.cc b/rtengine/PF_correct_RT.cc index 3553609eb..ed8f623c7 100644 --- a/rtengine/PF_correct_RT.cc +++ b/rtengine/PF_correct_RT.cc @@ -56,16 +56,15 @@ void ImProcFunctions::PF_correct_RT(LabImage * src, LabImage * dst, double radiu #pragma omp parallel #endif { - AlignedBuffer* buffer = new AlignedBuffer (max(src->W,src->H)); - gaussHorizontal (src->a, tmp1->a, buffer, src->W, src->H, radius, multiThread); - gaussHorizontal (src->b, tmp1->b, buffer, src->W, src->H, radius, multiThread); - gaussVertical (tmp1->a, tmp1->a, buffer, src->W, src->H, radius, multiThread); - gaussVertical (tmp1->b, tmp1->b, buffer, src->W, src->H, radius, multiThread); + AlignedBufferMP buffer(max(src->W,src->H)); - //gaussHorizontal (src->L, tmp1->L, buffer, src->W, src->H, radius, multiThread); - //gaussVertical (tmp1->L, tmp1->L, buffer, src->W, src->H, radius, multiThread); + gaussHorizontal (src->a, tmp1->a, buffer, src->W, src->H, radius); + gaussHorizontal (src->b, tmp1->b, buffer, src->W, src->H, radius); + gaussVertical (tmp1->a, tmp1->a, buffer, src->W, src->H, radius); + gaussVertical (tmp1->b, tmp1->b, buffer, src->W, src->H, radius); - delete buffer; + gaussHorizontal (src->L, tmp1->L, buffer, src->W, src->H, radius); + gaussVertical (tmp1->L, tmp1->L, buffer, src->W, src->H, radius); } //#ifdef _OPENMP diff --git a/rtengine/alignedbuffer.h b/rtengine/alignedbuffer.h index 430b0659b..057b654ad 100644 --- a/rtengine/alignedbuffer.h +++ b/rtengine/alignedbuffer.h @@ -1,7 +1,7 @@ /* * This file is part of RawTherapee. * - * Copyright (c) 2004-2010 Gabor Horvath +* Copyright (c) 2004-2012 Gabor Horvath , Oliver Duis * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,10 @@ #ifndef _ALIGNEDBUFFER_ #define _ALIGNEDBUFFER_ #include +#include +#include +// Aligned buffer that should be faster template class AlignedBuffer { private: @@ -27,10 +30,12 @@ template class AlignedBuffer { public: T* data ; + bool inUse; AlignedBuffer (size_t size, size_t align=16) { real = new T[size+2*align]; data = (T*)((uintptr_t)real + (align-((uintptr_t)real)%align)); + inUse=true; } ~AlignedBuffer () { @@ -38,4 +43,44 @@ template class AlignedBuffer { } }; +// Multi processor version, use with OpenMP +template class AlignedBufferMP { +private: + Glib::Mutex mtx; + std::vector*> buffers; + size_t size; + +public: + AlignedBufferMP(size_t sizeP) { + size=sizeP; + } + + ~AlignedBufferMP() { + for (int i=0;i* acquire() { + Glib::Mutex::Lock lock(mtx); + + // Find available buffer + for (int i;iinUse) { + buffers[i]->inUse=true; + return buffers[i]; + } + } + + // Add new buffer if nothing is free + AlignedBuffer* buffer=new AlignedBuffer(size); + buffers.push_back(buffer); + + return buffer; + } + + void release(AlignedBuffer* buffer) { + Glib::Mutex::Lock lock(mtx); + + buffer->inUse=false; + } +}; #endif diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 5fb5a0e35..d2a7591b1 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -7403,7 +7403,7 @@ canon_cr2: !strcmp(model,"D7000")) { width -= 44; } else if (!strcmp(model,"D3200") || - !strcmp(model,"D800")) { + !strcmp(model,"D800") || !strcmp(model,"D800E") ) { width -= 46; } else if (!strcmp(model,"D4")) { width -= 52; diff --git a/rtengine/dcraw.patch b/rtengine/dcraw.patch index 4676dca33..586e84be3 100644 --- a/rtengine/dcraw.patch +++ b/rtengine/dcraw.patch @@ -1,5 +1,5 @@ ---- C:/GCC/RT/RTSrc/rtengine/dcraw.c Fri Jun 22 14:06:35 2012 -+++ C:/GCC/RT/RTSrc/rtengine/dcraw.cc Fri Jun 22 18:20:12 2012 +--- C:/GCC/RT/RTSrc/rtengine/dcraw.c Tue Jun 26 23:00:34 2012 ++++ C:/GCC/RT/RTSrc/rtengine/dcraw.cc Fri Jun 29 23:09:38 2012 @@ -1,5 +1,17 @@ +/*RT*/#include +/*RT*/#include @@ -1427,6 +1427,19 @@ cam_mul[0] *= 256/527.0; cam_mul[2] *= 256/317.0; } else if (!strcmp(model,"D1X")) { +@@ -7786,11 +7401,11 @@ + width -= 42; + } else if (!strcmp(model,"D5100") || + !strcmp(model,"D7000")) { + width -= 44; + } else if (!strcmp(model,"D3200") || +- !strcmp(model,"D800")) { ++ !strcmp(model,"D800") || !strcmp(model,"D800E") ) { + width -= 46; + } else if (!strcmp(model,"D4")) { + width -= 52; + left_margin = 2; + } else if (!strncmp(model,"D40",3) || @@ -8681,198 +8296,11 @@ quit: cmsCloseProfile (hInProfile); diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 12c027357..01330dff6 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -79,7 +79,7 @@ void Crop::update (int todo) { else needsinitupdate = setCropSizes (wx, wy, ww, wh, ws, true); // this set skip=ws // it something has been reallocated, all processing steps have to be performed - if (needsinitupdate) + if (needsinitupdate || (todo & M_HIGHQUAL)) todo = ALL; baseCrop = origCrop; diff --git a/rtengine/gauss.h b/rtengine/gauss.h index 116b49f05..bf9cc271d 100644 --- a/rtengine/gauss.h +++ b/rtengine/gauss.h @@ -29,42 +29,50 @@ // classical filtering if the support window is small: -template void gaussHorizontal3 (T** src, T** dst, T* buffer, int W, int H, const float c0, const float c1, bool multiThread) { - +template void gaussHorizontal3 (T** src, T** dst, AlignedBufferMP &buffer, int W, int H, const float c0, const float c1) { #ifdef _OPENMP #pragma omp for #endif for (int i=0; i* pBuf = buffer.acquire(); + T* temp=(T*)pBuf->data; + for (int j=1; j void gaussVertical3 (T** src, T** dst, T* buffer, int W, int H, const float c0, const float c1, bool multiThread) { +template void gaussVertical3 (T** src, T** dst, AlignedBufferMP &buffer, int W, int H, const float c0, const float c1) { - //#pragma omp parallel for if (multiThread) #ifdef _OPENMP #pragma omp for #endif for (int i=0; i* pBuf = buffer.acquire(); + T* temp = (T*)pBuf->data; + for (int j = 1; j void gaussHorizontal (T** src, T** dst, AlignedBuffer* buffer, int W, int H, double sigma, bool multiThread) { +template void gaussHorizontal (T** src, T** dst, AlignedBufferMP &buffer, int W, int H, double sigma) { if (sigma<0.25) { // dont perform filtering @@ -81,7 +89,7 @@ template void gaussHorizontal (T** src, T** dst, AlignedBuffer* double csum = 2.0 * c1 + 1.0; c1 /= csum; double c0 = 1.0 / csum; - gaussHorizontal3 (src, dst, (T*)(buffer->data), W, H, c0, c1, multiThread); + gaussHorizontal3 (src, dst, buffer, W, H, c0, c1); return; } @@ -113,10 +121,12 @@ template void gaussHorizontal (T** src, T** dst, AlignedBuffer* for (int i=0; i<3; i++) for (int j=0; j<3; j++) M[i][j] /= (1.0+b1-b2+b3)*(1.0+b2+(b1-b3)*b3); - // if (multiThread) + #pragma omp for for (int i=0; idata; + AlignedBuffer* pBuf = buffer.acquire(); + double* temp2 = pBuf->data; + temp2[0] = B * src[i][0] + b1*src[i][0] + b2*src[i][0] + b3*src[i][0]; temp2[1] = B * src[i][1] + b1*temp2[0] + b2*src[i][0] + b3*src[i][0]; temp2[2] = B * src[i][2] + b1*temp2[1] + b2*temp2[0] + b3*src[i][0]; @@ -136,10 +146,13 @@ template void gaussHorizontal (T** src, T** dst, AlignedBuffer* temp2[j] = B * temp2[j] + b1*temp2[j+1] + b2*temp2[j+2] + b3*temp2[j+3]; for (int j=0; j void gaussVertical (T** src, T** dst, AlignedBuffer* buffer, int W, int H, double sigma, bool multiThread) { +} + +template void gaussVertical (T** src, T** dst, AlignedBufferMP &buffer, int W, int H, double sigma) { if (sigma<0.25) { // dont perform filtering @@ -156,7 +169,7 @@ template void gaussVertical (T** src, T** dst, AlignedBuffer* b double csum = 2.0 * c1 + 1.0; c1 /= csum; double c0 = 1.0 / csum; - gaussVertical3 (src, dst, (T*)(buffer->data), W, H, c0, c1, multiThread); + gaussVertical3 (src, dst, buffer, W, H, c0, c1); return; } @@ -192,7 +205,8 @@ template void gaussVertical (T** src, T** dst, AlignedBuffer* b #pragma omp for #endif for (int i=0; idata; + AlignedBuffer* pBuf = buffer.acquire(); + double* temp2 = pBuf->data; temp2[0] = B * src[0][i] + b1*src[0][i] + b2*src[0][i] + b3*src[0][i]; temp2[1] = B * src[1][i] + b1*temp2[0] + b2*src[0][i] + b3*src[0][i]; temp2[2] = B * src[2][i] + b1*temp2[1] + b2*temp2[0] + b3*src[0][i]; @@ -221,7 +235,7 @@ template void gaussVertical (T** src, T** dst, AlignedBuffer* b //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -template void gaussDerivH (T** src, T** dst, AlignedBuffer* buffer, int W, int H, double sigma, bool multiThread) { +template void gaussDerivH (T** src, T** dst, AlignedBufferMP &buffer, int W, int H, double sigma) { if (sigma<0.6) { @@ -230,13 +244,17 @@ template void gaussDerivH (T** src, T** dst, AlignedBuffer* buf #pragma omp for #endif for (int i=0; idata; + AlignedBuffer* pBuf = buffer.acquire(); + T* temp = (T*)pBuf->data; + // double* temp = buffer->data;// replaced by 2 lines above for (int j=1; j void gaussDerivH (T** src, T** dst, AlignedBuffer* buf for (int i=0; i<3; i++) for (int j=0; j<3; j++) M[i][j] /= (1.0+b1-b2+b3)*(1.0+b2+(b1-b3)*b3); - // if (multiThread) + #pragma omp for for (int i=0; idata; + AlignedBuffer* pBuf = buffer.acquire(); + T* temp2 = (T*)pBuf->data; + // double* temp2 = buffer->data;// replaced by 2 lines above double src0 = (src[i][1]-src[i][0]); @@ -300,12 +320,14 @@ template void gaussDerivH (T** src, T** dst, AlignedBuffer* buf temp2[j] = B * temp2[j] + b1*temp2[j+1] + b2*temp2[j+2] + b3*temp2[j+3]; for (int j=0; j void gaussDerivV (T** src, T** dst, AlignedBuffer* buffer, int W, int H, double sigma, bool multiThread) { +template void gaussDerivV (T** src, T** dst, AlignedBufferMP &buffer, int W, int H, double sigma) { if (sigma<0.6) { // apply symmetric derivative @@ -313,12 +335,17 @@ template void gaussDerivV (T** src, T** dst, AlignedBuffer* buf #pragma omp for #endif for (int j=0; jdata; + AlignedBuffer* pBuf = buffer.acquire(); + T* temp = (T*)pBuf->data; + // double* temp = buffer->data;// replaced by 2 lines above for (int i = 1; i void gaussDerivV (T** src, T** dst, AlignedBuffer* buf #pragma omp for #endif for (int i=0; idata; + AlignedBuffer* pBuf = buffer.acquire(); + T* temp2 = (T*)pBuf->data; + // double* temp2 = buffer->data;// replaced by 2 lines above double src0 = 0.5*(src[1][i]-src[0][i]); @@ -384,6 +413,8 @@ template void gaussDerivV (T** src, T** dst, AlignedBuffer* buf for (int j=0; j bufMP(width*3); + // LittleCMS cannot parallize planar setups // so build temporary buffers to allow multi processor execution #pragma omp parallel for for (int y=0; y* pBuf=bufMP.acquire(); + + float *p=pBuf->data, *pR=r[y], *pG=g[y], *pB=b[y]; for (int x=0; xdata, pBuf->data, width); - p=buffer; pR=r[y]; pG=g[y]; pB=b[y]; + p=pBuf->data; pR=r[y]; pG=g[y]; pB=b[y]; for (int x=0; xIsrgbSourceModified()) - || (!params.hlrecovery.enabled && params.hlrecovery.method=="Color" && imgsrc->IsrgbSourceModified())){ + if ( (todo & M_RAW) + || (!highDetailComputed && highDetailNeeded) + || ( params.hlrecovery.enabled && params.hlrecovery.method!="Color" && imgsrc->IsrgbSourceModified()) + || (!params.hlrecovery.enabled && params.hlrecovery.method=="Color" && imgsrc->IsrgbSourceModified())) + { - if (settings->verbose) printf("Demosaic %s\n",rp.dmethod.c_str()); + if (settings->verbose) printf("Demosaic %s\n",rp.dmethod.c_str()); + //TODO - denoise branch - is this code for WB params still necessary? currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.method); if (params.wb.method=="Camera") currWB = imgsrc->getWB (); @@ -156,7 +158,18 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { } params.wb.temperature = currWB.getTemp (); params.wb.green = currWB.getGreen (); + imgsrc->demosaic( rp ); + + if (highDetailNeeded) { + highDetailComputed = true; + if (params.hlrecovery.enabled && params.hlrecovery.method=="Color") { + todo |= M_INIT; + } + } + else + highDetailComputed = false; + LUTu aehist; int aehistcompr; double clip; @@ -165,7 +178,6 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { imgsrc->getAutoExpHistogram (aehist, aehistcompr); ipf.getAutoExp (aehist, aehistcompr, imgsrc->getDefGain(), clip, params.dirpyrDenoise.expcomp, brightness, contrast, black, hlcompr, hlcomprthresh); } - lastHighDetail=highDetailNeeded; if (todo & (M_INIT|M_LINDENOISE)) { diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index de19ad737..872f27b6d 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -60,7 +60,7 @@ class ImProcCoordinator : public StagedImageProcessor { ImProcFunctions ipf; int scale; - bool lastHighDetail; // was the last update running in high detail? + bool highDetailComputed; bool allocated; void freeAll (); diff --git a/rtengine/impulse_denoise.h b/rtengine/impulse_denoise.h index 06480a516..0ae7e5b58 100644 --- a/rtengine/impulse_denoise.h +++ b/rtengine/impulse_denoise.h @@ -62,14 +62,15 @@ void ImProcFunctions::impulse_nr (LabImage* lab, double thresh) { int i1, j1; //rangeblur (lab->L, lpf, impish /*used as buffer here*/, width, height, thresh, false); + #ifdef _OPENMP + #pragma omp parallel + #endif + { + AlignedBufferMP buffer(max(width,height)); - AlignedBuffer* buffer = new AlignedBuffer (max(width,height)); - - gaussHorizontal (lab->L, lpf, buffer, width, height, MAX(2.0,thresh-1.0), false /*multiThread*/); - gaussVertical (lpf, lpf, buffer, width, height, MAX(2.0,thresh-1.0), false); - - delete buffer; - + gaussHorizontal (lab->L, lpf, buffer, width, height, max(2.0,thresh-1.0)); + gaussVertical (lpf, lpf, buffer, width, height, max(2.0,thresh-1.0)); + } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index 8c89a65b5..fbdad662b 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -26,6 +26,7 @@ #include "../rtgui/options.h" #include "settings.h" #include "curves.h" +#include "alignedbuffer.h" #ifdef _OPENMP @@ -49,12 +50,14 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) { //gamutmap(lab); if (monitorTransform) { + AlignedBufferMP bufferMP(3*lab->W); // cmsDoTransform is relatively expensive #pragma omp parallel for for (int i=0; iH; i++) { // pre-conversion to integer, since the output is 8 bit anyway, but LCMS is MUCH faster not converting from float - unsigned short buffer[3*lab->W]; + AlignedBuffer* pBuf=bufferMP.acquire(); + unsigned short * buffer=pBuf->data; const int ix = i * 3 * lab->W; int iy = 0; @@ -81,6 +84,8 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) { } cmsDoTransform (monitorTransform, buffer, image->data + ix, lab->W); + + bufferMP.release(pBuf); } } else { diff --git a/rtengine/ipsharpen.cc b/rtengine/ipsharpen.cc index 0e00949c0..46600dbc2 100644 --- a/rtengine/ipsharpen.cc +++ b/rtengine/ipsharpen.cc @@ -80,15 +80,15 @@ void ImProcFunctions::deconvsharpening (LabImage* lab, float** b2) { #pragma omp parallel #endif { + AlignedBufferMP buffer(max(W,H)); - AlignedBuffer* buffer = new AlignedBuffer (max(W,H)); float damping = params->sharpening.deconvdamping / 5.0; bool needdamp = params->sharpening.deconvdamping > 0; for (int k=0; ksharpening.deconviter; k++) { // apply blur function (gaussian blur) - gaussHorizontal (tmpI, tmp, buffer, W, H, params->sharpening.deconvradius / scale, multiThread); - gaussVertical (tmp, tmp, buffer, W, H, params->sharpening.deconvradius / scale, multiThread); + gaussHorizontal (tmpI, tmp, buffer, W, H, params->sharpening.deconvradius / scale); + gaussVertical (tmp, tmp, buffer, W, H, params->sharpening.deconvradius / scale); if (!needdamp) { #ifdef _OPENMP @@ -102,8 +102,8 @@ void ImProcFunctions::deconvsharpening (LabImage* lab, float** b2) { else dcdamping (tmp, lab->L, damping, W, H); - gaussHorizontal (tmp, tmp, buffer, W, H, params->sharpening.deconvradius / scale, multiThread); - gaussVertical (tmp, tmp, buffer, W, H, params->sharpening.deconvradius / scale, multiThread); + gaussHorizontal (tmp, tmp, buffer, W, H, params->sharpening.deconvradius / scale); + gaussVertical (tmp, tmp, buffer, W, H, params->sharpening.deconvradius / scale); #ifdef _OPENMP #pragma omp for @@ -112,7 +112,6 @@ void ImProcFunctions::deconvsharpening (LabImage* lab, float** b2) { for (int j=0; jsharpening.deconvamount / 100.0; float p1 = 1.0 - p2; @@ -155,18 +154,17 @@ void ImProcFunctions::sharpening (LabImage* lab, float** b2) { { - AlignedBuffer* buffer = new AlignedBuffer (max(W,H)); + AlignedBufferMP buffer(max(W,H)); if (params->sharpening.edgesonly==false) { - gaussHorizontal (lab->L, b2, buffer, W, H, params->sharpening.radius / scale, multiThread); - gaussVertical (b2, b2, buffer, W, H, params->sharpening.radius / scale, multiThread); + gaussHorizontal (lab->L, b2, buffer, W, H, params->sharpening.radius / scale); + gaussVertical (b2, b2, buffer, W, H, params->sharpening.radius / scale); } else { bilateral (lab->L, (float**)b3, b2, W, H, params->sharpening.edges_radius / scale, params->sharpening.edges_tolerance, multiThread); - gaussHorizontal (b3, b2, buffer, W, H, params->sharpening.radius / scale, multiThread); - gaussVertical (b2, b2, buffer, W, H, params->sharpening.radius / scale, multiThread); + gaussHorizontal (b3, b2, buffer, W, H, params->sharpening.radius / scale); + gaussVertical (b2, b2, buffer, W, H, params->sharpening.radius / scale); } - delete buffer; float** base = lab->L; if (params->sharpening.edgesonly) diff --git a/rtengine/ipvibrance.cc b/rtengine/ipvibrance.cc index 3a27f6824..c3e773884 100644 --- a/rtengine/ipvibrance.cc +++ b/rtengine/ipvibrance.cc @@ -2005,10 +2005,17 @@ void ImProcFunctions::vibrance (LabImage* lab) { HH = new float[width*height]; */ + float chromaPastel = float(params->vibrance.pastels) / 100.0f; + float chromaSatur = float(params->vibrance.saturated) / 100.0f; + float limitpastelsatur = static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_TOPLEFT]) / 100.0f; + float transitionweighting = static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_BOTTOMLEFT]) / 100.0f; + + bool highlight = params->hlrecovery.enabled;//Get the value if "highlight reconstruction" is activated + #ifdef _DEBUG -#pragma omp parallel default(shared) reduction(+: negat, moreRGB, negsat ,moresat, Munspb, Munsry, Munsgy, Munsrp, depass) if (multiThread) +#pragma omp parallel default(shared) firstprivate(lab, width, chromaPastel, chromaSatur, highlight, limitpastelsatur, transitionweighting) reduction(+: negat, moreRGB, negsat ,moresat, Munspb, Munsry, Munsgy, Munsrp, depass) if (multiThread) #else -#pragma omp parallel default(shared) if (multiThread) +#pragma omp parallel default(shared) firstprivate(lab, width, chromaPastel, chromaSatur, highlight, limitpastelsatur, transitionweighting) if (multiThread) #endif { @@ -2017,9 +2024,6 @@ void ImProcFunctions::vibrance (LabImage* lab) { float fy,fx,fz,x_,y_,z_,Lprov,Lprov1,aprov1,bprov1,aprovn,bprovn,fxx,fyy,fzz,xx_,yy_,zz_; float saturation; TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params->icm.working); - float chromaPastel= (float) params->vibrance.pastels / 100.0f;// - float chromaSatur = (float) params->vibrance.saturated / 100.0f;// - bool highlight = params->hlrecovery.enabled;//Get the value if "highlight reconstruction" is activated //inverse matrix user select double wip[3][3] = { {wiprof[0][0],wiprof[0][1],wiprof[0][2]}, @@ -2030,26 +2034,16 @@ void ImProcFunctions::vibrance (LabImage* lab) { float satredu;//reduct sat in function of skin float sathue[5],sathue2[4];// adjust sat in function of hue float correctionHue; // Munsell's correction - float limitpastelsatur; - float limitpastelsaturbottom;//TS_TOPRIGHT int zone=0; bool allwaysingamut=true; - // sur la ligne ci-dessous, on a acces aux valeurs du seuil via le champs 'value' - // psthreshold est un seuil simple commencant en bas, ce qui signifie que seuls TS_BOTTOMLEFT et TS_TOPLEFT - // sont exploitables - limitpastelsaturbottom=static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_BOTTOMLEFT]) / 100.0f; - limitpastelsatur=static_cast(params->vibrance.psthreshold.value[ThresholdSelector::TS_TOPLEFT]) / 100.0f; - - if (limitpastelsatur < 0.07) limitpastelsatur=0.07; -// if (limitpastelsaturbottom < 0.07) limitpastelsaturbottom=0.07; + // Fitting limitpastelsatur into the real 0.07->1.0 range + limitpastelsatur = limitpastelsatur*0.93f + 0.07f; float p0,p1,p2;//adapt limit of pyramid to psThreshold float s0,s1,s2; float maxdp=(limitpastelsatur-0.07)/4.0; -// float maxdp=(limitpastelsaturbottom-0.07)/4.0; - float maxds=(1.0-limitpastelsatur)/4.0; p0=0.07+maxdp; p1=0.07+2.0*maxdp; @@ -2058,8 +2052,25 @@ void ImProcFunctions::vibrance (LabImage* lab) { s1=limitpastelsatur + 2.0*maxds; s2=limitpastelsatur + 3.0*maxds; + float chromamean=0.; + if(chromaPastel != chromaSatur){ + //if sliders pastels and saturated are different: transition with a double linear interpolation: between p2 and limitpastelsatur, and between limitpastelsatur and s0 + //modify the "mean" point in function of double threshold => differential transition + chromamean = maxdp * (chromaSatur-chromaPastel) / (s0-p2) + chromaPastel; + // move chromaMean up or down depending on transitionCtrl + if (transitionweighting > 0.0) { + float _chromamean = chromamean; + chromamean = (chromaSatur-chromamean) * transitionweighting + chromamean; + } + else if (transitionweighting < 0.0) { + float _chromamean = chromamean; + chromamean = (chromamean-chromaPastel) * transitionweighting + chromamean; + } + } + if (settings->verbose) printf("vibrance: p0=%1.2f p1=%1.2f p2=%1.2f s0=%1.2f s1=%1.2f s2=%1.2f\n", p0,p1,p2,s0,s1,s2); - if (settings->verbose) printf("vibrance: pastel=%f satur=%f limit= %1.2f\n",1.0+chromaPastel,1.0+chromaSatur, limitpastelsatur); + if (settings->verbose) printf("vibrance: pastel=%f satur=%f limit= %1.2f chromamean=%0.5f \n",1.0+chromaPastel,1.0+chromaSatur, limitpastelsatur, chromamean); + #pragma omp for schedule(dynamic, 10) for (int i=0; i differential transition - chromamean = (chromaSatur*limitpastelsatur + chromaPastel*limitpastelsaturbottom)/(limitpastelsaturbottom+limitpastelsatur); - chromaPastel_a = (chromaPastel-chromamean)/(p2-limitpastelsatur); - chromaPastel_b = chromaPastel-chromaPastel_a*p2; + if(saturation > p2 && saturation < limitpastelsatur) { - newchromaPastel = chromaPastel_a*saturation + chromaPastel_b; + float chromaPastel_a = (chromaPastel-chromamean)/(p2-limitpastelsatur); + float chromaPastel_b = chromaPastel-chromaPastel_a*p2; + float newchromaPastel = chromaPastel_a*saturation + chromaPastel_b; chmodpastel = newchromaPastel*satredu*sathue[3]; } - - chromaSatur_a=(chromaSatur-chromamean)/(s0-limitpastelsatur); - chromaSatur_b=chromaSatur-chromaSatur_a*s0; - if(saturation < s0 && saturation >=limitpastelsatur) {newchromaSatur=chromaSatur_a*saturation + chromaSatur_b; chmodsat = newchromaSatur*satredu*sathue2[0];} - }// end transition + + if(saturation < s0 && saturation >=limitpastelsatur) { + float chromaSatur_a=(chromaSatur-chromamean)/(s0-limitpastelsatur); + float chromaSatur_b=chromaSatur-chromaSatur_a*s0; + float newchromaSatur=chromaSatur_a*saturation + chromaSatur_b; + chmodsat = newchromaSatur*satredu*sathue2[0]; + } + }// end transition if (saturation <= limitpastelsatur) { if(chmodpastel > 2.0 ) chmodpastel = 2.0; //avoid too big values if(chmodpastel < -0.93) chmodpastel =-0.93; //avoid negative values diff --git a/rtengine/shmap.cc b/rtengine/shmap.cc index 8d60d52e7..28bd1a1da 100644 --- a/rtengine/shmap.cc +++ b/rtengine/shmap.cc @@ -44,23 +44,22 @@ SHMap::~SHMap () { } void SHMap::update (Imagefloat* img, double radius, double lumi[3], bool hq, int skip) { - - // fill with luminance - #pragma omp parallel for - for (int i=0; ir[i][j],0.f) + lumi[1]*std::max(img->g[i][j],0.f) + lumi[2]*std::max(img->b[i][j],0.f); - } #ifdef _OPENMP #pragma omp parallel #endif { - if (!hq) { - AlignedBuffer* buffer = new AlignedBuffer (max(W,H)); - gaussHorizontal (map, map, buffer, W, H, radius, multiThread); - gaussVertical (map, map, buffer, W, H, radius, multiThread); + // fill with luminance + #pragma omp for + for (int i=0; ir[i][j],0.f) + lumi[1]*std::max(img->g[i][j],0.f) + lumi[2]*std::max(img->b[i][j],0.f); + } - delete buffer; + if (!hq) { + AlignedBufferMP* pBuffer = new AlignedBufferMP (max(W,H)); + gaussHorizontal (map, map, *pBuffer, W, H, radius); + gaussVertical (map, map, *pBuffer, W, H, radius); + delete pBuffer; } else { /* diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 0088cabbf..0266edaa3 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -29,7 +29,6 @@ #include "rawimagesource.h" #include "../rtgui/ppversion.h" #undef THREAD_PRIORITY_NORMAL -#define CLIP(a) ((a)>0?((a)<65535?(a):65535):0) namespace rtengine { extern const Settings* settings; @@ -55,7 +54,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p } procparams::ProcParams& params = job->pparams; - // aquire image from imagesource + // acquire image from imagesource ImageSource* imgsrc = ii->getImageSource (); int tr = TR_NONE; diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index 90e8d8090..e6e9e6708 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -114,6 +114,7 @@ RTWindow::RTWindow () // decorate tab, the label is unimportant since its updated in batchqueuepanel anyway Gtk::Label* lbq = Gtk::manage ( new Gtk::Label (M("MAIN_FRAME_BATCHQUEUE")) ); + if (options.mainNBVertical) lbq->set_angle(90); mainNB->append_page (*bpanel, *lbq); // epanel is only for single tab mode diff --git a/rtgui/thresholdadjuster.cc b/rtgui/thresholdadjuster.cc index 6ec5ba569..087354671 100644 --- a/rtgui/thresholdadjuster.cc +++ b/rtgui/thresholdadjuster.cc @@ -27,6 +27,21 @@ #define MIN_RESET_BUTTON_HEIGHT 17 +ThresholdAdjuster::ThresholdAdjuster (Glib::ustring label, + double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider, bool editedCheckBox) + : tSelector(minValueBottom, maxValueBottom, defBottom, labelBottom, precisionBottom, minValueTop, maxValueTop, defTop, labelTop, precisionTop, curveProvider) + +{ + initialDefaultVal[ThresholdSelector::TS_BOTTOMLEFT] = defBottom; + initialDefaultVal[ThresholdSelector::TS_TOPLEFT] = defTop; + initialDefaultVal[ThresholdSelector::TS_BOTTOMRIGHT] = 0.; // unused + initialDefaultVal[ThresholdSelector::TS_TOPRIGHT] = 0.; // unused + + initObject (label, editedCheckBox); +} + ThresholdAdjuster::ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne, bool editedCheckBox) : tSelector(minValue, maxValue, defBottom, defTop, precision, startAtOne) @@ -196,11 +211,11 @@ void ThresholdAdjuster::setValue (double bottomLeft, double topLeft, double bott afterReset = false; } -void ThresholdAdjuster::getValue (Glib::ustring& bottom, Glib::ustring& top) { +inline void ThresholdAdjuster::getValue (Glib::ustring& bottom, Glib::ustring& top) { tSelector.getPositions (bottom, top); } -void ThresholdAdjuster::getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { +inline void ThresholdAdjuster::getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { tSelector.getPositions (bottomLeft, topLeft, bottomRight, topRight); } @@ -213,6 +228,11 @@ bool ThresholdAdjuster::notifyListener () { return false; } +inline void ThresholdAdjuster::setBgCurveProvider (ThresholdCurveProvider* provider) { + tSelector.setBgCurveProvider(provider); +} + + void ThresholdAdjuster::setEnabled (bool enabled) { tSelector.set_sensitive (enabled); @@ -283,9 +303,19 @@ void ThresholdAdjuster::sendToListener () { } } +void ThresholdAdjuster::set_tooltip_markup(const Glib::ustring& markup) { + tSelector.set_tooltip_markup(markup); +} + +void ThresholdAdjuster::set_tooltip_text(const Glib::ustring& text) { + tSelector.set_tooltip_text(text); +} + /* For better readability, this method create the history string of the parameter column, * so that the parameters list can be read in a more logical way (i.e. corresponding * to the startAtOne field) + * + * If separatedMode==true, the top slider is assumed to be the primary slider, then the bottom slider as the second one */ Glib::ustring ThresholdAdjuster::getHistoryString () { if (tSelector.isDouble()) { @@ -296,6 +326,6 @@ Glib::ustring ThresholdAdjuster::getHistoryString () { else { Glib::ustring b, t; tSelector.getPositions(b, t); - return Glib::ustring::compose(tSelector.isStartAtOne()?"%2, %1":"%1, %2", b, t); + return Glib::ustring::compose(tSelector.isStartAtOne()||separatedMode?"%2, %1":"%1, %2", b, t); } } diff --git a/rtgui/thresholdadjuster.h b/rtgui/thresholdadjuster.h index e589b5f2f..8b8c965ae 100644 --- a/rtgui/thresholdadjuster.h +++ b/rtgui/thresholdadjuster.h @@ -64,6 +64,7 @@ class ThresholdAdjuster : public Gtk::VBox { bool afterReset; bool blocked; bool addMode; + bool separatedMode; int delay; double shapeValue (double a); @@ -73,8 +74,16 @@ class ThresholdAdjuster : public Gtk::VBox { public: + // Custom Threshold widget with 2 cursors (Top and Bottom) and a custom curve provided by the instanciator, + // each cursor having his own range, default value and precision + ThresholdAdjuster (Glib::ustring label, + double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider, bool editedCheckBox=false); + // Simple Threshold widget with 2 linked sliders (0->1 or 1->0) ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne, bool editedCheckBox=false); + // Simple Threshold widget with 4 linked sliders by pairs (0->1->0 or 1->0->1) ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne, bool editedCheckBox=false); @@ -82,8 +91,13 @@ class ThresholdAdjuster : public Gtk::VBox { virtual ~ThresholdAdjuster (); void setAdjusterListener (ThresholdAdjusterListener* alistener) { adjusterListener = alistener; } + void setBgCurveProvider (ThresholdCurveProvider* provider); + ThresholdSelector* getThresholdSelector() { return &tSelector; }; + template - rtengine::procparams::Threshold getValue () { return tSelector.getPositions(); } + rtengine::procparams::Threshold getValue () { + return tSelector.getPositions(); + } void getValue (double& bottom, double& top); void getValue (double& bottomLeft, double& topLeft, double& bottomRight, double& topRight); void getValue (int& bottom, int& top); @@ -91,9 +105,7 @@ class ThresholdAdjuster : public Gtk::VBox { void getValue (Glib::ustring& bottom, Glib::ustring& top); void getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight); template - void setValue (const rtengine::procparams::Threshold &tValues) { - tSelector.setPositions(tValues); - } + void setValue (const rtengine::procparams::Threshold &tValues) { tSelector.setPositions(tValues); } void setValue (double bottom, double top); void setValue (double bottomLeft, double topLeft, double bottomRight, double topRight); void setEnabled (bool enabled); @@ -114,6 +126,9 @@ class ThresholdAdjuster : public Gtk::VBox { void resetPressed (GdkEventButton* event); void editedToggled (); Glib::ustring getHistoryString (); + void set_tooltip_markup(const Glib::ustring& markup); + // this set_tooltip_text method is to set_tooltip_markup, and text can contain markups + void set_tooltip_text(const Glib::ustring& text); }; #endif diff --git a/rtgui/thresholdselector.cc b/rtgui/thresholdselector.cc index 5dcc39843..ce04e90ff 100644 --- a/rtgui/thresholdselector.cc +++ b/rtgui/thresholdselector.cc @@ -20,17 +20,45 @@ #include "thresholdselector.h" #include "multilangmgr.h" #include -#include #include "mycurve.h" +ThresholdSelector::ThresholdSelector(double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider) { + positions[TS_BOTTOMLEFT] = defPos[TS_BOTTOMLEFT] = defBottom; + positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTop; + positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = 0; // unused + positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = 0; // unused + this->precisionTop = precisionTop; + this->precisionBottom = precisionBottom; + doubleThresh = false; + + separatedLabelBottom = labelBottom; + separatedLabelTop = labelTop; + + bgCurveProvider = curveProvider; + separatedSliders = true; + initalEq1 = false; // unused + minValBottom = minValueBottom; + maxValBottom = maxValueBottom; + minValTop = minValueTop; + maxValTop = maxValueTop; + + initValues (); +} + ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne) { positions[TS_BOTTOMLEFT] = defPos[TS_BOTTOMLEFT] = defBottom; positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTop; positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = maxValue; positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = maxValue; - this->precision = precision; + this->precisionTop = precision; + this->precisionBottom = precision; doubleThresh = false; + separatedLabelBottom = ""; + separatedLabelTop = ""; + #ifndef NDEBUG if (startAtOne) { assert (defBottom >= defTop); @@ -42,9 +70,17 @@ ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double de assert (defBottom >= minValue); assert (defTop <= maxValue); } + assert(minValue < maxValue); #endif - initValues (minValue, maxValue, startAtOne); + bgCurveProvider = NULL; + separatedSliders = false; + initalEq1 = startAtOne; + minValTop = minValBottom = minValue; + maxValTop = maxValBottom = maxValue; + + initValues (); + } ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne) { @@ -52,9 +88,13 @@ ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double de positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTopLeft; positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = defBottomRight; positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = defTopRight; - this->precision = precision; + this->precisionTop = precision; + this->precisionBottom = precision; doubleThresh = true; + separatedLabelBottom = ""; + separatedLabelTop = ""; + #ifndef NDEBUG if (startAtOne) { assert (minValue <= defTopLeft); @@ -70,20 +110,25 @@ ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double de assert (defTopRight <= defBottomRight); assert (defBottomRight <= maxValue); } + assert(minValue < maxValue); #endif - initValues (minValue, maxValue, startAtOne); + bgCurveProvider = NULL; + separatedSliders = false; + initalEq1 = startAtOne; + minValTop = minValBottom = minValue; + maxValTop = maxValBottom = maxValue; + + initValues (); } -void ThresholdSelector::initValues (double minValue, double maxValue, bool startAtOne) { - assert(minValue <= maxValue); - initalEq1 = startAtOne; - minVal = minValue; - maxVal = maxValue; +void ThresholdSelector::initValues () { + + additionalTTip = ""; oldLitCursor = litCursor = TS_UNDEFINED; movedCursor = TS_UNDEFINED; secondaryMovedCursor = TS_UNDEFINED; - set_size_request (-1, 20); + set_size_request (-1, 30); add_events(Gdk::LEAVE_NOTIFY_MASK); set_name("ThresholdSelector"); set_can_focus(false); @@ -96,7 +141,7 @@ void ThresholdSelector::initValues (double minValue, double maxValue, bool start */ void ThresholdSelector::setPositions (double bottom, double top) { - setPositions(bottom, top, maxVal, maxVal); + setPositions(bottom, top, maxValBottom, maxValTop); } /* @@ -120,7 +165,7 @@ void ThresholdSelector::setPositions (double bottomLeft, double topLeft, double void ThresholdSelector::setDefaults (double bottom, double top) { - setDefaults(bottom, top, maxVal, maxVal); + setDefaults(bottom, top, maxValBottom, maxValTop); } void ThresholdSelector::setDefaults (double bottomLeft, double topLeft, double bottomRight, double topRight) { @@ -133,21 +178,6 @@ void ThresholdSelector::setDefaults (double bottomLeft, double topLeft, double b } } -void ThresholdSelector::getPositions (Glib::ustring& bottom, Glib::ustring& top) { - - - bottom = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMLEFT]); - top = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPLEFT]); -} - -void ThresholdSelector::getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { - - bottomLeft = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMLEFT]); - topLeft = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPLEFT]); - bottomRight = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMRIGHT]); - topRight = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPRIGHT]); -} - void ThresholdSelector::setBgGradient (const std::vector &milestones) { bgGradient.clear(); bgGradient = milestones; @@ -175,9 +205,10 @@ bool ThresholdSelector::on_expose_event(GdkEventExpose* event) { int iw = w-wslider-2*hb; // inner width (excluding padding for tabs) - for (int i=0; i<4; i++) { - positions01[i] = to01(positions[i]); - } + positions01[TS_BOTTOMLEFT] = to01(TS_BOTTOMLEFT); + positions01[TS_TOPLEFT] = to01(TS_TOPLEFT); + positions01[TS_BOTTOMRIGHT] = to01(TS_BOTTOMRIGHT); + positions01[TS_TOPRIGHT] = to01(TS_TOPRIGHT); Gtk::StateType state = !is_sensitive() ? Gtk::STATE_INSENSITIVE : Gtk::STATE_NORMAL; Glib::RefPtr style = get_style(); @@ -208,24 +239,52 @@ bool ThresholdSelector::on_expose_event(GdkEventExpose* event) { } // draw curve - double yStart = initalEq1 ? double(int(float(h)*1.5f/7.f))+1.5 : double(int(float(h)*5.5f/7.f))-0.5; - double yEnd = initalEq1 ? double(int(float(h)*5.5f/7.f))-0.5 : double(int(float(h)*1.5f/7.f))+1.5; - ThreshCursorId p[4]; - if (initalEq1) { p[0] = TS_TOPLEFT; p[1] = TS_BOTTOMLEFT; p[2] = TS_BOTTOMRIGHT; p[3] = TS_TOPRIGHT; } - else { p[0] = TS_BOTTOMLEFT; p[1] = TS_TOPLEFT; p[2] = TS_TOPRIGHT; p[3] = TS_BOTTOMRIGHT; } - if (positions[p[1]] > minVal) - cr->move_to (hb+hwslider, yStart); - else - cr->move_to (hb+hwslider, yEnd); - if (positions[p[0]] > minVal) - cr->line_to (hb+hwslider+iw*positions01[p[0]]+0.5, yStart); - if (positions[p[1]] > minVal) - cr->line_to (hb+hwslider+iw*positions01[p[1]]+0.5, yEnd); - cr->line_to (hb+hwslider+iw*positions01[p[2]]+0.5, yEnd); - if (doubleThresh && positions[p[2]] < maxVal) { - cr->line_to (hb+hwslider+iw*positions01[p[3]]+0.5, yStart); - if (positions[p[3]] < maxVal) + + if (bgCurveProvider) { + double yStart = double(int(float(h)*5.5f/7.f))-0.5; + double yEnd = double(int(float(h)*1.5f/7.f))+1.5; + + std::vector pts = bgCurveProvider->getCurvePoints(this); // the values sent by the provider are not checked (assumed to be correct) + if (pts.size() >= 4) { + std::vector::iterator i=pts.begin(); + double x = *i; i++; + double y = *i; i++; + cr->move_to (hb+hwslider+iw*x+0.5, (yEnd-yStart)*y+yStart); + + for (; iline_to (hb+hwslider+iw*x+0.5, (yEnd-yStart)*y+yStart); + } + } + else { + // Draw a straight line because not enough points has been sent + double yStart = double(int(float(h)*5.5f/7.f))-0.5; + cr->move_to (hb+hwslider+0.5, yStart); cr->line_to (hb+hwslider+iw+0.5, yStart); + } + + } + else { + double yStart = initalEq1 ? double(int(float(h)*1.5f/7.f))+1.5 : double(int(float(h)*5.5f/7.f))-0.5; + double yEnd = initalEq1 ? double(int(float(h)*5.5f/7.f))-0.5 : double(int(float(h)*1.5f/7.f))+1.5; + ThreshCursorId p[4]; + if (initalEq1) { p[0] = TS_TOPLEFT; p[1] = TS_BOTTOMLEFT; p[2] = TS_BOTTOMRIGHT; p[3] = TS_TOPRIGHT; } + else { p[0] = TS_BOTTOMLEFT; p[1] = TS_TOPLEFT; p[2] = TS_TOPRIGHT; p[3] = TS_BOTTOMRIGHT; } + if (positions[p[1]] > minValTop) // we use minValTop since if this block is executed, it means that we are in a simple Threshold where both bottom and top range are the same + cr->move_to (hb+hwslider, yStart); + else + cr->move_to (hb+hwslider, yEnd); + if (positions[p[0]] > minValTop) + cr->line_to (hb+hwslider+iw*positions01[p[0]]+0.5, yStart); + if (positions[p[1]] > minValTop) + cr->line_to (hb+hwslider+iw*positions01[p[1]]+0.5, yEnd); + cr->line_to (hb+hwslider+iw*positions01[p[2]]+0.5, yEnd); + if (doubleThresh && positions[p[2]] < maxValTop) { + cr->line_to (hb+hwslider+iw*positions01[p[3]]+0.5, yStart); + if (positions[p[3]] < maxValTop) + cr->line_to (hb+hwslider+iw+0.5, yStart); + } } if (is_sensitive() && bgGradient.size()>1) { // draw surrounding curve @@ -371,14 +430,18 @@ bool ThresholdSelector::on_motion_notify_event (GdkEventMotion* event) { if (movedCursor != TS_UNDEFINED) { // user is moving a cursor or two - double minBound, maxBound; + double minBound, maxBound, dRange; findSecondaryMovedCursor(event->state); // computing the boundaries findBoundaries(minBound, maxBound); - double dX = ( (event->x-tmpX)*(maxVal-minVal) )/( w-2*hb ); + if (movedCursor==TS_BOTTOMLEFT || movedCursor==TS_BOTTOMRIGHT) + dRange = maxValBottom-minValBottom; + else + dRange = maxValTop-minValTop; + double dX = ( (event->x-tmpX)*dRange )/( w-2*hb ); // slow motion if CTRL is pressed if (event->state & Gdk::CONTROL_MASK) dX *= 0.05; @@ -429,7 +492,8 @@ void ThresholdSelector::findLitCursor(int posX, int posY) { litCursor = TS_TOPLEFT; if (doubleThresh) { - double cursorX = (posX-hb)*(maxVal-minVal)/(w-2*hb)+minVal; + // we use minValTop since if this block is executed, it means that we are in a simple Threshold where both bottom and top range are the same + double cursorX = (posX-hb)*(maxValTop-minValTop)/(w-2*hb)+minValTop; if (cursorX>positions[TS_TOPRIGHT] || abs(cursorX-positions[TS_TOPRIGHT]) < abs(cursorX-positions[TS_TOPLEFT])) litCursor = TS_TOPRIGHT; @@ -440,7 +504,8 @@ void ThresholdSelector::findLitCursor(int posX, int posY) { if (posX > 0 && posX < w) { litCursor = TS_BOTTOMLEFT; if (doubleThresh) { - double cursorX = (posX-hb)*(maxVal-minVal)/(w-2*hb)+minVal; + // we use minValTop since if this block is executed, it means that we are in a simple Threshold where both bottom and top range are the same + double cursorX = (posX-hb)*(maxValTop-minValTop)/(w-2*hb)+minValTop; if (cursorX>positions[TS_BOTTOMRIGHT] || abs(cursorX-positions[TS_BOTTOMRIGHT]) < abs(cursorX-positions[TS_BOTTOMLEFT])) litCursor = TS_BOTTOMRIGHT; @@ -453,55 +518,75 @@ void ThresholdSelector::findBoundaries(double &min, double &max) { switch (movedCursor) { case (TS_BOTTOMLEFT): - if (initalEq1) { - min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : minVal+(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]); + if (separatedSliders) { + if (movedCursor == TS_BOTTOMLEFT) { + min = minValBottom; + max = maxValBottom; + } + else if (movedCursor == TS_TOPLEFT) { + min = minValTop; + max = maxValTop; + } + } + else if (initalEq1) { + min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : minValTop+(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]); max = positions[TS_BOTTOMRIGHT]; } else { - min = minVal; + min = minValTop; max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : positions[TS_TOPRIGHT]-(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]); } break; case (TS_TOPLEFT): - if (initalEq1) { - min = minVal; + if (separatedSliders) { + if (movedCursor == TS_BOTTOMLEFT) { + min = minValBottom; + max = maxValBottom; + } + else if (movedCursor == TS_TOPLEFT) { + min = minValTop; + max = maxValTop; + } + } + else if (initalEq1) { + min = minValTop; max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : positions[TS_BOTTOMRIGHT]-(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]); } else { - min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : minVal+(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]); + min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : minValTop+(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]); max = positions[TS_TOPRIGHT]; } break; case (TS_BOTTOMRIGHT): if (initalEq1) { min = positions[TS_BOTTOMLEFT]; - max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : maxVal-(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]); + max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : maxValTop-(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]); } else { min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : positions[TS_TOPLEFT]+(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]); - max = maxVal; + max = maxValTop; } break; case (TS_TOPRIGHT): if (initalEq1) { min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : positions[TS_BOTTOMLEFT]+(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]); - max = maxVal; + max = maxValTop; } else { min = positions[TS_TOPLEFT]; - max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : maxVal-(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]); + max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : maxValTop-(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]); } break; default: - min = minVal; - max = maxVal; + min = minValTop; + max = maxValTop; break; } } void ThresholdSelector::findSecondaryMovedCursor(guint state) { secondaryMovedCursor = TS_UNDEFINED; - if (!(state & Gdk::SHIFT_MASK)) { + if (!separatedSliders && !(state & Gdk::SHIFT_MASK)) { switch (movedCursor) { case (TS_BOTTOMLEFT): secondaryMovedCursor = TS_TOPLEFT; @@ -537,34 +622,79 @@ void ThresholdSelector::reset () { queue_draw (); } -inline double ThresholdSelector::to01(double value) { +inline double ThresholdSelector::to01(ThreshCursorId cursorId) { - double rVal = (value-minVal)/(maxVal-minVal); + double rVal; + if (cursorId==TS_BOTTOMLEFT || cursorId==TS_BOTTOMRIGHT) + rVal = (positions[cursorId]-minValBottom)/(maxValBottom-minValBottom); + else + rVal = (positions[cursorId]-minValTop)/(maxValTop-minValTop); if (rVal < 0.) rVal = 0.; else if (rVal > 1.) rVal = 1.; return rVal; } +inline void ThresholdSelector::setBgCurveProvider (ThresholdCurveProvider* provider) { + bgCurveProvider = provider; +} + +inline void ThresholdSelector::setSeparatedSliders(bool separated) { + separatedSliders = separated; +} + +inline bool ThresholdSelector::getSeparatedSliders() { + return separatedSliders; +} + void ThresholdSelector::updateTooltip() { Glib::ustring tTip; - if (doubleThresh) - tTip = Glib::ustring::compose("%1: %2 %3: %4\n%5: %6 %7: %8\n%9", - M("THRESHOLDSELECTOR_TL"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPLEFT]), - M("THRESHOLDSELECTOR_TR"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPRIGHT]), - M("THRESHOLDSELECTOR_BL"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMLEFT]), - M("THRESHOLDSELECTOR_BR"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMRIGHT]), - M("THRESHOLDSELECTOR_HINT") + if (doubleThresh) { + tTip = Glib::ustring::compose("%1: %2 %3: %4\n%5: %6 %7: %8", + M("THRESHOLDSELECTOR_TL"), Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPLEFT]), + M("THRESHOLDSELECTOR_TR"), Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPRIGHT]), + M("THRESHOLDSELECTOR_BL"), Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMLEFT]), + M("THRESHOLDSELECTOR_BR"), Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMRIGHT]) ); - else - tTip = Glib::ustring::compose("%1: %2\n%3: %4\n%5", - M("THRESHOLDSELECTOR_T"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPLEFT]), - M("THRESHOLDSELECTOR_B"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMLEFT]), - M("THRESHOLDSELECTOR_HINT") + if (!additionalTTip.empty()) + tTip += Glib::ustring::compose("\n\n%1", additionalTTip); + tTip += Glib::ustring::compose("\n\n%1", M("THRESHOLDSELECTOR_HINT")); + } + else if (separatedSliders) { + tTip = Glib::ustring::compose("%1: %2\n%3: %4", + separatedLabelTop, Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPLEFT]), + separatedLabelBottom, Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMLEFT]) ); - set_tooltip_markup(tTip); + if (!additionalTTip.empty()) + tTip += Glib::ustring::compose("\n\n%1", additionalTTip); + } + else { + tTip = Glib::ustring::compose("%1: %2\n%3: %4", + M("THRESHOLDSELECTOR_T"), Glib::ustring::format(std::fixed, std::setprecision(precisionTop), positions[TS_TOPLEFT]), + M("THRESHOLDSELECTOR_B"), Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), positions[TS_BOTTOMLEFT]) + ); + if (!additionalTTip.empty()) + tTip += Glib::ustring::compose("\n\n%1", additionalTTip); + tTip += Glib::ustring::compose("\n\n%1", M("THRESHOLDSELECTOR_HINT")); + } + Gtk::Widget::set_tooltip_markup(tTip); } sigc::signal ThresholdSelector::signal_value_changed() { return sig_val_changed; } + +double ThresholdSelector::shapePositionValue (ThreshCursorId cursorId) { + unsigned int precision = (cursorId==TS_BOTTOMLEFT || cursorId==TS_BOTTOMRIGHT) ? precisionBottom : precisionTop; + return round(positions[cursorId]*pow(double(10), precision)) / pow(double(10), precision); +} + +void ThresholdSelector::set_tooltip_markup(const Glib::ustring& markup) { + additionalTTip = markup; + updateTooltip(); +} + +void ThresholdSelector::set_tooltip_text(const Glib::ustring& text) { + additionalTTip = text; + updateTooltip(); +} diff --git a/rtgui/thresholdselector.h b/rtgui/thresholdselector.h index 0308ad9d6..482213e94 100644 --- a/rtgui/thresholdselector.h +++ b/rtgui/thresholdselector.h @@ -21,6 +21,21 @@ #include "guiutils.h" #include "../rtengine/procparams.h" +#include + +class ThresholdSelector; + +/* + * This class let the instanciator to provide the background curve + */ +class ThresholdCurveProvider { + public: + /* + * The curve provider has to send back a list of point (at least 2 points) in the [0.0 ; 1.0] range + * for both X and Y axis; X and Y values are streamlined ( X1, Y1, X2, Y2, X3, Y3, ...) + */ + virtual std::vector getCurvePoints(ThresholdSelector* tAdjuster) const = 0; +}; /* * This widget will let you select a linearly variable threshold, creating a ramp up @@ -36,6 +51,11 @@ * * Please note that the values are related to the cursors, depending on their position * on the graph. E.g. the "bottomLeft" value is related to the bottom left cursor. + * + * It is also possible to have a threshold with 2 totally independent cursors, each one having his own range, + * man/max/default values and precision. This let developers create their own threshold curve, that they will + * have to provide through the + * */ class ThresholdSelector : public Gtk::DrawingArea { @@ -57,17 +77,24 @@ class ThresholdSelector : public Gtk::DrawingArea { Glib::RefPtr gc_; Glib::RefPtr backBuffer; std::vector bgGradient; + ThresholdCurveProvider* bgCurveProvider; + Glib::ustring additionalTTip; + Glib::ustring separatedLabelBottom; // Label for the bottom cursor, displayed if separatedSliders==true only + Glib::ustring separatedLabelTop; // Label for the top cursor, displayed if separatedSliders==true only + bool separatedSliders; // If true, the Top and Bottom sliders are totally separate and can be drag through the full range; for simple threshold only! bool doubleThresh; // If true: there curve is a double threshold (0 to 1 to 0, or 1 to 0 to 1). bool initalEq1; // If true: the curve start at 1 (top); if false: the curve start at 0 (bottom) - unsigned int precision; // Decimal number if this object has to handle "double" values + unsigned int precisionTop; // Decimal number if this object has to handle "double" values, for the Top slider + unsigned int precisionBottom; // Decimal number if this object has to handle "double" values, for the Bottom slider ThreshCursorId litCursor; ThreshCursorId oldLitCursor; double boundary1[2], boundary2[2]; double tmpX, tmpPos; ThreshCursorId movedCursor, secondaryMovedCursor; - double minVal, maxVal; + double minValTop, maxValTop; + double minValBottom, maxValBottom; double defPos[4]; double positions[4]; unsigned short wslider; @@ -75,22 +102,24 @@ class ThresholdSelector : public Gtk::DrawingArea { const static int hb = 3; // horizontal border const static int vb = 2; // vertical border - void initValues (double minValue, double maxValue, bool startAtOne); + void initValues (); void findLitCursor(int posX, int posY); void findSecondaryMovedCursor(guint state); void findBoundaries(double &min, double &max); - double to01(double value); + double to01(ThreshCursorId cursorId); void updateTooltip(); public: sigc::signal signal_value_changed(); + ThresholdSelector(double minValueBottom, double maxValueBottom, double defBottom, Glib::ustring labelBottom, unsigned int precisionBottom, + double minValueTop, double maxValueTop, double defTop, Glib::ustring labelTop, unsigned int precisionTop, + ThresholdCurveProvider* curveProvider); ThresholdSelector(double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne); ThresholdSelector(double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne); - double shapeValue (double value) { return round(value*pow(double(10), precision)) / pow(double(10), precision); } - + double shapePositionValue (ThreshCursorId cursorId); template void setDefaults (const rtengine::procparams::Threshold &t) { defPos[TS_BOTTOMLEFT] = double(t.value[0]); // should we use shapeValue() ? @@ -100,10 +129,8 @@ class ThresholdSelector : public Gtk::DrawingArea { defPos[TS_TOPRIGHT] = double(t.value[3]); } } - void setDefaults (double bottom, double top); void setDefaults (double bottomLeft, double topLeft, double bottomRight, double topRight); - template void setPositions (const rtengine::procparams::Threshold &tValues) { positions[TS_BOTTOMLEFT] = static_cast(tValues.value[TS_BOTTOMLEFT]); @@ -122,28 +149,42 @@ class ThresholdSelector : public Gtk::DrawingArea { rtengine::procparams::Threshold getPositions () { if (doubleThresh) { rtengine::procparams::Threshold rThresh( - static_cast(shapeValue(positions[TS_BOTTOMLEFT])), - static_cast(shapeValue(positions[TS_TOPLEFT])), - static_cast(shapeValue(positions[TS_BOTTOMRIGHT])), - static_cast(shapeValue(positions[TS_TOPRIGHT])), + static_cast(shapePositionValue(TS_BOTTOMLEFT)), + static_cast(shapePositionValue(TS_TOPLEFT)), + static_cast(shapePositionValue(TS_BOTTOMRIGHT)), + static_cast(shapePositionValue(TS_TOPRIGHT)), initalEq1 ); return rThresh; } else { rtengine::procparams::Threshold rThresh( - static_cast(shapeValue(positions[TS_BOTTOMLEFT])), - static_cast(shapeValue(positions[TS_TOPLEFT])), + static_cast(shapePositionValue(TS_BOTTOMLEFT)), + static_cast(shapePositionValue(TS_TOPLEFT)), initalEq1 ); return rThresh; } } - void getPositions (Glib::ustring& bottom, Glib::ustring& top); - void getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight); + template + void getPositions (T &bottom, T &top) { + bottom = static_cast(shapePositionValue(TS_BOTTOMLEFT)); + top = static_cast(shapePositionValue(TS_TOPLEFT)); + } + template + void getPositions (T &bottomLeft, T &topLeft, T &bottomRight, T &topRight) { + bottomLeft = static_cast(shapePositionValue(TS_BOTTOMLEFT)); + topLeft = static_cast(shapePositionValue(TS_TOPLEFT)); + bottomRight = static_cast(shapePositionValue(TS_BOTTOMRIGHT)); + topRight = static_cast(shapePositionValue(TS_TOPRIGHT)); + } + + void setSeparatedSliders(bool separated); + bool getSeparatedSliders(); void setBgGradient (const std::vector &milestones); + void setBgCurveProvider (ThresholdCurveProvider* provider); bool isStartAtOne() { return initalEq1; } bool isDouble() { return doubleThresh; } void on_realize (); @@ -153,9 +194,27 @@ class ThresholdSelector : public Gtk::DrawingArea { bool on_motion_notify_event (GdkEventMotion* event); bool on_leave_notify_event (GdkEventCrossing* event); void styleChanged (const Glib::RefPtr& style); - unsigned int getPrecision () { return precision; } + unsigned int getPrecision () { return precisionTop; } void reset (); + void set_tooltip_markup(const Glib::ustring& markup); + // this set_tooltip_text method is to set_tooltip_markup, and text can contain markups + void set_tooltip_text(const Glib::ustring& text); }; +template<> +inline void ThresholdSelector::getPositions (Glib::ustring& bottom, Glib::ustring& top) { + bottom = Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), shapePositionValue(TS_BOTTOMLEFT)); + top = Glib::ustring::format(std::fixed, std::setprecision(precisionTop), shapePositionValue(TS_TOPLEFT)); +} + +template<> +inline void ThresholdSelector::getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) { + + bottomLeft = Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), shapePositionValue(TS_BOTTOMLEFT)); + topLeft = Glib::ustring::format(std::fixed, std::setprecision(precisionTop), shapePositionValue(TS_TOPLEFT)); + bottomRight = Glib::ustring::format(std::fixed, std::setprecision(precisionBottom), shapePositionValue(TS_BOTTOMRIGHT)); + topRight = Glib::ustring::format(std::fixed, std::setprecision(precisionTop), shapePositionValue(TS_TOPRIGHT)); +} + #endif diff --git a/rtgui/vibrance.cc b/rtgui/vibrance.cc index 589009c08..0d3b05410 100644 --- a/rtgui/vibrance.cc +++ b/rtgui/vibrance.cc @@ -28,19 +28,20 @@ Vibrance::Vibrance () : Gtk::VBox(), FoldableToolPanel(this) { enabled->set_active (false); pack_start(*enabled, Gtk::PACK_SHRINK, 0); - pastels = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PASTELS"),-100,100,5,50)); - pastels->setAdjusterListener (this); - //if (pastels->delay < 1000) pastels->delay = 1000; - pack_start( *pastels, Gtk::PACK_SHRINK, 0); - - saturated = Gtk::manage(new Adjuster (M("TP_VIBRANCE_SATURATED"),-100,100,5,50)); + saturated = Gtk::manage(new Adjuster (M("TP_VIBRANCE_SATURATED"),-100,100,1,50)); saturated->setAdjusterListener (this); saturated->set_sensitive(false); //if (saturated->delay < 1000) saturated->delay = 1000; pack_start( *saturated, Gtk::PACK_SHRINK, 0); - psThreshold = Gtk::manage (new ThresholdAdjuster (M("TP_VIBRANCE_PSTHRESHOLD"), 0., 100., 1., 75., 0, false)); + pastels = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PASTELS"),-100,100,1,50)); + pastels->setAdjusterListener (this); + //if (pastels->delay < 1000) pastels->delay = 1000; + pack_start( *pastels, Gtk::PACK_SHRINK, 0); + + 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)); psThreshold->setAdjusterListener (this); + psThreshold->set_tooltip_markup(M("TP_VIBRANCE_PSTHRESHOLD_TOOLTIP")); psThreshold->set_sensitive(false); //if (psThreshold->delay < 1000) psThreshold->delay = 1000; pack_start( *psThreshold, Gtk::PACK_SHRINK, 0); @@ -251,12 +252,14 @@ void Vibrance::adjusterChanged (Adjuster* a, double newval) { else if (a == saturated && !pastSatTog->get_active()) listener->panelChanged (EvVibranceSaturated, value ); } + if (pastSatTog->get_active()) + psThreshold->queue_draw(); } void Vibrance::adjusterChanged (ThresholdAdjuster* a, int newBottom, int newTop) { - if (listener && enabled->get_active()) { - listener->panelChanged (EvVibrancePastSatThreshold, psThreshold->getHistoryString()); - } + if (listener && enabled->get_active()) { + listener->panelChanged (EvVibrancePastSatThreshold, psThreshold->getHistoryString()); + } } @@ -295,3 +298,46 @@ void Vibrance::trimValues (ProcParams* pp) { pastels->trimValue (pp->vibrance.pastels); saturated->trimValue (pp->vibrance.saturated); } + +std::vector Vibrance::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; +} diff --git a/rtgui/vibrance.h b/rtgui/vibrance.h index 160024ba9..a53599f89 100644 --- a/rtgui/vibrance.h +++ b/rtgui/vibrance.h @@ -24,7 +24,7 @@ #include "thresholdadjuster.h" #include "toolpanel.h" -class Vibrance : public Gtk::VBox, public AdjusterListener, public ThresholdAdjusterListener, public FoldableToolPanel { +class Vibrance : public Gtk::VBox, public AdjusterListener, public ThresholdAdjusterListener, public FoldableToolPanel, public ThresholdCurveProvider { protected: Gtk::CheckButton* enabled; @@ -60,7 +60,8 @@ public: void enabled_toggled (); void protectskins_toggled (); void avoidcolorshift_toggled (); - void pastsattog_toggled (); + void pastsattog_toggled (); + std::vector getCurvePoints(ThresholdSelector* tAdjuster) const; };