diff --git a/COMPILE.txt b/COMPILE.txt index 9892d7582..08ba4ae59 100644 --- a/COMPILE.txt +++ b/COMPILE.txt @@ -1,3 +1,6 @@ +If you have problems with the compilation, identified the reason and fixed +the bug, please send me the updated build scripts (CMakeLists.txt files) to: +hgabor@rawtherapee.com Windows ------- @@ -28,32 +31,50 @@ Windows - Type: mingw32-make.exe install - You find the compiled program in the "release" directory - Linux ----- -Requirements: -- CMake -- GTK and GTKMM development packages -- libtiff, libpng, libjpeg, lcms, libiptcdata development packages -- ...did I forget something? + Requirements: + - CMake + - GTK and GTKMM development packages + - libtiff, libpng, libjpeg, lcms, libiptcdata development packages + - ...did I forget something? -On Ubuntu/Debian the requirements can be installed by running: -sudo apt-get install build-essential cmake libgtk2.0-dev libgtkmm-2.4-dev libtiff-dev libpng-dev libjpeg-dev liblcms-dev libiptcdata-dev merciurial + On Ubuntu/Debian the requirements can be installed by running: + sudo apt-get install build-essential cmake libgtk2.0-dev libgtkmm-2.4-dev libtiff-dev libpng-dev libjpeg-dev liblcms-dev libiptcdata-dev merciurial -Compile: -- Enter the root directory of the RawTherapee source tree -- Type: cmake -DCMAKE_INSTALL_PREFIX=./release -DBINDIR=. -DDATADIR=. -DLIBDIR=. -- Type: make install -- You find the compiled program in the release directory (you can copy it -anywhere you want) + Compile: + - Enter the root directory of the RawTherapee source tree + - Type: cmake -DCMAKE_INSTALL_PREFIX=./release -DBINDIR=. -DDATADIR=. -DLIBDIR=. + - Type: make install + - You find the compiled program in the release directory (you can copy it + anywhere you want) -(By changing the cmake flags, you can change where the release is. By removing all flags it should go to the standard system install location). + (By changing the cmake flags, you can change where the release is. By removing all flags it should go to the standard system install location). -...If you have problems with the compilation, identified the reason and fixed -the bug, please send me the updated build scripts (CMakeLists.txt files) to: -hgabor@rawtherapee.com + Using Eclipse under Linux: + + Eclipse mercurial plugin: + click Help -> Install new Software. + The Eclipse Update Site for MercurialEclipse is available at this URL: + http://cbes.javaforge.com/update + + Import the rawtherapee mercurial repository: + File->new->'other'->mercurial->Clone existing repository + fill in URL: https://rawtherapee.googlecode.com/hg + + Enter root directory of RawTherapee source tree from a terminal window. + configure the source for Eclipse with: + cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_INSTALL_PREFIX=./release -DBINDIR=. -DDATADIR=. -DLIBDIR=. -DCMAKE_BUILD_TYPE=Release + + Eclipse does not do 'make install' but only 'make all' so to get the release there are 2 ways. + 1. type 'make install' in the console or, + 2. in 'Project'->'properties'->'C/C++ Make Project'->'Build (incremental build)' change 'all' to 'install' + + +... + OSX --- diff --git a/rtdata/languages/English b/rtdata/languages/English index 632e1c8e0..1776f0002 100644 --- a/rtdata/languages/English +++ b/rtdata/languages/English @@ -409,9 +409,6 @@ !PREFERENCES_CACHETHUMBFORM;Cache Thumbnail Format !PREFERENCES_CACHETHUMBHEIGHT;Maximal Thumbnail Height !PREFERENCES_CACORRECTION;Apply CA auto correction -!PREFERENCES_CLEARDLG_LINE1;Clearing cache -!PREFERENCES_CLEARDLG_LINE2;This may take a few seconds. -!PREFERENCES_CLEARDLG_TITLE;Please wait !PREFERENCES_CLIPPINGIND;Clipping indication !PREFERENCES_CMETRICINTENT;Colorimetric Intent !PREFERENCES_DATEFORMAT;Date Format @@ -455,9 +452,9 @@ !PREFERENCES_OUTDIR;Output Directory !PREFERENCES_OUTDIRFOLDER;Save to folder !PREFERENCES_OUTDIRFOLDERHINT;Put the saved images to the selected folder -!PREFERENCES_OUTDIRHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the directories and sub-paths of the path of the raw file.nnFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:n%f=dsc0012, %d1=02-09-2006, %d2=image, ...n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory 'converted' located the directory of the original, write:n%p1/converted/%fnnIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:n%p2/converted/%d1/%f +!PREFERENCES_OUTDIRHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the different parts of the photo's pathname.nnFor example, if the photo being processed has the following pathname:n/home/tom/photos/2010-10-31/dsc0042.nefnthe meaning of the formatting strings are:n%d4 = homen%d3 = tomn%d2 = photosn%d1 = 2010-10-31n%f = dsc0042n%p1 = /home/tom/photos/2010-10-31/n%p2 = /home/tom/photos/n%p3 = /home/tom/n%p4 = /home/nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:n%p1/converted/%fnnIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:n%p2/converted/%d1/%f !PREFERENCES_OUTDIRTEMPLATE;Use Template -!PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the directories and sub-paths of the path of the raw file.nnFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:n%f=dsc0012, %d1=02-09-2006, %d2=image, ...n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory 'converted' located the directory of the original, write:n%p1/converted/%fnnIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:n%p2/converted/%d1/%f +!PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the different parts of the photo's pathname.nnFor example, if the photo being processed has the following pathname:n/home/tom/photos/2010-10-31/dsc0042.nefnthe meaning of the formatting strings are:n%d4 = homen%d3 = tomn%d2 = photosn%d1 = 2010-10-31n%f = dsc0042n%p1 = /home/tom/photos/2010-10-31/n%p2 = /home/tom/photos/n%p3 = /home/tom/n%p4 = /home/nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:n%p1/converted/%fnnIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:n%p2/converted/%d1/%f !PREFERENCES_OVERLAY_FILENAMES;Overlay filenames on thumbnails !PREFERENCES_PARSEDEXT;Parsed Extensions !PREFERENCES_PARSEDEXTADD;Add Extension @@ -610,15 +607,15 @@ !TP_EXPOSURE_BLACKLEVEL;Black !TP_EXPOSURE_BRIGHTNESS;Brightness !TP_EXPOSURE_CLIP;Clip -!TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight rolloff -!TP_EXPOSURE_COMPRSHADOWS;Shadow compression +!TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight recovery +!TP_EXPOSURE_COMPRSHADOWS;Shadow recovery !TP_EXPOSURE_CONTRAST;Contrast !TP_EXPOSURE_CURVEEDITOR;Tone Curve !TP_EXPOSURE_EXPCOMP;Exp. Comp. !TP_EXPOSURE_LABEL;Exposure !TP_HLREC_CIELAB;CIELab Blending !TP_HLREC_COLOR;Color Propagation -!TP_HLREC_LABEL;Highlight Recovery +!TP_HLREC_LABEL;Highlight Reconstruction !TP_HLREC_LUMINANCE;Luminance Recovery !TP_HLREC_METHOD;Method: !TP_ICM_FILEDLGFILTERANY;Any files diff --git a/rtdata/languages/English (UK) b/rtdata/languages/English (UK) index b3ca450ff..8d0099ac2 100644 --- a/rtdata/languages/English (UK) +++ b/rtdata/languages/English (UK) @@ -412,9 +412,6 @@ TP_HLREC_COLOR;Colour Propagation !PREFERENCES_CACHETHUMBFORM;Cache Thumbnail Format !PREFERENCES_CACHETHUMBHEIGHT;Maximal Thumbnail Height !PREFERENCES_CACORRECTION;Apply CA auto correction -!PREFERENCES_CLEARDLG_LINE1;Clearing cache -!PREFERENCES_CLEARDLG_LINE2;This may take a few seconds. -!PREFERENCES_CLEARDLG_TITLE;Please wait !PREFERENCES_CLIPPINGIND;Clipping indication !PREFERENCES_CMETRICINTENT;Colorimetric Intent !PREFERENCES_DATEFORMAT;Date Format @@ -457,9 +454,9 @@ TP_HLREC_COLOR;Colour Propagation !PREFERENCES_OUTDIR;Output Directory !PREFERENCES_OUTDIRFOLDER;Save to folder !PREFERENCES_OUTDIRFOLDERHINT;Put the saved images to the selected folder -!PREFERENCES_OUTDIRHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the directories and sub-paths of the path of the raw file.nnFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:n%f=dsc0012, %d1=02-09-2006, %d2=image, ...n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory 'converted' located the directory of the original, write:n%p1/converted/%fnnIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:n%p2/converted/%d1/%f +!PREFERENCES_OUTDIRHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the different parts of the photo's pathname.nnFor example, if the photo being processed has the following pathname:n/home/tom/photos/2010-10-31/dsc0042.nefnthe meaning of the formatting strings are:n%d4 = homen%d3 = tomn%d2 = photosn%d1 = 2010-10-31n%f = dsc0042n%p1 = /home/tom/photos/2010-10-31/n%p2 = /home/tom/photos/n%p3 = /home/tom/n%p4 = /home/nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:n%p1/converted/%fnnIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:n%p2/converted/%d1/%f !PREFERENCES_OUTDIRTEMPLATE;Use Template -!PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the directories and sub-paths of the path of the raw file.nnFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:n%f=dsc0012, %d1=02-09-2006, %d2=image, ...n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory 'converted' located the directory of the original, write:n%p1/converted/%fnnIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:n%p2/converted/%d1/%f +!PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the different parts of the photo's pathname.nnFor example, if the photo being processed has the following pathname:n/home/tom/photos/2010-10-31/dsc0042.nefnthe meaning of the formatting strings are:n%d4 = homen%d3 = tomn%d2 = photosn%d1 = 2010-10-31n%f = dsc0042n%p1 = /home/tom/photos/2010-10-31/n%p2 = /home/tom/photos/n%p3 = /home/tom/n%p4 = /home/nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:n%p1/converted/%fnnIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:n%p2/converted/%d1/%f !PREFERENCES_OVERLAY_FILENAMES;Overlay filenames on thumbnails !PREFERENCES_PARSEDEXT;Parsed Extensions !PREFERENCES_PARSEDEXTADD;Add Extension @@ -607,14 +604,14 @@ TP_HLREC_COLOR;Colour Propagation !TP_EXPOSURE_BLACKLEVEL;Black !TP_EXPOSURE_BRIGHTNESS;Brightness !TP_EXPOSURE_CLIP;Clip -!TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight rolloff -!TP_EXPOSURE_COMPRSHADOWS;Shadow compression +!TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight recovery +!TP_EXPOSURE_COMPRSHADOWS;Shadow recovery !TP_EXPOSURE_CONTRAST;Contrast !TP_EXPOSURE_CURVEEDITOR;Tone Curve !TP_EXPOSURE_EXPCOMP;Exp. Comp. !TP_EXPOSURE_LABEL;Exposure !TP_HLREC_CIELAB;CIELab Blending -!TP_HLREC_LABEL;Highlight Recovery +!TP_HLREC_LABEL;Highlight Reconstruction !TP_HLREC_LUMINANCE;Luminance Recovery !TP_HLREC_METHOD;Method: !TP_ICM_FILEDLGFILTERANY;Any files diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US) index bcbe9b60d..b1307a18e 100644 --- a/rtdata/languages/English (US) +++ b/rtdata/languages/English (US) @@ -409,9 +409,6 @@ !PREFERENCES_CACHETHUMBFORM;Cache Thumbnail Format !PREFERENCES_CACHETHUMBHEIGHT;Maximal Thumbnail Height !PREFERENCES_CACORRECTION;Apply CA auto correction -!PREFERENCES_CLEARDLG_LINE1;Clearing cache -!PREFERENCES_CLEARDLG_LINE2;This may take a few seconds. -!PREFERENCES_CLEARDLG_TITLE;Please wait !PREFERENCES_CLIPPINGIND;Clipping indication !PREFERENCES_CMETRICINTENT;Colorimetric Intent !PREFERENCES_DATEFORMAT;Date Format @@ -455,9 +452,9 @@ !PREFERENCES_OUTDIR;Output Directory !PREFERENCES_OUTDIRFOLDER;Save to folder !PREFERENCES_OUTDIRFOLDERHINT;Put the saved images to the selected folder -!PREFERENCES_OUTDIRHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the directories and sub-paths of the path of the raw file.nnFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:n%f=dsc0012, %d1=02-09-2006, %d2=image, ...n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory 'converted' located the directory of the original, write:n%p1/converted/%fnnIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:n%p2/converted/%d1/%f +!PREFERENCES_OUTDIRHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the different parts of the photo's pathname.nnFor example, if the photo being processed has the following pathname:n/home/tom/photos/2010-10-31/dsc0042.nefnthe meaning of the formatting strings are:n%d4 = homen%d3 = tomn%d2 = photosn%d1 = 2010-10-31n%f = dsc0042n%p1 = /home/tom/photos/2010-10-31/n%p2 = /home/tom/photos/n%p3 = /home/tom/n%p4 = /home/nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:n%p1/converted/%fnnIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:n%p2/converted/%d1/%f !PREFERENCES_OUTDIRTEMPLATE;Use Template -!PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the directories and sub-paths of the path of the raw file.nnFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:n%f=dsc0012, %d1=02-09-2006, %d2=image, ...n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory 'converted' located the directory of the original, write:n%p1/converted/%fnnIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:n%p2/converted/%d1/%f +!PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:n%f, %d1, %d2, ..., %p1, %p2, ...nnThese formatting strings refer to the different parts of the photo's pathname.nnFor example, if the photo being processed has the following pathname:n/home/tom/photos/2010-10-31/dsc0042.nefnthe meaning of the formatting strings are:n%d4 = homen%d3 = tomn%d2 = photosn%d1 = 2010-10-31n%f = dsc0042n%p1 = /home/tom/photos/2010-10-31/n%p2 = /home/tom/photos/n%p3 = /home/tom/n%p4 = /home/nnIf you want to save the output image where the original is, write:n%p1/%fnnIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:n%p1/converted/%fnnIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:n%p2/converted/%d1/%f !PREFERENCES_OVERLAY_FILENAMES;Overlay filenames on thumbnails !PREFERENCES_PARSEDEXT;Parsed Extensions !PREFERENCES_PARSEDEXTADD;Add Extension @@ -610,15 +607,15 @@ !TP_EXPOSURE_BLACKLEVEL;Black !TP_EXPOSURE_BRIGHTNESS;Brightness !TP_EXPOSURE_CLIP;Clip -!TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight rolloff -!TP_EXPOSURE_COMPRSHADOWS;Shadow compression +!TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight recovery +!TP_EXPOSURE_COMPRSHADOWS;Shadow recovery !TP_EXPOSURE_CONTRAST;Contrast !TP_EXPOSURE_CURVEEDITOR;Tone Curve !TP_EXPOSURE_EXPCOMP;Exp. Comp. !TP_EXPOSURE_LABEL;Exposure !TP_HLREC_CIELAB;CIELab Blending !TP_HLREC_COLOR;Color Propagation -!TP_HLREC_LABEL;Highlight Recovery +!TP_HLREC_LABEL;Highlight Reconstruction !TP_HLREC_LUMINANCE;Luminance Recovery !TP_HLREC_METHOD;Method: !TP_ICM_FILEDLGFILTERANY;Any files diff --git a/rtdata/languages/Slovak b/rtdata/languages/Slovak index 1fd723db9..519973d6d 100644 --- a/rtdata/languages/Slovak +++ b/rtdata/languages/Slovak @@ -4,11 +4,14 @@ #02 01.02.2009 #03 23.10.2010 #04 (Slapo) + + + ADJUSTER_RESET_TO_DEFAULT;Resetovať na predvolené nastavenia BATCHQUEUE_AUTOSTART;Auto štart BATCH_PROCESSING;Dávkové spracovanie CURVEEDITOR_CUSTOM;Vlastné -CURVEEDITOR_DARKS;Hlboké tiene +CURVEEDITOR_DARKS;Tiene CURVEEDITOR_FILEDLGFILTERANY;Všetky súbory CURVEEDITOR_FILEDLGFILTERCURVE;Súbory kriviek CURVEEDITOR_HIGHLIGHTS;Najvyššie svetlá @@ -18,7 +21,7 @@ CURVEEDITOR_LOADDLGLABEL;Načítať krivku... CURVEEDITOR_NURBS;Kontrolná klietka CURVEEDITOR_PARAMETRIC;Parametricky CURVEEDITOR_SAVEDLGLABEL;Uložiť krivku... -CURVEEDITOR_SHADOWS;Tiene +CURVEEDITOR_SHADOWS;Hlboké tiene CURVEEDITOR_TOOLTIPLINEAR;Resetovať krivku na lineárnu CURVEEDITOR_TOOLTIPLOAD;Načítať krivku zo súboru CURVEEDITOR_TOOLTIPSAVE;Uložiť súčasnú krivku @@ -129,11 +132,12 @@ HISTOGRAM_TOOLTIP_B;Zobraziť/Schovať MODRÝ histogram HISTOGRAM_TOOLTIP_G;Zobraziť/Schovať ZELENÝ histogram HISTOGRAM_TOOLTIP_L;Zobraziť/Schovať histogram CIELAB svietivosti HISTOGRAM_TOOLTIP_R;Zobraziť/Schovať ČERVENÝ histogram -HISTORY_CHANGED;Zmenené HISTORY_CUSTOMCURVE;Vlastná krivka HISTORY_DELSNAPSHOT;Odstrániť Snímok HISTORY_FROMCLIPBOARD;Zo schránky +HISTORY_CHANGED;Zmenené HISTORY_LABEL;História +HISTORY_MSG_1;Fotka načítaná HISTORY_MSG_10;Kompresia tieňov HISTORY_MSG_11;Krivka tónov HISTORY_MSG_12;Auto expozícia @@ -144,7 +148,7 @@ HISTORY_MSG_16;Čierna svietivosti HISTORY_MSG_17;Kompresia najvyšších svetiel v oblasti svietivosti HISTORY_MSG_18;Kompresia tieňov v oblasti svietivosti HISTORY_MSG_19;Krivka svietivosti -HISTORY_MSG_1;Fotka načítaná +HISTORY_MSG_2;Profil načítaný HISTORY_MSG_20;Doostrenie HISTORY_MSG_21;Polomer doostrenia HISTORY_MSG_22;Množstvo doostrenia @@ -155,7 +159,7 @@ HISTORY_MSG_26;Tolerancia okrajov pri doostrení HISTORY_MSG_27;Kontrola svätožiary pri doostrení HISTORY_MSG_28;Množstvo kontroly svätožiary HISTORY_MSG_29;Doostrovacia metóda -HISTORY_MSG_2;Profil načítaný +HISTORY_MSG_3;Profil zmenený HISTORY_MSG_30;Polomer dekonvolúcie HISTORY_MSG_31;Množstvo dekonvolúcie HISTORY_MSG_32;Tlmenie dekonvolúcie @@ -166,7 +170,7 @@ HISTORY_MSG_36;Hranica sýtosti HISTORY_MSG_37;Zosilnenie farieb HISTORY_MSG_38;Metóda vyváženia bielej HISTORY_MSG_39;Farebná teplota -HISTORY_MSG_3;Profil zmenený +HISTORY_MSG_4;História prehliadania HISTORY_MSG_40;Nádych vyváženia bielej HISTORY_MSG_41;Farebný posun "A" HISTORY_MSG_42;Farebný posun "B" @@ -177,7 +181,7 @@ HISTORY_MSG_46;Farebné odšumenie HISTORY_MSG_47;Polomer farebného odšumenia HISTORY_MSG_48;Tolerancia okrajov farebného odšumenia HISTORY_MSG_49;Citlivosť na okraje pri farebnom odšumení -HISTORY_MSG_4;História prehliadania +HISTORY_MSG_5;Jas HISTORY_MSG_50;Nástroj Tiene/Najvyššie svetlá HISTORY_MSG_51;Zosilnenie najvyšších svetiel HISTORY_MSG_52;Zosilnenie tieňov @@ -188,7 +192,7 @@ HISTORY_MSG_56;Polomer Tiene/Najvyššie svetlá HISTORY_MSG_57;Hrubé otočenie HISTORY_MSG_58;Horizontálne preklápanie HISTORY_MSG_59;Vertikálne preklápanie -HISTORY_MSG_5;Jas +HISTORY_MSG_6;Kontrast HISTORY_MSG_60;Otočenie HISTORY_MSG_61;Otočenie HISTORY_MSG_62;Korekcia zakrivenia objektívu @@ -199,7 +203,7 @@ HISTORY_MSG_66;Obnova najvyšších svetiel HISTORY_MSG_67;Množstvo obnovy najvyšších svetiel HISTORY_MSG_68;Metóda obnovy najvyšších svetiel HISTORY_MSG_69;Pracovný farebný priestor -HISTORY_MSG_6;Kontrast +HISTORY_MSG_7;Čierna HISTORY_MSG_70;Výstupný farebný priestor HISTORY_MSG_71;Vstupný farebný priestor HISTORY_MSG_72;Korekcia vignetácie @@ -208,11 +212,11 @@ HISTORY_MSG_74;Zmeniť veľkosť - Rozmer HISTORY_MSG_75;Zmeniť veľkosť - Metóda HISTORY_MSG_76;Exif Metadáta HISTORY_MSG_77;IPTC Metadáta -HISTORY_MSG_78;Data specified for resize -HISTORY_MSG_79;Resize width -HISTORY_MSG_7;Čierna -HISTORY_MSG_80;Resize height -HISTORY_MSG_81;Resize enabled +HISTORY_MSG_78;Zadané údaje pre zmenu veľkosti +HISTORY_MSG_79;Zmena veľkosti podľa šírky +HISTORY_MSG_8;Kompenzácia expozície +HISTORY_MSG_80;Zmena veľkosti podľa výšky +HISTORY_MSG_81;Zmena veľkosti povolená HISTORY_MSG_82;Profil zmenený HISTORY_MSG_83;Tiene/najvyšie svetlá vyskokej kvality HISTORY_MSG_84;Náprava perspektívy @@ -220,11 +224,10 @@ HISTORY_MSG_85;Koeficienty vlnky HISTORY_MSG_86;Vyrovnávač vlnky HISTORY_MSG_87;Impulzná redukcia šumu HISTORY_MSG_89;Smerová pyramída -HISTORY_MSG_8;Kompenzácia expozície +HISTORY_MSG_9;Kompresia najvyšších svetiel HISTORY_MSG_90;Svietivosť smerovej pyramídy HISTORY_MSG_91;Farebnosť smerovej pyramídy HISTORY_MSG_92;Gamma smerovej pyramídy -HISTORY_MSG_9;Kompresia najvyšších svetiel HISTORY_NEWSNAPSHOT;Nový Snímok HISTORY_NEWSNAPSHOTAS;Ako... HISTORY_NEWSSDIALOGLABEL;Menovka snímku: @@ -295,8 +298,8 @@ MAIN_BUTTON_PUTTOQUEUE;Vložiť do radu MAIN_BUTTON_PUTTOQUEUE_TOOLTIP;Pridať súčasný obrázok do radu na spracovanie Ctrl+Q MAIN_BUTTON_QUEUE;Vložiť do radu MAIN_BUTTON_SAVE;Uložiť obrázok -MAIN_BUTTON_SAVEAS;Ako... MAIN_BUTTON_SAVE_TOOLTIP;Uložiť súčasný obrázok Ctrl+S +MAIN_BUTTON_SAVEAS;Ako... MAIN_BUTTON_SENDTOEDITOR;Odoslať do editora MAIN_BUTTON_SENDTOEDITOR_TOOLTIP;Upraviť súčasný obrázok v externom editore Ctrl+E MAIN_BUTTON_UNFULLSCREEN;Ukončiť zobrazenie na celú obrazovku @@ -387,6 +390,7 @@ PREFERENCES_APPLNEXTSTARTUP;Aplikovaný pri ďalšom spustení PREFERENCES_BATCH_PROCESSING;dávkové spracovanie PREFERENCES_BEHAVIOR;Správanie sa PREFERENCES_BLINKCLIPPED;Blikať orezanými miestami +PREFERENCES_CACORRECTION;Použiť automatickú úpravu CA PREFERENCES_CACHECLEARALL;Vyčistiť všetko PREFERENCES_CACHECLEARPROFILES;Vyčistiť profily PREFERENCES_CACHECLEARTHUMBS;Vyčistiť zmenšeniny @@ -394,12 +398,11 @@ PREFERENCES_CACHEFORMAT1;Vlastné (rýchlejšie a kvalitnejšie) PREFERENCES_CACHEFORMAT2;JPEG (menšia veľkosť na disku) PREFERENCES_CACHEMAXENTRIES;Maximálny počet vstupov v cache PREFERENCES_CACHEOPTS;Možnosti cache +PREFERENCES_CACHESTRAT;Stratégia použitia cache PREFERENCES_CACHESTRAT1;Uprednostniť rýchlosť pred malou spotrebou pamäte PREFERENCES_CACHESTRAT2;Uprednostniť malú spotrebu pamäte pred rýchlosťou -PREFERENCES_CACHESTRAT;Stratégia použitia cache PREFERENCES_CACHETHUMBFORM;Formát zmenšenín pre cache PREFERENCES_CACHETHUMBHEIGHT;Maximálna výška zmenšenín -PREFERENCES_CACORRECTION;Použiť automatickú úpravu CA PREFERENCES_CLEARDLG_LINE1;Čistím cache PREFERENCES_CLEARDLG_LINE2;Môže to pár sekúnd trvať. PREFERENCES_CLEARDLG_TITLE;Prosím, čakajte. @@ -544,10 +547,10 @@ TP_COARSETRAF_TOOLTIP_HFLIP;Prevrátiť horizontálne TP_COARSETRAF_TOOLTIP_ROTLEFT;Otočiť doľava TP_COARSETRAF_TOOLTIP_ROTRIGHT;Otočiť doprava TP_COARSETRAF_TOOLTIP_VFLIP;Prevrátiť vertikálne -TP_COLORBOOST_ACHANNEL;kanál "a" +TP_COLORBOOST_ACHANNEL;Kanál "a" TP_COLORBOOST_AMOUNT;Množstvo TP_COLORBOOST_AVOIDCOLORCLIP;Vyhnúť sa orezaniu farieb -TP_COLORBOOST_BCHANNEL;kanál "b" +TP_COLORBOOST_BCHANNEL;Kanál "b" TP_COLORBOOST_CHAB;a & b TP_COLORBOOST_CHANNEL;Kanál TP_COLORBOOST_CHSEPARATE;oddelené @@ -717,16 +720,3 @@ ZOOMPANEL_ZOOM100;Priblíženie na 100% 1 ZOOMPANEL_ZOOMFITSCREEN;Prispôsobiť obrazovke F ZOOMPANEL_ZOOMIN;Priblížiť + ZOOMPANEL_ZOOMOUT;Oddialiť - - - -!!!!!!!!!!!!!!!!!!!!!!!!! -! Untranslated keys follow; remove the ! prefix after an entry is translated. -!!!!!!!!!!!!!!!!!!!!!!!!! - - -!NAVIGATOR_LAB_A_NA;A = n/a -!NAVIGATOR_LAB_A_VALUE;A = %1 -!NAVIGATOR_LAB_B_NA;B = n/a -!NAVIGATOR_LAB_B_VALUE;B = %1 -!NAVIGATOR_LAB_L_NA;L = n/a -!NAVIGATOR_LAB_L_VALUE;L = %1 diff --git a/rtdata/languages/default b/rtdata/languages/default index 31229b41a..0b14fb18c 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -402,9 +402,6 @@ PREFERENCES_CACHESTRAT;Cache Strategy PREFERENCES_CACHETHUMBFORM;Cache Thumbnail Format PREFERENCES_CACHETHUMBHEIGHT;Maximal Thumbnail Height PREFERENCES_CACORRECTION;Apply CA auto correction -PREFERENCES_CLEARDLG_LINE1;Clearing cache -PREFERENCES_CLEARDLG_LINE2;This may take a few seconds. -PREFERENCES_CLEARDLG_TITLE;Please wait PREFERENCES_CLIPPINGIND;Clipping indication PREFERENCES_CMETRICINTENT;Colorimetric Intent PREFERENCES_DATEFORMAT;Date Format @@ -448,9 +445,9 @@ PREFERENCES_MULTITAB;Multiple tabs mode PREFERENCES_OUTDIR;Output Directory PREFERENCES_OUTDIRFOLDER;Save to folder PREFERENCES_OUTDIRFOLDERHINT;Put the saved images to the selected folder -PREFERENCES_OUTDIRHINT;You can use the following formatting strings:\n%f, %d1, %d2, ..., %p1, %p2, ...\n\nThese formatting strings refer to the directories and sub-paths of the path of the raw file.\n\nFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:\n%f=dsc0012, %d1=02-09-2006, %d2=image, ...\n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...\n\nIf you want to save the output image where the original is, write:\n%p1/%f\n\nIf you want to save the output image in a directory 'converted' located the directory of the original, write:\n%p1/converted/%f\n\nIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:\n%p2/converted/%d1/%f +PREFERENCES_OUTDIRHINT;You can use the following formatting strings:\n%f, %d1, %d2, ..., %p1, %p2, ...\n\nThese formatting strings refer to the different parts of the photo's pathname.\n\nFor example, if the photo being processed has the following pathname:\n/home/tom/photos/2010-10-31/dsc0042.nef\nthe meaning of the formatting strings are:\n%d4 = home\n%d3 = tom\n%d2 = photos\n%d1 = 2010-10-31\n%f = dsc0042\n%p1 = /home/tom/photos/2010-10-31/\n%p2 = /home/tom/photos/\n%p3 = /home/tom/\n%p4 = /home/\n\nIf you want to save the output image where the original is, write:\n%p1/%f\n\nIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:\n%p1/converted/%f\n\nIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:\n%p2/converted/%d1/%f PREFERENCES_OUTDIRTEMPLATE;Use Template -PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:\n%f, %d1, %d2, ..., %p1, %p2, ...\n\nThese formatting strings refer to the directories and sub-paths of the path of the raw file.\n\nFor example, if /home/tom/image/02-09-2006/dsc0012.nefhas been opened, the meaning of the formatting strings are:\n%f=dsc0012, %d1=02-09-2006, %d2=image, ...\n%p1=/home/tom/image/02-09-2006, %p2=/home/tom/image, %p3=/home/tom, ...\n\nIf you want to save the output image where the original is, write:\n%p1/%f\n\nIf you want to save the output image in a directory 'converted' located the directory of the original, write:\n%p1/converted/%f\n\nIf you want to save the output image in directory '/home/tom/converted' with keeping the same subdirectory of dates, write:\n%p2/converted/%d1/%f +PREFERENCES_OUTDIRTEMPLATEHINT;You can use the following formatting strings:\n%f, %d1, %d2, ..., %p1, %p2, ...\n\nThese formatting strings refer to the different parts of the photo's pathname.\n\nFor example, if the photo being processed has the following pathname:\n/home/tom/photos/2010-10-31/dsc0042.nef\nthe meaning of the formatting strings are:\n%d4 = home\n%d3 = tom\n%d2 = photos\n%d1 = 2010-10-31\n%f = dsc0042\n%p1 = /home/tom/photos/2010-10-31/\n%p2 = /home/tom/photos/\n%p3 = /home/tom/\n%p4 = /home/\n\nIf you want to save the output image where the original is, write:\n%p1/%f\n\nIf you want to save the output image in a directory named "converted" located in the directory of the opened image, write:\n%p1/converted/%f\n\nIf you want to save the output image in a directory named "/home/tom/photos/converted/2010-10-31", write:\n%p2/converted/%d1/%f PREFERENCES_OVERLAY_FILENAMES;Overlay filenames on thumbnails PREFERENCES_PARSEDEXT;Parsed Extensions PREFERENCES_PARSEDEXTADD;Add Extension @@ -603,15 +600,15 @@ TP_EXPOSURE_AUTOLEVELS;Auto Levels TP_EXPOSURE_BLACKLEVEL;Black TP_EXPOSURE_BRIGHTNESS;Brightness TP_EXPOSURE_CLIP;Clip -TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight rolloff -TP_EXPOSURE_COMPRSHADOWS;Shadow compression +TP_EXPOSURE_COMPRHIGHLIGHTS;Highlight recovery +TP_EXPOSURE_COMPRSHADOWS;Shadow recovery TP_EXPOSURE_CONTRAST;Contrast TP_EXPOSURE_CURVEEDITOR;Tone Curve TP_EXPOSURE_EXPCOMP;Exp. Comp. TP_EXPOSURE_LABEL;Exposure TP_HLREC_CIELAB;CIELab Blending TP_HLREC_COLOR;Color Propagation -TP_HLREC_LABEL;Highlight Recovery +TP_HLREC_LABEL;Highlight Reconstruction TP_HLREC_LUMINANCE;Luminance Recovery TP_HLREC_METHOD;Method: TP_ICM_FILEDLGFILTERANY;Any files @@ -689,6 +686,10 @@ TP_SHARPENING_USM;Unsharp Mask TP_VIGNETTING_AMOUNT;Amount TP_VIGNETTING_LABEL;Vignetting Correction TP_VIGNETTING_RADIUS;Radius +TP_VIGNETTING_STRENGTH;Strength +TP_VIGNETTING_CENTER_X;Center X +TP_VIGNETTING_CENTER_Y;Center Y +TP_VIGNETTING_CENTER;Center TP_WBALANCE_AUTO;Auto TP_WBALANCE_CAMERA;Camera TP_WBALANCE_CUSTOM;Custom diff --git a/rtdata/options/options.lin b/rtdata/options/options.lin index 54b5a2c01..02016e9e4 100644 --- a/rtdata/options/options.lin +++ b/rtdata/options/options.lin @@ -41,6 +41,9 @@ RenameUseTemplates=false ThumbnailZoomRatios=0.2;0.3;0.45;0.6;0.8;1; OverlayedFileNames=true +# if TRUE, only fast, internal preview images are taken if the image is not edited yet +InternalThumbIfUntouched=true + [Clipping Indication] HighlightThreshold=253 ShadowThreshold=8 @@ -57,6 +60,8 @@ PathTemplate=%p1/converted/%f PathFolder= UsePathTemplate=true LastSaveAsPath= + +# if TRUE, existing output JPGs/PNGs are overwritten, instead of adding ..-1.jpg, -2.jpg etc. OverwriteOutputFile=false [Profiles] diff --git a/rtdata/options/options.osx b/rtdata/options/options.osx index 1793efe78..f05215ae6 100644 --- a/rtdata/options/options.osx +++ b/rtdata/options/options.osx @@ -41,6 +41,9 @@ RenameUseTemplates=false ThumbnailZoomRatios=0.2;0.3;0.45;0.6;0.8;1; OverlayedFileNames=true +# if TRUE, only fast, internal preview images are taken if the image is not edited yet +InternalThumbIfUntouched=true + [Clipping Indication] HighlightThreshold=253 ShadowThreshold=8 @@ -57,6 +60,8 @@ PathTemplate=%p1/converted/%f PathFolder= UsePathTemplate=true LastSaveAsPath= + +# if TRUE, existing output JPGs/PNGs are overwritten, instead of adding ..-1.jpg, -2.jpg etc. OverwriteOutputFile=false [Profiles] diff --git a/rtdata/options/options.win b/rtdata/options/options.win index 519c5bdd3..8834ed124 100644 --- a/rtdata/options/options.win +++ b/rtdata/options/options.win @@ -41,6 +41,9 @@ RenameUseTemplates=false ThumbnailZoomRatios=0.2;0.3;0.45;0.6;0.8;1; OverlayedFileNames=true +# if TRUE, only fast, internal preview images are taken if the image is not edited yet +InternalThumbIfUntouched=true + [Clipping Indication] HighlightThreshold=253 ShadowThreshold=8 @@ -57,6 +60,8 @@ PathTemplate=%p1/converted/%f PathFolder= UsePathTemplate=true LastSaveAsPath= + +# if TRUE, existing output JPGs/PNGs are overwritten, instead of adding ..-1.jpg, -2.jpg etc. OverwriteOutputFile=false [Profiles] diff --git a/rtdata/profiles/crisp.pp3 b/rtdata/profiles/crisp.pp3 index b49fe52b4..75b7910cc 100644 --- a/rtdata/profiles/crisp.pp3 +++ b/rtdata/profiles/crisp.pp3 @@ -1,16 +1,16 @@ [Version] -Version=231 +Version=466 [Exposure] Auto=true -Clip=0.02 +Clip=0.001 Compensation=0 Brightness=0 Contrast=12 Black=0 -HighlightCompr=100 -ShadowCompr=100 +HighlightCompr=70 +ShadowCompr=25 Curve=1;0;0;1;1; [Channel Mixer] diff --git a/rtdata/profiles/default.pp3 b/rtdata/profiles/default.pp3 index a76f8d10c..fd8f1c188 100644 --- a/rtdata/profiles/default.pp3 +++ b/rtdata/profiles/default.pp3 @@ -1,16 +1,16 @@ [Version] -Version=231 +Version=466 [Exposure] Auto=true -Clip=0.01 +Clip=0.001 Compensation=0 Brightness=0 Contrast=0 Black=0 -HighlightCompr=100 -ShadowCompr=100 +HighlightCompr=70 +ShadowCompr=25 Curve=1;0;0;1;1; [Channel Mixer] diff --git a/rtdata/profiles/neutral.pp3 b/rtdata/profiles/neutral.pp3 index 7d9000170..6c8070367 100644 --- a/rtdata/profiles/neutral.pp3 +++ b/rtdata/profiles/neutral.pp3 @@ -1,16 +1,16 @@ [Version] -Version=231 +Version=466 [Exposure] Auto=false -Clip=0.01 +Clip=0.001 Compensation=0 Brightness=0 Contrast=0 Black=0 -HighlightCompr=100 -ShadowCompr=100 +HighlightCompr=70 +ShadowCompr=25 Curve=1;0;0;1;1; [Channel Mixer] diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 688c7455e..d8ce097bd 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -13,6 +13,7 @@ set (RTENGINESOURCEFILES colortemp.cc curves.cc dcraw.cc iccstore.cc stdimagesource.cc myfile.cc iccjpeg.c hlmultipliers.cc improccoordinator.cc processingjob.cc rtthumbnail.cc utils.cc labimage.cc iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc + jpeg_memsrc.c wavelet_dec.cc ipequalizer.cc dirpyrLab_denoise.cc dirpyrLab_equalizer.cc dirpyr_equalizer.cc) add_library (rtengine ${RTENGINESOURCEFILES}) diff --git a/rtengine/curves.cc b/rtengine/curves.cc index 69876a737..236c95b58 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -621,7 +621,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector satcurvePoints; satcurvePoints.push_back((double)((CurveType)NURBS)); - if (saturation>-99.5) { + if (saturation>0) { satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range @@ -635,10 +635,10 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou satcurvePoints.push_back(1); // value at white point } else { satcurvePoints.push_back(0); - satcurvePoints.push_back(0.5); + satcurvePoints.push_back(-0.5*(saturation/100.0)); satcurvePoints.push_back(1); - satcurvePoints.push_back(0.5); + satcurvePoints.push_back(1+saturation/200.0); } Curve* satcurve = NULL; satcurve = new Curve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, @@ -728,9 +728,11 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou std::vector basecurvePoints; basecurvePoints.push_back((double)((CurveType)NURBS)); - float toex = MIN(1,black/(a*def_mul)); - float toey = toex*a*def_mul*(1-shcompr/100.0); - float shoulderx = 1/(a*def_mul);//point in x at which line of slope a starting at (0,0) reaches y=1 + //float toex = MIN(1,black/(a*def_mul)); + //float toey = MAX(0,toex*a*def_mul*(shcompr/25.0-1)); + float toex = black; + float toey = MAX(0,toex*(shcompr/25.0-1)); + float shoulderx = MAX(black,1/(a*def_mul));//point in x at which line of slope a starting at (0,0) reaches y=1 float shouldery=1; float toneslope=(shouldery-toey)/(shoulderx-toex); if (shoulderx<1) {//a>1; positive EC @@ -752,7 +754,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou shouldery = toey + (1-toex)*a; }*/ - basecurvePoints.push_back(0); //black point. Value in [0 ; 1] range + basecurvePoints.push_back(MAX(0,0.99*toex*(1-shcompr/25.0))); //black point. Value in [0 ; 1] range basecurvePoints.push_back(0); //black point. Value in [0 ; 1] range basecurvePoints.push_back(toex); //toe point diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index ca4957228..5ddd16e27 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -1,6 +1,5 @@ /*RT*/#include /*RT*/#include -/*RT*/int ciff_base, ciff_len, exif_base, pre_filters; /*RT*/#undef MAX /*RT*/#undef MIN /*RT*/#define NO_LCMS @@ -111,60 +110,70 @@ typedef unsigned long long UINT64; typedef unsigned char uchar; typedef unsigned short ushort; +// RT specify thread local storage +#ifdef __GNUC__ +#define THREAD_LOCAL static __thread +#define THREAD_LOCK +#else +#define THREAD_LOCAL +#define THREAD_LOCK Glib::Mutex::Lock locker(*dcrMutex); +#endif + /* All global variables are defined here, and all functions that access them are prefixed with "CLASS". Note that a thread-safe C++ class cannot have non-const static local variables. */ -/*RT*/IMFILE *ifp; -FILE * ofp; -short order; -const char *ifname; -char *meta_data; -char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64]; -float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len; -time_t timestamp; -unsigned shot_order, kodak_cbpp, filters, exif_cfa, unique_id; -off_t strip_offset, data_offset; -off_t thumb_offset, meta_offset, profile_offset; -unsigned thumb_length, meta_length, profile_length; -unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0; -unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress; -unsigned black, cblack[8], maximum, mix_green, raw_color, zero_is_bad; -unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error; -unsigned tile_width, tile_length, gpsdata[32], load_flags; -ushort raw_height, raw_width, height, width, top_margin, left_margin; -ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height; -int flip, tiff_flip, colors; -double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 }; -ushort (*image)[4], white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4]; -float bright=1, user_mul[4]={0,0,0,0}, threshold=0; -int half_size=0, four_color_rgb=0, document_mode=0, highlight=0; -int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=-1; -int output_color=1, output_bps=8, output_tiff=0, med_passes=0; -int no_auto_bright=0; -unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX }; -float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4]; -const double xyz_rgb[3][3] = { /* XYZ from RGB */ +/*RT*/THREAD_LOCAL int ciff_base, ciff_len, exif_base, pre_filters; +/*RT*/THREAD_LOCAL IMFILE *ifp; +THREAD_LOCAL FILE * ofp; +THREAD_LOCAL short order; +THREAD_LOCAL const char *ifname; +THREAD_LOCAL char *meta_data; +THREAD_LOCAL char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64]; +THREAD_LOCAL float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len; +THREAD_LOCAL time_t timestamp; +THREAD_LOCAL unsigned shot_order, kodak_cbpp, filters, exif_cfa, unique_id; +THREAD_LOCAL off_t strip_offset, data_offset; +THREAD_LOCAL off_t thumb_offset, meta_offset, profile_offset; +THREAD_LOCAL unsigned thumb_length, meta_length, profile_length; +THREAD_LOCAL unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0; +THREAD_LOCAL unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress; +THREAD_LOCAL unsigned black, cblack[8], maximum, mix_green, raw_color, zero_is_bad; +THREAD_LOCAL unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error; +THREAD_LOCAL unsigned tile_width, tile_length, gpsdata[32], load_flags; +THREAD_LOCAL ushort raw_height, raw_width, height, width, top_margin, left_margin; +THREAD_LOCAL ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height; +THREAD_LOCAL int flip, tiff_flip, colors; +THREAD_LOCAL double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 }; +THREAD_LOCAL ushort (*image)[4], white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4]; +THREAD_LOCAL float bright=1, user_mul[4]={0,0,0,0}, threshold=0; +THREAD_LOCAL int half_size=0, four_color_rgb=0, document_mode=0, highlight=0; +THREAD_LOCAL int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=-1; +THREAD_LOCAL int output_color=1, output_bps=8, output_tiff=0, med_passes=0; +THREAD_LOCAL int no_auto_bright=0; +THREAD_LOCAL unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX }; +THREAD_LOCAL float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4]; +static const double xyz_rgb[3][3] = { /* XYZ from RGB */ { 0.412453, 0.357580, 0.180423 }, { 0.212671, 0.715160, 0.072169 }, { 0.019334, 0.119193, 0.950227 } }; -const float d65_white[3] = { 0.950456, 1, 1.088754 }; -int histogram[4][0x2000]; -void (*write_thumb)(), (*write_fun)(); -void (*load_raw)(), (*thumb_load_raw)(); -jmp_buf failure; +static const float d65_white[3] = { 0.950456, 1, 1.088754 }; +THREAD_LOCAL int histogram[4][0x2000]; +THREAD_LOCAL void (*write_thumb)(), (*write_fun)(); +THREAD_LOCAL void (*load_raw)(), (*thumb_load_raw)(); +THREAD_LOCAL jmp_buf failure; -struct decode { +THREAD_LOCAL struct decode { struct decode *branch[2]; int leaf; } first_decode[2048], *second_decode, *free_decode; -struct tiff_ifd { +THREAD_LOCAL struct tiff_ifd { int width, height, bps, comp, phint, offset, flip, samples, bytes; } tiff_ifd[10]; -struct ph1 { +THREAD_LOCAL struct ph1 { int format, key_off, black, black_off, split_col, tag_21a; float tag_210; } ph1; @@ -555,8 +564,8 @@ int CLASS canon_s2is() */ unsigned CLASS getbithuff (int nbits, ushort *huff) { - static unsigned bitbuf=0; - static int vbits=0, reset=0; + THREAD_LOCAL unsigned bitbuf=0; + THREAD_LOCAL int vbits=0, reset=0; unsigned c; if (nbits == -1) @@ -1577,8 +1586,8 @@ void CLASS phase_one_load_raw() unsigned CLASS ph1_bithuff (int nbits, ushort *huff) { - static UINT64 bitbuf=0; - static int vbits=0; + THREAD_LOCAL UINT64 bitbuf=0; + THREAD_LOCAL int vbits=0; unsigned c; if (nbits == -1) @@ -1840,8 +1849,8 @@ void CLASS nokia_load_raw() unsigned CLASS pana_bits (int nbits) { - static uchar buf[0x4000]; - static int vbits; + THREAD_LOCAL uchar buf[0x4000]; + THREAD_LOCAL int vbits; int byte; if (!nbits) return vbits=0; @@ -2130,7 +2139,7 @@ void CLASS kodak_jpeg_load_raw() {} METHODDEF(boolean) fill_input_buffer (j_decompress_ptr cinfo) { - static uchar jpeg_buffer[4096]; + THREAD_LOCAL uchar jpeg_buffer[4096]; size_t nbytes; nbytes = fread (jpeg_buffer, 1, 4096, ifp); @@ -2408,7 +2417,7 @@ void CLASS kodak_thumb_load_raw() void CLASS sony_decrypt (unsigned *data, int len, int start, int key) { - static unsigned pad[128], p; + THREAD_LOCAL unsigned pad[128], p; if (start) { for (p=0; p < 4; p++) @@ -2655,7 +2664,7 @@ void CLASS smal_v9_load_raw() void CLASS foveon_decoder (unsigned size, unsigned code) { - static unsigned huff[1024]; + THREAD_LOCAL unsigned huff[1024]; struct decode *cur; int i, len; @@ -7227,6 +7236,8 @@ canon_cr2: } else if (!strcmp(model,"D1X")) { width -= 4; pixel_aspect = 0.5; + } else if (!strcmp(model,"D7000")) { + width -= 40; } else if (!strcmp(model,"D40X") || !strcmp(model,"D60") || !strcmp(model,"D80") || @@ -8935,7 +8946,7 @@ int loadRaw (const char* fname, struct RawImage *ri) { { 0.222507, 0.716888, 0.060608 }, { 0.013930, 0.097097, 0.714022 } }; -dcrMutex->lock (); + THREAD_LOCK ifname = fname;//strdup (fname); image = NULL; @@ -8950,7 +8961,6 @@ dcrMutex->lock (); ri->profile_data = NULL; ifp = gfopen (fname); if (!ifp) { - dcrMutex->unlock (); return 3; } @@ -8962,7 +8972,6 @@ dcrMutex->lock (); use_camera_wb = 1; if (!is_raw) { fclose(ifp); - dcrMutex->unlock (); return 2; } @@ -8981,7 +8990,6 @@ dcrMutex->lock (); if (ri->data) free(ri->data); fclose (ifp); - dcrMutex->unlock (); return 100; } @@ -9068,15 +9076,14 @@ dcrMutex->lock (); ri->coeff[a][b] = rgb_cam[a][b]; free (image); -dcrMutex->unlock (); return 0; } int getRawFileBasicInfo (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int& rotation, int& thumbWidth, int& thumbHeight, int& thumbOffset, int& thumbType) { - int status=0; + THREAD_LOCK -dcrMutex->lock (); + int status=0; exif_base = -1; ciff_base = -1; @@ -9095,14 +9102,12 @@ dcrMutex->lock (); ifname = fname.c_str(); if (!(ifp = gfopen (ifname))) { status = 2; - dcrMutex->unlock (); return status; } identify (); if (!is_raw || colors>3) { status = 3; fclose (ifp); - dcrMutex->unlock (); return status; } @@ -9134,15 +9139,78 @@ dcrMutex->lock (); rml.ciffLength = ciff_len; fclose (ifp); -dcrMutex->unlock (); return !is_raw; } #include +rtengine::Thumbnail* rtengine::Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh) { + + THREAD_LOCK + + image = NULL; + ifname = fname.c_str(); + exif_base = -1; + ciff_base = -1; + ciff_len = -1; + verbose = settings->verbose; + oprof = NULL; + ifp = gfopen (fname.c_str()); + if (!ifp) { + printf("DCRAW: failed0\n"); + return NULL; + } + + if (setjmp (failure)) { + printf("DCRAW: failed1\n"); + fclose (ifp); + return NULL; + } + + identify (); + if (!is_raw || colors>3) { + printf("DCRAW: failed2\n"); + fclose(ifp); + return NULL; + } + + rml.exifBase = exif_base; + rml.ciffBase = ciff_base; + rml.ciffLength = ciff_len; + + char thumb_buffer[thumb_length]; + fseek(ifp,thumb_offset,SEEK_SET); + fread(thumb_buffer,1,thumb_length,ifp); + fclose(ifp); + + rtengine::Thumbnail* tpp = rtengine::Thumbnail::loadFromMemory(thumb_buffer,thumb_length,w,h,fixwh); + if ( tpp == 0 ) + { + printf("DCRAW: failed3\n"); + return NULL; + } + + int deg = 0; + if (flip==5) + deg = 270; + else if (flip==3) + deg = 180; + else if (flip==6) + deg = 90; + + if (deg>0) { + Image16* rot = tpp->thumbImg->rotate (deg); + delete tpp->thumbImg; + tpp->thumbImg = rot; + } + + return tpp; +} + rtengine::Thumbnail* rtengine::Thumbnail::loadFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh) { -dcrMutex->lock (); + THREAD_LOCK + MyTime t0, t1, t2, t3, t4, t5, t6; t0.set (); @@ -9155,7 +9223,6 @@ t0.set (); oprof = NULL; ifp = gfopen (fname.c_str()); if (!ifp) { - dcrMutex->unlock (); return NULL; } @@ -9165,7 +9232,6 @@ t1.set (); if (image) free (image); fclose (ifp); - dcrMutex->unlock (); return NULL; } @@ -9177,7 +9243,6 @@ t1.set (); use_camera_wb = 1; if (!is_raw || colors>3) { fclose(ifp); - dcrMutex->unlock (); return NULL; } @@ -9439,8 +9504,6 @@ if (settings->verbose) printf ("0: %d, 1: %d, 2: %d, 3: %d, 4: %d, 5: %d All: %d free (image); -dcrMutex->unlock (); - return tpp; } diff --git a/rtengine/dcraw.patch b/rtengine/dcraw.patch index 826580dff..fec902bbc 100644 --- a/rtengine/dcraw.patch +++ b/rtengine/dcraw.patch @@ -1,9 +1,8 @@ ---- dcraw.c 2010-06-30 10:40:16.000000000 -0600 -+++ dcraw.cc 2010-06-30 10:36:00.000000000 -0600 -@@ -1,3 +1,16 @@ +--- dcraw.c 2010-10-25 09:45:14.000000000 -0400 ++++ dcraw.cc 2010-10-27 11:30:33.000000000 -0400 +@@ -1,3 +1,15 @@ +/*RT*/#include +/*RT*/#include -+/*RT*/int ciff_base, ciff_len, exif_base, pre_filters; +/*RT*/#undef MAX +/*RT*/#undef MIN +/*RT*/#define NO_LCMS @@ -17,7 +16,7 @@ /* dcraw.c -- Dave Coffin's raw photo decoder Copyright 1997-2010 by Dave Coffin, dcoffin a cybercom o net -@@ -46,7 +59,9 @@ +@@ -46,7 +58,9 @@ NO_LCMS disables the "-p" option. */ #ifndef NO_JPEG @@ -28,17 +27,115 @@ #endif #ifndef NO_LCMS #include -@@ -101,7 +116,8 @@ +@@ -96,59 +110,70 @@ + typedef unsigned char uchar; + typedef unsigned short ushort; + ++// RT specify thread local storage ++#ifdef __GNUC__ ++#define THREAD_LOCAL static __thread ++#define THREAD_LOCK ++#else ++#define THREAD_LOCAL ++#define THREAD_LOCK Glib::Mutex::Lock locker(*dcrMutex); ++#endif ++ + /* + All global variables are defined here, and all functions that access them are prefixed with "CLASS". Note that a thread-safe C++ class cannot have non-const static local variables. */ -FILE *ifp, *ofp; -+/*RT*/IMFILE *ifp; -+FILE * ofp; - short order; - const char *ifname; - char *meta_data; -@@ -271,6 +287,7 @@ +-short order; +-const char *ifname; +-char *meta_data; +-char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64]; +-float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len; +-time_t timestamp; +-unsigned shot_order, kodak_cbpp, filters, exif_cfa, unique_id; +-off_t strip_offset, data_offset; +-off_t thumb_offset, meta_offset, profile_offset; +-unsigned thumb_length, meta_length, profile_length; +-unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0; +-unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress; +-unsigned black, cblack[8], maximum, mix_green, raw_color, zero_is_bad; +-unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error; +-unsigned tile_width, tile_length, gpsdata[32], load_flags; +-ushort raw_height, raw_width, height, width, top_margin, left_margin; +-ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height; +-int flip, tiff_flip, colors; +-double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 }; +-ushort (*image)[4], white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4]; +-float bright=1, user_mul[4]={0,0,0,0}, threshold=0; +-int half_size=0, four_color_rgb=0, document_mode=0, highlight=0; +-int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=-1; +-int output_color=1, output_bps=8, output_tiff=0, med_passes=0; +-int no_auto_bright=0; +-unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX }; +-float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4]; +-const double xyz_rgb[3][3] = { /* XYZ from RGB */ ++/*RT*/THREAD_LOCAL int ciff_base, ciff_len, exif_base, pre_filters; ++/*RT*/THREAD_LOCAL IMFILE *ifp; ++THREAD_LOCAL FILE * ofp; ++THREAD_LOCAL short order; ++THREAD_LOCAL const char *ifname; ++THREAD_LOCAL char *meta_data; ++THREAD_LOCAL char cdesc[5], desc[512], make[64], model[64], model2[64], artist[64]; ++THREAD_LOCAL float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len; ++THREAD_LOCAL time_t timestamp; ++THREAD_LOCAL unsigned shot_order, kodak_cbpp, filters, exif_cfa, unique_id; ++THREAD_LOCAL off_t strip_offset, data_offset; ++THREAD_LOCAL off_t thumb_offset, meta_offset, profile_offset; ++THREAD_LOCAL unsigned thumb_length, meta_length, profile_length; ++THREAD_LOCAL unsigned thumb_misc, *oprof, fuji_layout, shot_select=0, multi_out=0; ++THREAD_LOCAL unsigned tiff_nifds, tiff_samples, tiff_bps, tiff_compress; ++THREAD_LOCAL unsigned black, cblack[8], maximum, mix_green, raw_color, zero_is_bad; ++THREAD_LOCAL unsigned zero_after_ff, is_raw, dng_version, is_foveon, data_error; ++THREAD_LOCAL unsigned tile_width, tile_length, gpsdata[32], load_flags; ++THREAD_LOCAL ushort raw_height, raw_width, height, width, top_margin, left_margin; ++THREAD_LOCAL ushort shrink, iheight, iwidth, fuji_width, thumb_width, thumb_height; ++THREAD_LOCAL int flip, tiff_flip, colors; ++THREAD_LOCAL double pixel_aspect, aber[4]={1,1,1,1}, gamm[6]={ 0.45,4.5,0,0,0,0 }; ++THREAD_LOCAL ushort (*image)[4], white[8][8], curve[0x10000], cr2_slice[3], sraw_mul[4]; ++THREAD_LOCAL float bright=1, user_mul[4]={0,0,0,0}, threshold=0; ++THREAD_LOCAL int half_size=0, four_color_rgb=0, document_mode=0, highlight=0; ++THREAD_LOCAL int verbose=0, use_auto_wb=0, use_camera_wb=0, use_camera_matrix=-1; ++THREAD_LOCAL int output_color=1, output_bps=8, output_tiff=0, med_passes=0; ++THREAD_LOCAL int no_auto_bright=0; ++THREAD_LOCAL unsigned greybox[4] = { 0, 0, UINT_MAX, UINT_MAX }; ++THREAD_LOCAL float cam_mul[4], pre_mul[4], cmatrix[3][4], rgb_cam[3][4]; ++static const double xyz_rgb[3][3] = { /* XYZ from RGB */ + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } }; +-const float d65_white[3] = { 0.950456, 1, 1.088754 }; +-int histogram[4][0x2000]; +-void (*write_thumb)(), (*write_fun)(); +-void (*load_raw)(), (*thumb_load_raw)(); +-jmp_buf failure; ++static const float d65_white[3] = { 0.950456, 1, 1.088754 }; ++THREAD_LOCAL int histogram[4][0x2000]; ++THREAD_LOCAL void (*write_thumb)(), (*write_fun)(); ++THREAD_LOCAL void (*load_raw)(), (*thumb_load_raw)(); ++THREAD_LOCAL jmp_buf failure; + +-struct decode { ++THREAD_LOCAL struct decode { + struct decode *branch[2]; + int leaf; + } first_decode[2048], *second_decode, *free_decode; + +-struct tiff_ifd { ++THREAD_LOCAL struct tiff_ifd { + int width, height, bps, comp, phint, offset, flip, samples, bytes; + } tiff_ifd[10]; + +-struct ph1 { ++THREAD_LOCAL struct ph1 { + int format, key_off, black, black_off, split_col, tag_21a; + float tag_210; + } ph1; +@@ -271,6 +296,7 @@ fprintf (stderr,_("Corrupt data near 0x%llx\n"), (INT64) ftello(ifp)); } data_error++; @@ -46,7 +143,7 @@ } ushort CLASS sget2 (uchar *s) -@@ -344,7 +361,7 @@ +@@ -344,7 +370,7 @@ { if (fread (pixel, 2, count, ifp) < count) derror(); if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) @@ -55,7 +152,45 @@ } void CLASS canon_600_fixed_wb (int temp) -@@ -2117,7 +2134,7 @@ +@@ -538,8 +564,8 @@ + */ + unsigned CLASS getbithuff (int nbits, ushort *huff) + { +- static unsigned bitbuf=0; +- static int vbits=0, reset=0; ++ THREAD_LOCAL unsigned bitbuf=0; ++ THREAD_LOCAL int vbits=0, reset=0; + unsigned c; + + if (nbits == -1) +@@ -1560,8 +1586,8 @@ + + unsigned CLASS ph1_bithuff (int nbits, ushort *huff) + { +- static UINT64 bitbuf=0; +- static int vbits=0; ++ THREAD_LOCAL UINT64 bitbuf=0; ++ THREAD_LOCAL int vbits=0; + unsigned c; + + if (nbits == -1) +@@ -1823,8 +1849,8 @@ + + unsigned CLASS pana_bits (int nbits) + { +- static uchar buf[0x4000]; +- static int vbits; ++ THREAD_LOCAL uchar buf[0x4000]; ++ THREAD_LOCAL int vbits; + int byte; + + if (!nbits) return vbits=0; +@@ -2113,11 +2139,11 @@ + METHODDEF(boolean) + fill_input_buffer (j_decompress_ptr cinfo) + { +- static uchar jpeg_buffer[4096]; ++ THREAD_LOCAL uchar jpeg_buffer[4096]; size_t nbytes; nbytes = fread (jpeg_buffer, 1, 4096, ifp); @@ -64,7 +199,25 @@ cinfo->src->next_input_byte = jpeg_buffer; cinfo->src->bytes_in_buffer = nbytes; return TRUE; -@@ -3765,6 +3782,7 @@ +@@ -2391,7 +2417,7 @@ + + void CLASS sony_decrypt (unsigned *data, int len, int start, int key) + { +- static unsigned pad[128], p; ++ THREAD_LOCAL unsigned pad[128], p; + + if (start) { + for (p=0; p < 4; p++) +@@ -2638,7 +2664,7 @@ + + void CLASS foveon_decoder (unsigned size, unsigned code) + { +- static unsigned huff[1024]; ++ THREAD_LOCAL unsigned huff[1024]; + struct decode *cur; + int i, len; + +@@ -3765,6 +3791,7 @@ for (row = FC(1,0) >> 1; row < height; row+=2) for (col = FC(row,1) & 1; col < width; col+=2) image[row*width+col][1] = image[row*width+col][3]; @@ -72,7 +225,7 @@ filters &= ~((filters & 0x55555555) << 1); } } -@@ -4817,7 +4835,7 @@ +@@ -4817,7 +4844,7 @@ unsigned sony_curve[] = { 0,0,0,0,0,4095 }; unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; struct jhead jh; @@ -81,7 +234,7 @@ if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0]) return 1; -@@ -5225,12 +5243,13 @@ +@@ -5225,12 +5252,13 @@ fread (buf, sony_length, 1, ifp); sony_decrypt (buf, sony_length/4, 1, sony_key); sfp = ifp; @@ -100,7 +253,7 @@ ifp = sfp; free (buf); } -@@ -5256,6 +5275,8 @@ +@@ -5256,6 +5284,8 @@ int doff, max_samp=0, raw=-1, thm=-1, i; struct jhead jh; @@ -109,7 +262,7 @@ fseek (ifp, base, SEEK_SET); order = get2(); if (order != 0x4949 && order != 0x4d4d) return; -@@ -5424,7 +5445,7 @@ +@@ -5424,7 +5454,7 @@ { const char *file, *ext; char *jname, *jfile, *jext; @@ -118,7 +271,7 @@ ext = strrchr (ifname, '.'); file = strrchr (ifname, '/'); -@@ -5452,7 +5473,8 @@ +@@ -5452,7 +5482,8 @@ *jext = '0'; } if (strcmp (jname, ifname)) { @@ -128,7 +281,7 @@ if (verbose) fprintf (stderr,_("Reading metadata from %s ...\n"), jname); parse_tiff (12); -@@ -5790,7 +5812,11 @@ +@@ -5790,7 +5821,11 @@ order = get2(); hlen = get4(); if (get4() == 0x48454150) /* "HEAP" */ @@ -140,7 +293,7 @@ parse_tiff (save+6); fseek (ifp, save+len, SEEK_SET); } -@@ -6738,6 +6764,12 @@ +@@ -6738,6 +6773,12 @@ fread (head, 1, 32, ifp); fseek (ifp, 0, SEEK_END); flen = fsize = ftell(ifp); @@ -153,7 +306,7 @@ if ((cp = (char *) memmem (head, 32, "MMMM", 4)) || (cp = (char *) memmem (head, 32, "IIII", 4))) { parse_phase_one (cp-head); -@@ -6745,6 +6777,8 @@ +@@ -6745,6 +6786,8 @@ } else if (order == 0x4949 || order == 0x4d4d) { if (!memcmp (head+6,"HEAPCCDR",8)) { data_offset = hlen; @@ -162,7 +315,25 @@ parse_ciff (hlen, flen - hlen); } else { parse_tiff(0); -@@ -8494,13 +8528,13 @@ +@@ -6858,6 +6901,8 @@ + if (height == 3136 && width == 4736) /* Pentax K-7 */ + { height = 3122; width = 4684; + top_margin = 2; filters = 0x16161616; } ++ if (height == 2868 && width == 4352) /* Pentax K-x */ ++ width = 4308; + if (height == 3014 && width == 4096) /* Ricoh GX200 */ + width = 4014; + if (dng_version) { +@@ -7405,7 +7450,7 @@ + } else if (!strcmp(model,"K20D")) { + filters = 0x16161616; + } else if (!strcmp(model,"K-x")) { +- width = 4309; ++ width = 4308; + filters = 0x16161616; + } else if (!strcmp(model,"Optio S")) { + if (fsize == 3178560) { +@@ -8494,13 +8539,13 @@ FORCC ppm [col*colors+c] = curve[image[soff][c]] >> 8; else FORCC ppm2[col*colors+c] = curve[image[soff][c]]; if (output_bps == 16 && !output_tiff && htons(0x55aa) != 0x55aa) @@ -178,7 +349,7 @@ { int arg, status=0; int timestamp_only=0, thumbnail_only=0, identify_only=0; -@@ -8613,7 +8647,7 @@ +@@ -8613,7 +8658,7 @@ case 'i': identify_only = 1; break; case 'c': write_to_stdout = 1; break; case 'v': verbose = 1; break; @@ -187,7 +358,7 @@ case 'f': four_color_rgb = 1; break; case 'A': FORC4 greybox[c] = atoi(argv[arg++]); case 'a': use_auto_wb = 1; break; -@@ -8877,3 +8911,537 @@ +@@ -8877,3 +8922,526 @@ } return status; } @@ -213,7 +384,7 @@ + { 0.222507, 0.716888, 0.060608 }, + { 0.013930, 0.097097, 0.714022 } }; + -+dcrMutex->lock (); ++ THREAD_LOCK + + ifname = fname;//strdup (fname); + image = NULL; @@ -228,7 +399,6 @@ + ri->profile_data = NULL; + ifp = gfopen (fname); + if (!ifp) { -+ dcrMutex->unlock (); + return 3; + } + @@ -240,7 +410,6 @@ + use_camera_wb = 1; + if (!is_raw) { + fclose(ifp); -+ dcrMutex->unlock (); + return 2; + } + @@ -259,7 +428,6 @@ + if (ri->data) + free(ri->data); + fclose (ifp); -+ dcrMutex->unlock (); + return 100; + } + @@ -346,15 +514,14 @@ + ri->coeff[a][b] = rgb_cam[a][b]; + + free (image); -+dcrMutex->unlock (); + return 0; +} + +int getRawFileBasicInfo (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int& rotation, int& thumbWidth, int& thumbHeight, int& thumbOffset, int& thumbType) { + -+ int status=0; ++ THREAD_LOCK + -+dcrMutex->lock (); ++ int status=0; + + exif_base = -1; + ciff_base = -1; @@ -373,14 +540,12 @@ + ifname = fname.c_str(); + if (!(ifp = gfopen (ifname))) { + status = 2; -+ dcrMutex->unlock (); + return status; + } + identify (); + if (!is_raw || colors>3) { + status = 3; + fclose (ifp); -+ dcrMutex->unlock (); + return status; + } + @@ -412,7 +577,6 @@ + rml.ciffLength = ciff_len; + + fclose (ifp); -+dcrMutex->unlock (); + return !is_raw; +} + @@ -420,7 +584,8 @@ + +rtengine::Thumbnail* rtengine::Thumbnail::loadFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh) { + -+dcrMutex->lock (); ++ THREAD_LOCK ++ +MyTime t0, t1, t2, t3, t4, t5, t6; +t0.set (); + @@ -433,7 +598,6 @@ + oprof = NULL; + ifp = gfopen (fname.c_str()); + if (!ifp) { -+ dcrMutex->unlock (); + return NULL; + } + @@ -443,7 +607,6 @@ + if (image) + free (image); + fclose (ifp); -+ dcrMutex->unlock (); + return NULL; + } + @@ -455,7 +618,6 @@ + use_camera_wb = 1; + if (!is_raw || colors>3) { + fclose(ifp); -+ dcrMutex->unlock (); + return NULL; + } + @@ -610,7 +772,7 @@ + + // generate histogram for auto exposure + tpp->aeHistCompression = 3; -+ tpp->aeHistogram = new int[65536>>tpp->aeHistCompression]; ++ tpp->aeHistogram = new unsigned int[65536>>tpp->aeHistCompression]; + memset (tpp->aeHistogram, 0, (65536>>tpp->aeHistCompression)*sizeof(int)); + int radd = 4; + int gadd = 2; @@ -717,8 +879,6 @@ + + free (image); + -+dcrMutex->unlock (); -+ + return tpp; +} + diff --git a/rtengine/iccstore.cc b/rtengine/iccstore.cc index 748a3046d..f5e96f713 100644 --- a/rtengine/iccstore.cc +++ b/rtengine/iccstore.cc @@ -27,8 +27,6 @@ namespace rtengine { -ICCStore iccStore; - const double (*wprofiles[])[3] = {sRGB_d50, adobe_d50, prophoto_d50, widegamut_d50, bruce_d50, beta_d50, best_d50}; const double (*iwprofiles[])[3] = {d50_sRGB, d50_adobe, d50_prophoto, d50_widegamut, d50_bruce, d50_beta, d50_best}; const char* wpnames[] = {"sRGB", "Adobe RGB", "ProPhoto", "WideGamut", "BruceRGB", "Beta RGB", "BestRGB"}; @@ -43,11 +41,13 @@ std::vector getWorkingProfiles () { std::vector getOutputProfiles () { - return iccStore.getOutputProfiles (); + return iccStore->getOutputProfiles (); } std::vector ICCStore::getOutputProfiles () { + Glib::Mutex::Lock lock(mutex_); + std::vector res; for (std::map::iterator i=fileProfiles.begin(); i!=fileProfiles.end(); i++) res.push_back (i->first); @@ -55,14 +55,30 @@ std::vector ICCStore::getOutputProfiles () { } -ICCStore::ICCStore () { +ICCStore* +ICCStore::getInstance(void) +{ + static ICCStore* instance_ = 0; + if ( instance_ == 0 ) + { + static Glib::Mutex smutex_; + Glib::Mutex::Lock lock(smutex_); + if ( instance_ == 0 ) + { + instance_ = new ICCStore(); + } + } + return instance_; +} +ICCStore::ICCStore () +{ cmsErrorAction (LCMS_ERROR_SHOW); int N = sizeof(wpnames)/sizeof(wpnames[0]); for (int i=0; i::iterator r = fileProfiles.find (name); if (r!=fileProfiles.end()) @@ -137,11 +154,15 @@ cmsHPROFILE ICCStore::getProfile (Glib::ustring name) { ProfileContent ICCStore::getContent (Glib::ustring name) { + Glib::Mutex::Lock lock(mutex_); + return fileProfileContents[name]; } std::vector ICCStore::parseDir (Glib::ustring pdir) { + Glib::Mutex::Lock lock(mutex_); + fileProfiles.clear (); fileProfileContents.clear (); std::vector result; @@ -309,6 +330,8 @@ cmsHPROFILE ICCStore::createFromMatrix (const double matrix[3][3], bool gamma, G strcpy ((char *)oprof+pbody[5]+12, name.c_str()); - return cmsOpenProfileFromMem (oprof, ntohl(oprof[0])); + cmsHPROFILE p = cmsOpenProfileFromMem (oprof, ntohl(oprof[0])); + delete [] oprof; + return p; } } diff --git a/rtengine/iccstore.h b/rtengine/iccstore.h index 3c0a9dc49..5e67b55a7 100644 --- a/rtengine/iccstore.h +++ b/rtengine/iccstore.h @@ -54,9 +54,14 @@ class ICCStore { cmsHPROFILE xyz; cmsHPROFILE srgb; + + Glib::Mutex mutex_; + + ICCStore (); public: - ICCStore (); + + static ICCStore* getInstance(void); int numOfWProfiles (); cmsHPROFILE createFromMatrix (const double matrix[3][3], bool gamma=false, Glib::ustring name=""); @@ -74,7 +79,7 @@ class ICCStore { std::vector getOutputProfiles (); }; -extern ICCStore iccStore; +#define iccStore ICCStore::getInstance() //extern const char* wpnames[]; } diff --git a/rtengine/image16.cc b/rtengine/image16.cc index 1afe0e258..1f6227f98 100644 --- a/rtengine/image16.cc +++ b/rtengine/image16.cc @@ -17,6 +17,7 @@ * along with RawTherapee. If not, see . */ #include +#include #include #include @@ -239,3 +240,19 @@ Image16* Image16::resize (int nw, int nh, TypeInterpolation interp) { } return NULL; } + +Image8* +Image16::to8() const +{ + Image8* img8 = new Image8(width,height); + for ( int h = 0; h < height; ++h ) + { + for ( int w = 0; w < width; ++w ) + { + img8->r(h,w,r[h][w] >> 8); + img8->g(h,w,g[h][w] >> 8); + img8->b(h,w,b[h][w] >> 8); + } + } + return img8; +} diff --git a/rtengine/image16.h b/rtengine/image16.h index 8a778215b..ba719535b 100644 --- a/rtengine/image16.h +++ b/rtengine/image16.h @@ -29,6 +29,8 @@ namespace rtengine { enum TypeInterpolation { TI_Nearest, TI_Bilinear }; +class Image8; + class Image16 : public ImageIO, public IImage16 { private: @@ -54,6 +56,8 @@ class Image16 : public ImageIO, public IImage16 { Image16* copy (); + Image8* to8() const; + Image16* rotate (int deg); Image16* hflip (); Image16* vflip (); diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index 59c32ebc5..432fafae2 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -259,6 +259,75 @@ int ImageIO::loadPNG (Glib::ustring fname) { extern jmp_buf jpeg_jmp_buf; +extern "C" { +void jpeg_memory_src (jpeg_decompress_struct* cinfo, const char* buffer, int bufsize); +} + +int ImageIO::loadJPEGFromMemory (const char* buffer, int bufsize) +{ + jpeg_decompress_struct cinfo; + jpeg_error_mgr jerr; + cinfo.err = my_jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_memory_src (&cinfo,buffer,bufsize); + + if (pl) { + pl->setProgressStr ("Loading JPEG file..."); + pl->setProgress (0.0); + + } + + setup_read_icc_profile (&cinfo); + + if (!setjmp(jpeg_jmp_buf)) { + jpeg_memory_src (&cinfo,buffer,bufsize); + jpeg_read_header(&cinfo, TRUE); + + unsigned int proflen; + delete loadedProfileData; + loadedProfileData = NULL; + bool hasprofile = read_icc_profile (&cinfo, (JOCTET**)&loadedProfileData, (unsigned int*)&loadedProfileLength); + if (hasprofile) + embProfile = cmsOpenProfileFromMem (loadedProfileData, loadedProfileLength); + else + embProfile = NULL; + + jpeg_start_decompress(&cinfo); + + int width = cinfo.output_width; + int height = cinfo.output_height; + + allocate (width, height); + + unsigned char *row=new unsigned char[width*3]; + while (cinfo.output_scanline < height) { + if (jpeg_read_scanlines(&cinfo,&row,1) < 1) { + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + delete [] row; + return IMIO_READERROR; + } + setScanline (cinfo.output_scanline-1, row, 8); + + if (pl && !(cinfo.output_scanline%100)) + pl->setProgress ((double)(cinfo.output_scanline)/cinfo.output_height); + } + delete [] row; + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + if (pl) { + pl->setProgressStr ("Ready."); + pl->setProgress (1.0); + } + return IMIO_SUCCESS; + } + else { + jpeg_destroy_decompress(&cinfo); + return IMIO_READERROR; + } +} + int ImageIO::loadJPEG (Glib::ustring fname) { FILE *file=g_fopen(fname.c_str(),"rb"); diff --git a/rtengine/imageio.h b/rtengine/imageio.h index 5c7a2a033..a036060b0 100644 --- a/rtengine/imageio.h +++ b/rtengine/imageio.h @@ -72,6 +72,8 @@ class ImageIO { int loadJPEG (Glib::ustring fname); int loadTIFF (Glib::ustring fname); + int loadJPEGFromMemory (const char* buffer, int bufsize); + int savePNG (Glib::ustring fname, int compression = -1, int bps = -1); int saveJPEG (Glib::ustring fname, int quality = 100); int saveTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 8107175f7..07e771092 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -124,7 +124,7 @@ void ImProcFunctions::setScale (double iscale) { void ImProcFunctions::firstAnalysis_ (Image16* original, Glib::ustring wprofile, unsigned int* histogram, int* chroma_radius, int row_from, int row_to) { - TMatrix wprof = iccStore.workingSpaceMatrix (wprofile); + TMatrix wprof = iccStore->workingSpaceMatrix (wprofile); int toxyz[3][3]; toxyz[0][0] = round(32768.0 * wprof[0][0] / 0.96422); toxyz[1][0] = round(32768.0 * wprof[1][0] / 0.96422); @@ -184,12 +184,12 @@ void ImProcFunctions::firstAnalysis (Image16* original, const ProcParams* params if (monitorTransform) cmsDeleteTransform (monitorTransform); monitorTransform = NULL; - cmsHPROFILE monitor = iccStore.getProfile ("file:"+settings->monitorProfile); + cmsHPROFILE monitor = iccStore->getProfile ("file:"+settings->monitorProfile); if (monitor) { - cmsHPROFILE iprof = iccStore.getXYZProfile (); - cmsHPROFILE oprof = iccStore.getProfile (params->icm.output); + cmsHPROFILE iprof = iccStore->getXYZProfile (); + cmsHPROFILE oprof = iccStore->getProfile (params->icm.output); if (!oprof) - oprof = iccStore.getsRGBProfile (); + oprof = iccStore->getsRGBProfile (); lcmsMutex->lock (); monitorTransform = cmsCreateTransform (iprof, TYPE_RGB_16, monitor, TYPE_RGB_8, settings->colorimetricIntent, 0); lcmsMutex->unlock (); @@ -257,7 +257,7 @@ void ImProcFunctions::rgbProc (Image16* working, LabImage* lab, int* tonecurve1, bool processLCE = params->sh.enabled && shmap!=NULL && params->sh.localcontrast>0; double lceamount = params->sh.localcontrast / 200.0; - TMatrix wprof = iccStore.workingSpaceMatrix (params->icm.working); + TMatrix wprof = iccStore->workingSpaceMatrix (params->icm.working); int toxyz[3][3] = { { floor(32768.0 * wprof[0][0] / 0.96422), @@ -324,19 +324,39 @@ void ImProcFunctions::rgbProc (Image16* working, LabImage* lab, int* tonecurve1, /*r = tonecurve[r]; g = tonecurve[g]; b = tonecurve[b];*/ - int Y = (int)(0.299*r + 0.587*g + 0.114*b); - float tonefactor = (Y>0 ? (float)tonecurve1[Y]/Y : 1); - /*float rtonefactor = (r>0 ? (float)tonecurve1[r]/r : 1); - float gtonefactor = (g>0 ? (float)tonecurve1[g]/g : 1); - float btonefactor = (b>0 ? (float)tonecurve1[b]/b : 1); - float tonefactor = MIN(rtonefactor, MIN(gtonefactor,btonefactor));*/ + int Y = (0.299*r + 0.587*g + 0.114*b); + int Ynew = tonecurve1[Y]; + float tonefactor = (Y>0 ? (float)Ynew/Y : 1); r *= tonefactor; g *= tonefactor; b *= tonefactor; + /*float maxfactor = 1; + if (r>65535) + maxfactor = MIN(maxfactor, (float)(65535.0f-Ynew)/(r-Ynew)); + if (g>65535) + maxfactor = MIN(maxfactor, (float)(65535.0f-Ynew)/(g-Ynew)); + if (b>65535) + maxfactor = MIN(maxfactor, (float)(65535.0f-Ynew)/(b-Ynew)); + + if (r<0) + maxfactor = MIN(maxfactor, ((float)Ynew)/(Ynew-r)); + if (g<0) + maxfactor = MIN(maxfactor, ((float)Ynew)/(Ynew-g)); + if (b<0) + maxfactor = MIN(maxfactor, ((float)Ynew)/(Ynew-b)); + + float U = (float)(-0.14713*r - 0.28886*g + 0.436*b)*maxfactor; + float V = (float)(0.615*r - 0.51499*g - 0.10001*b)*maxfactor; + + r = CLIP(Ynew + 1.13983*V); + g = CLIP(Ynew - 0.39465*U - 0.58060*V); + b = CLIP(Ynew + 2.03211*U);*/ + r = tonecurve2[CLIP(r)]; g = tonecurve2[CLIP(g)]; b = tonecurve2[CLIP(b)]; + int x = (toxyz[0][0] * r + toxyz[1][0] * g + toxyz[2][0] * b) >> 15; int y = (toxyz[0][1] * r + toxyz[1][1] * g + toxyz[2][1] * b) >> 15; @@ -347,7 +367,6 @@ void ImProcFunctions::rgbProc (Image16* working, LabImage* lab, int* tonecurve1, z = CLIPTO(z,0,2*65536-1); int L = cacheL[y]; - //int L = cacheL[tonecurve[y]];//for luminance tone curve, use this (and comment out rgb tonecurves) lab->L[i][j] = L; lab->a[i][j] = CLIPC(((cachea[x] - cachea[y]) * chroma_scale) >> 15); lab->b[i][j] = CLIPC(((cacheb[y] - cacheb[z]) * chroma_scale) >> 15); @@ -540,7 +559,7 @@ void ImProcFunctions::getAutoExp (unsigned int* histogram, int histcompr, doubl double corr = pow(2.0, expcomp); // black point selection is based on the linear result (yielding better visual results) - bl = (int)(shc * corr); + bl = (int)(shc /* * corr*/); // compute the white point of the exp. compensated gamma corrected image double awg = (int)(CurveFactory::gamma2 (aw * corr / 65536.0) * 65536.0); diff --git a/rtengine/init.cc b/rtengine/init.cc index 167ca3e3a..6e8874160 100644 --- a/rtengine/init.cc +++ b/rtengine/init.cc @@ -32,7 +32,7 @@ Glib::Mutex* lcmsMutex = NULL; int init (const Settings* s) { settings = s; - iccStore.parseDir (s->iccDirectory); + iccStore->parseDir (s->iccDirectory); CurveFactory::init (); ImProcFunctions::initCache (); delete dcrMutex; diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index cf94f25e7..95900da69 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -114,10 +114,10 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, Image8* image = new Image8 (cw, ch); - cmsHPROFILE oprof = iccStore.getProfile (profile); + cmsHPROFILE oprof = iccStore->getProfile (profile); if (oprof) { - cmsHPROFILE iprof = iccStore.getXYZProfile (); + cmsHPROFILE iprof = iccStore->getXYZProfile (); lcmsMutex->lock (); cmsHTRANSFORM hTransform = cmsCreateTransform (iprof, TYPE_RGB_16, oprof, TYPE_RGB_8, settings->colorimetricIntent, 0); lcmsMutex->unlock (); @@ -193,7 +193,7 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int Image16* image = new Image16 (cw, ch); - cmsHPROFILE oprof = iccStore.getProfile (profile); + cmsHPROFILE oprof = iccStore->getProfile (profile); if (oprof) { #pragma omp parallel for if (multiThread) @@ -222,7 +222,7 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int za[j-cx] = CLIP(z_); } } - cmsHPROFILE iprof = iccStore.getXYZProfile (); + cmsHPROFILE iprof = iccStore->getXYZProfile (); lcmsMutex->lock (); cmsHTRANSFORM hTransform = cmsCreateTransform (iprof, TYPE_RGB_16_PLANAR, oprof, TYPE_RGB_16_PLANAR, settings->colorimetricIntent, 0); lcmsMutex->unlock (); diff --git a/rtengine/iptransform.cc b/rtengine/iptransform.cc index ae053c1e9..682738f16 100644 --- a/rtengine/iptransform.cc +++ b/rtengine/iptransform.cc @@ -223,25 +223,42 @@ void ImProcFunctions::transform (Image16* original, Image16* transformed, int cx transformSep (original, transformed, cx, cy, sx, sy, oW, oH); } +void calcVignettingParams(int oW, int oH, const VignettingParams& vignetting, double &w2, double &h2, double& maxRadius, double &v, double &b, double &mul) +{ + // vignette center is a point with coordinates between -1 and +1 + double x = vignetting.centerX / 100.0; + double y = vignetting.centerY / 100.0; + + // calculate vignette center in pixels + w2 = (double) oW / 2.0 - 0.5 + x * oW; + h2 = (double) oH / 2.0 - 0.5 + y * oH; + + // max vignette radius in pixels + maxRadius = sqrt( (double)( oW*oW + oH*oH ) ) / 2.; + + // vignette variables with applied strength + v = 1.0 - vignetting.strength * vignetting.amount * 3.0 / 400.0; + b = 1.0 + vignetting.radius * 7.0 / 100.0; + mul = (1.0-v) / tanh(b); +} + void ImProcFunctions::vignetting (Image16* original, Image16* transformed, int cx, int cy, int oW, int oH) { - double w2 = (double) oW / 2.0 - 0.5; - double h2 = (double) oH / 2.0 - 0.5; - - double maxRadius = sqrt( (double)( oW*oW + oH*oH ) ) / 2.; - - double v = 1.0 - params->vignetting.amount * 3.0 / 400.0; - double b = 1.0 + params->vignetting.radius * 7.0 / 100.0; - - double mul = (1.0-v) / tanh(b); + double vig_w2; + double vig_h2; + double maxRadius; + double v; + double b; + double mul; + calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); #pragma omp parallel for if (multiThread) for (int y=0; yheight; y++) { - double y_d = (double) (y + cy) - h2 ; + double vig_y_d = (double) (y + cy) - vig_h2 ; int val; for (int x=0; xwidth; x++) { - double x_d = (double) (x + cx) - w2 ; - double r = sqrt(x_d*x_d + y_d*y_d); + double vig_x_d = (double) (x + cx) - vig_w2 ; + double r = sqrt(vig_x_d*vig_x_d + vig_y_d*vig_y_d); double vign = v + mul * tanh (b*(maxRadius-r) / maxRadius); val = original->r[y][x] / vign; transformed->r[y][x] = CLIP(val); @@ -255,9 +272,16 @@ void ImProcFunctions::vignetting (Image16* original, Image16* transformed, int c #include "cubint.cc" void ImProcFunctions::transformNonSep (Image16* original, Image16* transformed, int cx, int cy, int sx, int sy, int oW, int oH) { + double w2 = (double) oW / 2.0 - 0.5; + double h2 = (double) oH / 2.0 - 0.5; - double w2 = (double) oW / 2.0 - 0.5; - double h2 = (double) oH / 2.0 - 0.5; + double vig_w2; + double vig_h2; + double maxRadius; + double v; + double b; + double mul; + calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); // auxiliary variables for distortion correction double a = params->distortion.amount; @@ -266,11 +290,6 @@ void ImProcFunctions::transformNonSep (Image16* original, Image16* transformed, double cost = cos(params->rotate.degree * 3.14/180.0); double sint = sin(params->rotate.degree * 3.14/180.0); - // auxiliary variables for vignetting - double maxRadius = sqrt( (double)( oW*oW + oH*oH ) ) / 2.; - double v = 1.0 - params->vignetting.amount * 3.0 / 400.0; - double b = 1.0 + params->vignetting.radius * 7.0 / 100.0; - double mul = (1.0-v) / tanh(b); bool dovign = params->vignetting.amount != 0; // auxiliary variables for vertical perspective correction @@ -293,6 +312,8 @@ void ImProcFunctions::transformNonSep (Image16* original, Image16* transformed, for (int x=0; xwidth; x++) { double x_d = ascale * (x + cx - w2); // centering x coord & scale double y_d = ascale * (y + cy - h2); // centering y coord & scale + double vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale + double vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale // horizontal perspective transformation y_d = y_d * maxRadius / (maxRadius + x_d*hptanpt); @@ -308,11 +329,14 @@ void ImProcFunctions::transformNonSep (Image16* original, Image16* transformed, // distortion correction double r = sqrt(Dx*Dx + Dy*Dy) / maxRadius; - double r2 = sqrt(Dx*Dx + Dy*Dy); double s = 1.0 - a + a * r ; Dx *= s; Dy *= s; + double vig_Dx = vig_x_d * cost - vig_y_d * sint; + double vig_Dy = vig_x_d * sint + vig_y_d * cost; + double r2 = sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy); + // de-center Dx += w2; Dy += h2; @@ -357,8 +381,16 @@ void ImProcFunctions::transformNonSep (Image16* original, Image16* transformed, #include "cubintch.cc" void ImProcFunctions::transformSep (Image16* original, Image16* transformed, int cx, int cy, int sx, int sy, int oW, int oH) { - double w2 = (double) oW / 2.0 - 0.5; - double h2 = (double) oH / 2.0 - 0.5; + double w2 = (double) oW / 2.0 - 0.5; + double h2 = (double) oH / 2.0 - 0.5; + + double vig_w2; + double vig_h2; + double maxRadius; + double v; + double b; + double mul; + calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); // auxiliary variables for c/a correction double cdist[3]; @@ -381,11 +413,6 @@ void ImProcFunctions::transformSep (Image16* original, Image16* transformed, int double cost = cos(params->rotate.degree * 3.14/180.0); double sint = sin(params->rotate.degree * 3.14/180.0); - // auxiliary variables for vignetting - double maxRadius = sqrt( (double)( oW*oW + oH*oH ) ) / 2.; - double v = 1.0 - params->vignetting.amount * 3.0 / 400.0; - double b = 1.0 + params->vignetting.radius * 7.0 / 100.0; - double mul = (1.0-v) / tanh(b); bool dovign = params->vignetting.amount != 0; // auxiliary variables for vertical perspective correction @@ -408,6 +435,8 @@ void ImProcFunctions::transformSep (Image16* original, Image16* transformed, int for (int x=0; xwidth; x++) { double x_d = ascale * (x + cx - w2); // centering x coord & scale double y_d = ascale * (y + cy - h2); // centering y coord & scale + double vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale + double vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale // horizontal perspective transformation y_d = y_d * maxRadius / (maxRadius + x_d*hptanpt); @@ -423,9 +452,12 @@ void ImProcFunctions::transformSep (Image16* original, Image16* transformed, int // distortion correction double r = sqrt(Dxc*Dxc + Dyc*Dyc) / maxRadius; - double r2 = sqrt(Dxc*Dxc + Dyc*Dyc); double s = 1.0 - a + a * r ; + double vig_Dx = vig_x_d * cost - vig_y_d * sint; + double vig_Dy = vig_x_d * sint + vig_y_d * cost; + double r2 = sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy); + for (int c=0; c<3; c++) { double Dx = Dxc * (s + cdist[c]); @@ -468,8 +500,16 @@ void ImProcFunctions::transformSep (Image16* original, Image16* transformed, int void ImProcFunctions::simpltransform (Image16* original, Image16* transformed, int cx, int cy, int sx, int sy, int oW, int oH) { - double w2 = (double) oW / 2.0 - 0.5; - double h2 = (double) oH / 2.0 - 0.5; + double w2 = (double) oW / 2.0 - 0.5; + double h2 = (double) oH / 2.0 - 0.5; + + double vig_w2; + double vig_h2; + double maxRadius; + double v; + double b; + double mul; + calcVignettingParams(oW, oH, params->vignetting, vig_w2, vig_h2, maxRadius, v, b, mul); // auxiliary variables for distortion correction double a = params->distortion.amount; @@ -478,11 +518,6 @@ void ImProcFunctions::simpltransform (Image16* original, Image16* transformed, i double cost = cos(params->rotate.degree * 3.14/180.0); double sint = sin(params->rotate.degree * 3.14/180.0); - // auxiliary variables for vignetting - double maxRadius = sqrt( (double)( oW*oW + oH*oH ) ) / 2.; - double v = 1.0 - params->vignetting.amount * 3.0 / 400.0; - double b = 1.0 + params->vignetting.radius * 7.0 / 100.0; - double mul = (1.0-v) / tanh(b); bool dovign = params->vignetting.amount != 0; // auxiliary variables for vertical perspective correction @@ -505,6 +540,8 @@ void ImProcFunctions::simpltransform (Image16* original, Image16* transformed, i for (int x=0; xwidth; x++) { double y_d = ascale * (y + cy - h2); // centering y coord & scale double x_d = ascale * (x + cx - w2); // centering x coord & scale + double vig_x_d = ascale * (x + cx - vig_w2); // centering x coord & scale + double vig_y_d = ascale * (y + cy - vig_h2); // centering y coord & scale // horizontal perspective transformation y_d = y_d * maxRadius / (maxRadius + x_d*hptanpt); @@ -520,11 +557,14 @@ void ImProcFunctions::simpltransform (Image16* original, Image16* transformed, i // distortion correction double r = sqrt(Dx*Dx + Dy*Dy) / maxRadius; - double r2 = sqrt(Dx*Dx + Dy*Dy); double s = 1.0 - a + a * r ; Dx *= s; Dy *= s; + double vig_Dx = vig_x_d * cost - vig_y_d * sint; + double vig_Dy = vig_x_d * sint + vig_y_d * cost; + double r2 = sqrt(vig_Dx*vig_Dx + vig_Dy*vig_Dy); + // de-center Dx += w2; Dy += h2; diff --git a/rtengine/jpeg_memsrc.c b/rtengine/jpeg_memsrc.c new file mode 100644 index 000000000..595649b85 --- /dev/null +++ b/rtengine/jpeg_memsrc.c @@ -0,0 +1,167 @@ +/* + * memsrc.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from a memory buffer that is preloaded with the entire + * JPEG file. This would not seem especially useful at first sight, but + * a number of people have asked for it. + * This is really just a stripped-down version of jdatasrc.c. Comparison + * of this code with jdatasrc.c may be helpful in seeing how to make + * custom source managers for other purposes. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +//#include "jinclude.h" +//#include "jmorecfg.h" +//#include "jpeglib.h" +//#include "jerror.h" +#include +#include +#include + + +/* Expanded data source object for memory input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + JOCTET eoi_buffer[2]; /* a place to put a dummy EOI */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +static void +init_source (j_decompress_ptr cinfo) +{ + /* No work, since jpeg_memory_src set up the buffer pointer and count. + * Indeed, if we want to read multiple JPEG images from one buffer, + * this *must* not do anything to the pointer. + */ +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In this application, this routine should never be called; if it is called, + * the decompressor has overrun the end of the input buffer, implying we + * supplied an incomplete or corrupt JPEG datastream. A simple error exit + * might be the most appropriate response. + * + * But what we choose to do in this code is to supply dummy EOI markers + * in order to force the decompressor to finish processing and supply + * some sort of output image, no matter how corrupted. + */ + +static int +fill_input_buffer(j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + WARNMS(cinfo, JWRN_JPEG_EOF); + + /* Create a fake EOI marker */ + src->eoi_buffer[0] = (JOCTET) 0xFF; + src->eoi_buffer[1] = (JOCTET) JPEG_EOI; + src->pub.next_input_byte = src->eoi_buffer; + src->pub.bytes_in_buffer = 2; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * If we overrun the end of the buffer, we let fill_input_buffer deal with + * it. An extremely large skip could cause some time-wasting here, but + * it really isn't supposed to happen ... and the decompressor will never + * skip more than 64K anyway. + */ + +static void +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +static void +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a memory buffer. + */ + +GLOBAL(void) +jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET * buffer, size_t bufsize) +{ + my_src_ptr src; + + /* The source object is made permanent so that a series of JPEG images + * can be read from a single buffer by calling jpeg_memory_src + * only before the first one. + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_source_mgr)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + + src->pub.next_input_byte = buffer; + src->pub.bytes_in_buffer = bufsize; +} diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index c58306e48..674d66e2b 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -46,13 +46,13 @@ void ProcParams::destroy (ProcParams* pp) { void ProcParams::setDefaults () { toneCurve.autoexp = false; - toneCurve.clip = 0.002; + toneCurve.clip = 0.001; toneCurve.expcomp = 0; toneCurve.brightness = 0; toneCurve.contrast = 0; toneCurve.black = 0; toneCurve.hlcompr = 70; - toneCurve.shcompr = 85; + toneCurve.shcompr = 25; toneCurve.curve.clear (); labCurve.brightness = 0; @@ -147,6 +147,9 @@ void ProcParams::setDefaults () { vignetting.amount = 0; vignetting.radius = 50; + vignetting.strength = 1; + vignetting.centerX = 0; + vignetting.centerY = 0; chmixer.red[0] = 100; chmixer.red[1] = 0; @@ -326,6 +329,9 @@ int ProcParams::save (Glib::ustring fname) const { // save vignetting correction keyFile.set_integer ("Vignetting Correction", "Amount", vignetting.amount); keyFile.set_integer ("Vignetting Correction", "Radius", vignetting.radius); + keyFile.set_integer ("Vignetting Correction", "Strength", vignetting.strength); + keyFile.set_integer ("Vignetting Correction", "CenterX", vignetting.centerX); + keyFile.set_integer ("Vignetting Correction", "CenterY", vignetting.centerY); // save highlight recovery settings keyFile.set_boolean ("HLRecovery", "Enabled", hlrecovery.enabled); @@ -580,6 +586,9 @@ if (keyFile.has_group ("CACorrection")) { if (keyFile.has_group ("Vignetting Correction")) { if (keyFile.has_key ("Vignetting Correction", "Amount")) vignetting.amount = keyFile.get_integer ("Vignetting Correction", "Amount"); if (keyFile.has_key ("Vignetting Correction", "Radius")) vignetting.radius = keyFile.get_integer ("Vignetting Correction", "Radius"); + if (keyFile.has_key ("Vignetting Correction", "Strength")) vignetting.strength = keyFile.get_integer ("Vignetting Correction", "Strength"); + if (keyFile.has_key ("Vignetting Correction", "CenterX")) vignetting.centerX = keyFile.get_integer ("Vignetting Correction", "CenterX"); + if (keyFile.has_key ("Vignetting Correction", "CenterY")) vignetting.centerY = keyFile.get_integer ("Vignetting Correction", "CenterY"); } // load highlight recovery settings @@ -775,6 +784,9 @@ bool ProcParams::operator== (const ProcParams& other) { && cacorrection.blue == other.cacorrection.blue && vignetting.amount == other.vignetting.amount && vignetting.radius == other.vignetting.radius + && vignetting.strength == other.vignetting.strength + && vignetting.centerX == other.vignetting.centerX + && vignetting.centerY == other.vignetting.centerY && !memcmp (&chmixer.red, &other.chmixer.red, 3*sizeof(int)) && !memcmp (&chmixer.green, &other.chmixer.green, 3*sizeof(int)) && !memcmp (&chmixer.blue, &other.chmixer.blue, 3*sizeof(int)) diff --git a/rtengine/procparams.h b/rtengine/procparams.h index cb7d0253c..884fc4468 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -249,6 +249,9 @@ class VignettingParams { public: int amount; int radius; + int strength; + int centerX; + int centerY; }; /** diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index ffbae35f2..cacb52b60 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -782,7 +782,7 @@ int RawImageSource::load (Glib::ustring fname, bool batch) { for (int j=0; j<3; j++) for (int k=0; k<3; k++) cam[i][j] += coeff[k][i] * sRGB_d50[k][j]; - camProfile = iccStore.createFromMatrix (cam, false, "Camera"); + camProfile = iccStore->createFromMatrix (cam, false, "Camera"); inverse33 (cam, icam); if (ri->profile_data) @@ -1165,7 +1165,7 @@ void RawImageSource::colorSpaceConversion (Image16* im, ColorManagementParams cm else if (inProfile=="(camera)" || inProfile=="") in = camprofile; else { - in = iccStore.getProfile (inProfile); + in = iccStore->getProfile (inProfile); if (in==NULL) inProfile = "(camera)"; } @@ -1174,11 +1174,11 @@ void RawImageSource::colorSpaceConversion (Image16* im, ColorManagementParams cm if (inProfile=="(camera)" || inProfile=="" || (inProfile=="(embedded)" && !embedded)) { // in this case we avoid using the slllllooooooowwww lcms -// out = iccStore.workingSpace (wProfile); +// out = iccStore->workingSpace (wProfile); // hTransform = cmsCreateTransform (in, TYPE_RGB_16_PLANAR, out, TYPE_RGB_16_PLANAR, settings->colorimetricIntent, cmsFLAGS_MATRIXINPUT | cmsFLAGS_MATRIXOUTPUT);//cmsFLAGS_MATRIXINPUT | cmsFLAGS_MATRIXOUTPUT); // cmsDoTransform (hTransform, im->data, im->data, im->planestride/2); // cmsDeleteTransform(hTransform); - TMatrix work = iccStore.workingSpaceInverseMatrix (cmp.working); + TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working); double mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; for (int i=0; i<3; i++) for (int j=0; j<3; j++) @@ -1199,8 +1199,8 @@ void RawImageSource::colorSpaceConversion (Image16* im, ColorManagementParams cm } } else { - out = iccStore.workingSpace (cmp.working); -// out = iccStore.workingSpaceGamma (wProfile); + out = iccStore->workingSpace (cmp.working); +// out = iccStore->workingSpaceGamma (wProfile); lcmsMutex->lock (); cmsHTRANSFORM hTransform = cmsCreateTransform (in, TYPE_RGB_16_PLANAR, out, TYPE_RGB_16_PLANAR, settings->colorimetricIntent, 0); lcmsMutex->unlock (); @@ -1834,14 +1834,14 @@ int RawImageSource::getAEHistogram (unsigned int* histogram, int& histcompr) { } if (ri->filters) for (int j=start; jdata[i][j]>>histcompr]+=2; - else + else*/ histogram[ri->data[i][j]>>histcompr]+=4; else for (int j=start; j<3*end; j++) { histogram[ri->data[i][j+0]>>histcompr]++; - histogram[ri->data[i][j+1]>>histcompr]++; + histogram[ri->data[i][j+1]>>histcompr]+=2; histogram[ri->data[i][j+2]>>histcompr]++; } } diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 3ee47d5a5..43f64539c 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -48,6 +48,62 @@ my_jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile); namespace rtengine { +Thumbnail* Thumbnail::loadFromMemory (const char* image, int length, int &w, int &h, int fixwh) { + Image16* img = new Image16 (); + int err = img->loadJPEGFromMemory(image,length); + if (err) { + printf("loadfromMemory: error\n"); + delete img; + return NULL; + } + + Thumbnail* tpp = new Thumbnail (); + + tpp->camwbRed = 1.0; + tpp->camwbGreen = 1.0; + tpp->camwbBlue = 1.0; + + tpp->embProfileLength = 0; + unsigned char* data; + img->getEmbeddedProfileData (tpp->embProfileLength, data); + if (data && tpp->embProfileLength) { + tpp->embProfileData = new unsigned char [tpp->embProfileLength]; + memcpy (tpp->embProfileData, data, tpp->embProfileLength); + } + else { + tpp->embProfileLength = 0; + tpp->embProfileData = NULL; + } + + tpp->redMultiplier = 1.0; + tpp->greenMultiplier = 1.0; + tpp->blueMultiplier = 1.0; + + tpp->scaleForSave = 8192; + tpp->defGain = 1.0; + tpp->gammaCorrected = false; + tpp->isRaw = 1; + memset (tpp->colorMatrix, 0, sizeof(tpp->colorMatrix)); + tpp->colorMatrix[0][0] = 1.0; + tpp->colorMatrix[1][1] = 1.0; + tpp->colorMatrix[2][2] = 1.0; + + if (fixwh==1) { + w = h * img->width / img->height; + tpp->scale = (double)img->height / h; + } + else { + h = w * img->height / img->width; + tpp->scale = (double)img->width / w; + } + + tpp->thumbImg = img->resize (w, h, TI_Nearest); + delete img; + + tpp->init (); + return tpp; +} + Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh) { Image16* img = new Image16 (); @@ -143,7 +199,7 @@ void Thumbnail::init () { for (int j=0; j<3; j++) for (int k=0; k<3; k++) camToD50[i][j] += colorMatrix[k][i] * sRGB_d50[k][j]; - camProfile = iccStore.createFromMatrix (camToD50, false, "Camera"); + camProfile = iccStore->createFromMatrix (camToD50, false, "Camera"); } bool Thumbnail::igammacomputed = false; @@ -173,6 +229,39 @@ Thumbnail::~Thumbnail () { cmsCloseProfile(camProfile); } +IImage8* Thumbnail::quickProcessImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, double& myscale) { + + int rwidth; + if (params.coarse.rotate==90 || params.coarse.rotate==270) { + rwidth = rheight; + rheight = thumbImg->height * rwidth / thumbImg->width; + } + else + rwidth = thumbImg->width * rheight / thumbImg->height; + Image16* baseImg = thumbImg->resize (rwidth, rheight, interp); + + if (params.coarse.rotate) { + Image16* tmp = baseImg->rotate (params.coarse.rotate); + rwidth = tmp->width; + rheight = tmp->height; + delete baseImg; + baseImg = tmp; + } + if (params.coarse.hflip) { + Image16* tmp = baseImg->hflip (); + delete baseImg; + baseImg = tmp; + } + if (params.coarse.vflip) { + Image16* tmp = baseImg->vflip (); + delete baseImg; + baseImg = tmp; + } + Image8* img8 = baseImg->to8(); + delete baseImg; + return img8; +} + IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, double& myscale) { // compute WB multipliers diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index f9b1c9914..438fe2e04 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -70,9 +70,12 @@ namespace rtengine { void init (); IImage8* processImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, double& scale); + IImage8* quickProcessImage (const procparams::ProcParams& pparams, int rheight, TypeInterpolation interp, double& scale); int getImageWidth (const procparams::ProcParams& pparams, int rheight); void getFinalSize (const rtengine::procparams::ProcParams& pparams, int& w, int& h); + static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh); + static Thumbnail* loadFromMemory (const char* image, int length, int &w, int &h, int fixwh); static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh); static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh); diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 37f46ebab..169b7954c 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -199,7 +199,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p ProfileContent pc; if (params.icm.output.compare (0, 6, "No ICM") && params.icm.output!="") - pc = iccStore.getContent (params.icm.output); + pc = iccStore->getContent (params.icm.output); readyImg->setOutputProfile (pc.data, pc.length); diff --git a/rtengine/stdimagesource.cc b/rtengine/stdimagesource.cc index 3ef593ead..cbffc3597 100644 --- a/rtengine/stdimagesource.cc +++ b/rtengine/stdimagesource.cc @@ -256,19 +256,19 @@ void StdImageSource::getImage (ColorTemp ctemp, int tran, Image16* image, Previe void StdImageSource::colorSpaceConversion (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded) { cmsHPROFILE in; - cmsHPROFILE out = iccStore.workingSpace (cmp.working); + cmsHPROFILE out = iccStore->workingSpace (cmp.working); if (cmp.input=="(embedded)" || cmp.input=="" || cmp.input=="(camera)") { if (embedded) in = embedded; else - in = iccStore.getsRGBProfile (); + in = iccStore->getsRGBProfile (); } else if (cmp.input!="(none)") { - in = iccStore.getProfile (cmp.input); + in = iccStore->getProfile (cmp.input); if (in==NULL && embedded) in = embedded; else if (in==NULL) - in = iccStore.getsRGBProfile (); + in = iccStore->getsRGBProfile (); else if (cmp.gammaOnInput) for (int i=0; iheight; i++) for (int j=0; jwidth; j++) { diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 221521cb1..adf2eaf88 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -14,6 +14,7 @@ set (BASESOURCEFILES whitebalance.cc vignetting.cc rotate.cc distortion.cc crophandler.cc curveeditor.cc dirbrowser.cc filecatalog.cc + previewloader.cc histogrampanel.cc history.cc imagearea.cc imageareapanel.cc iptcpanel.cc labcurve.cc lumadenoise.cc main.cc multilangmgr.cc mycurve.cc options.cc diff --git a/rtgui/cachemanager.cc b/rtgui/cachemanager.cc index a7029951a..f44fcb165 100644 --- a/rtgui/cachemanager.cc +++ b/rtgui/cachemanager.cc @@ -24,10 +24,26 @@ #include #include -CacheManager cacheMgr; +CacheManager* +CacheManager::getInstance(void) +{ + static CacheManager* instance_ = 0; + if ( instance_ == 0 ) + { + static Glib::Mutex smutex_; + Glib::Mutex::Lock lock(smutex_); + if ( instance_ == 0 ) + { + instance_ = new CacheManager(); + } + } + return instance_; +} void CacheManager::init () { + Glib::Mutex::Lock lock(mutex_); + openEntries.clear (); baseDir = options.cacheBaseDir; @@ -49,12 +65,18 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) { Thumbnail* res = NULL; - std::map::iterator r = openEntries.find (fname); - // if it is open, return it - if (r!=openEntries.end()) { - r->second->increaseRef (); - return r->second; - } + // take manager lock and search for entry, if found return it else release + // lock and create it + { + Glib::Mutex::Lock lock(mutex_); + + string_thumb_map::iterator r = openEntries.find (fname); + // if it is open, return it + if (r!=openEntries.end()) { + r->second->increaseRef (); + return r->second; + } + } // compute the md5 std::string md5 = getMD5 (fname); @@ -78,6 +100,7 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) { } // if not, create a new one if (!res) { + res = new Thumbnail (this, fname, md5); if (!res->isSupported ()) { delete res; @@ -85,20 +108,45 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) { } } + // retake the lock and see if it was added while we we're unlocked, if it + // was use it over our version. if not added we create the cache entry if (res) - openEntries[fname] = res; + { + Glib::Mutex::Lock lock(mutex_); + + string_thumb_map::iterator r = openEntries.find (fname); + if (r!=openEntries.end()) { + delete res; + r->second->increaseRef (); + return r->second; + } + + // it wasn't, create a new entry + openEntries[fname] = res; + } + return res; } void CacheManager::deleteEntry (const Glib::ustring& fname) { + Glib::Mutex::Lock lock(mutex_); + // check if it is opened - std::map::iterator r = openEntries.find (fname); + string_thumb_map::iterator r = openEntries.find (fname); // if it is open, dont delete it if (r!=openEntries.end()) { std::string md5 = r->second->getMD5 (); - r->second->decreaseRef (); + + // decrease reference count; this will call back into CacheManager so + // we release the lock for it. + { + lock.release(); + r->second->decreaseRef (); + lock.acquire(); + } + // if in the editor, the thumbnail still exists. If not, delete it: r = openEntries.find (fname); if (r==openEntries.end() && md5!="") { @@ -126,6 +174,8 @@ void CacheManager::deleteEntry (const Glib::ustring& fname) { void CacheManager::renameEntry (const std::string& oldfilename, const std::string& oldmd5, const std::string& newfilename) { + Glib::Mutex::Lock lock(mutex_); + std::string newmd5 = getMD5 (newfilename); ::g_rename ((getCacheFileName ("profiles", oldfilename, oldmd5) + paramFileExtension).c_str(), (getCacheFileName ("profiles", newfilename, newmd5) + paramFileExtension).c_str()); @@ -136,7 +186,7 @@ void CacheManager::renameEntry (const std::string& oldfilename, const std::strin ::g_rename ((getCacheFileName ("data", oldfilename, oldmd5) + ".txt").c_str(), (getCacheFileName ("data", newfilename, newmd5) + ".txt").c_str()); // check if it is opened - std::map::iterator r = openEntries.find (oldfilename); + string_thumb_map::iterator r = openEntries.find (oldfilename); // if it is open, update md5 if (r!=openEntries.end()) { Thumbnail* t = r->second; @@ -144,14 +194,16 @@ void CacheManager::renameEntry (const std::string& oldfilename, const std::strin t->setFileName (newfilename); openEntries[newfilename] = t; t->updateCache (); - t->reSaveThumbnail (); + t->saveThumbnail (); } } void CacheManager::closeThumbnail (Thumbnail* t) { + Glib::Mutex::Lock lock(mutex_); + t->updateCache (); - std::map::iterator r = openEntries.find (t->getFileName()); + string_thumb_map::iterator r = openEntries.find (t->getFileName()); if (r!=openEntries.end()) openEntries.erase (r); delete t; @@ -159,11 +211,15 @@ void CacheManager::closeThumbnail (Thumbnail* t) { void CacheManager::closeCache () { + Glib::Mutex::Lock lock(mutex_); + applyCacheSizeLimitation (); } void CacheManager::clearAll () { + Glib::Mutex::Lock lock(mutex_); + deleteDir ("images"); deleteDir ("aehistograms"); deleteDir ("embprofiles"); @@ -171,30 +227,34 @@ void CacheManager::clearAll () { deleteDir ("data"); // re-generate thumbnail images and clear profiles of open thumbnails - std::map::iterator i; - for (i=openEntries.begin(); i!=openEntries.end(); i++) { - i->second->clearProcParams (CACHEMGR); - i->second->generateThumbnailImage (); - i->second->updateCache (); - } + //string_thumb_map::iterator i; + //for (i=openEntries.begin(); i!=openEntries.end(); i++) { + // i->second->clearProcParams (CACHEMGR); + // i->second->generateThumbnailImage (); + // i->second->updateCache (); + //} } void CacheManager::clearThumbImages () { + Glib::Mutex::Lock lock(mutex_); + deleteDir ("images"); deleteDir ("aehistograms"); deleteDir ("embprofiles"); // re-generate thumbnail images of open thumbnails - std::map::iterator i; - for (i=openEntries.begin(); i!=openEntries.end(); i++) - i->second->generateThumbnailImage (); + //string_thumb_map::iterator i; + //for (i=openEntries.begin(); i!=openEntries.end(); i++) + // i->second->generateThumbnailImage (); } void CacheManager::clearProfiles () { + Glib::Mutex::Lock lock(mutex_); + deleteDir ("profiles"); // clear profiles of open thumbnails - std::map::iterator i; + string_thumb_map::iterator i; for (i=openEntries.begin(); i!=openEntries.end(); i++) i->second->clearProcParams (CACHEMGR); } @@ -234,9 +294,9 @@ void CacheManager::applyCacheSizeLimitation () { std::vector flist; Glib::ustring dataDir = Glib::build_filename (baseDir, "data"); Glib::RefPtr dir = Gio::File::create_for_path (dataDir); - - safe_build_file_list (dir, flist); - + + safe_build_file_list (dir, flist); + if (flist.size() > options.maxCacheEntries) { std::sort (flist.begin(), flist.end()); while (flist.size() > options.maxCacheEntries) { @@ -245,7 +305,7 @@ void CacheManager::applyCacheSizeLimitation () { ::g_remove ((Glib::build_filename (Glib::build_filename (baseDir, "images"), flist.front().fname) + ".jpg").c_str()); ::g_remove ((Glib::build_filename (Glib::build_filename (baseDir, "aehistograms"), flist.front().fname)).c_str()); ::g_remove ((Glib::build_filename (Glib::build_filename (baseDir, "embprofiles"), flist.front().fname) + ".icc").c_str()); -// ::g_remove ((Glib::build_filename (Glib::build_filename (baseDir, "profiles"), flist.front().fname) + paramFileExtension).c_str()); + // ::g_remove ((Glib::build_filename (Glib::build_filename (baseDir, "profiles"), flist.front().fname) + paramFileExtension).c_str()); flist.erase (flist.begin()); } } diff --git a/rtgui/cachemanager.h b/rtgui/cachemanager.h index f2f15c59d..983c2acbd 100644 --- a/rtgui/cachemanager.h +++ b/rtgui/cachemanager.h @@ -29,14 +29,21 @@ class Thumbnail; class CacheManager { - std::map openEntries; - Glib::ustring baseDir; + typedef std::pair string_thumb_pair; + typedef std::map string_thumb_map; + + string_thumb_map openEntries; + Glib::ustring baseDir; + Glib::Mutex mutex_; void deleteDir (const Glib::ustring& dirName); - public: CacheManager () {} + public: + + static CacheManager* getInstance(void); + void init (); Thumbnail* getEntry (const Glib::ustring& fname); void deleteEntry (const Glib::ustring& fname); @@ -44,7 +51,7 @@ class CacheManager { void closeThumbnail (Thumbnail* t); - const Glib::ustring& getBaseDir () { return baseDir; } + const Glib::ustring& getBaseDir () { Glib::Mutex::Lock lock(mutex_); return baseDir; } void closeCache (); static std::string getMD5 (const Glib::ustring& fname); @@ -58,7 +65,7 @@ class CacheManager { Glib::ustring getCacheFileName (const Glib::ustring& subdir, const Glib::ustring& fname, const Glib::ustring& md5); }; -extern CacheManager cacheMgr; +#define cacheMgr CacheManager::getInstance() #endif diff --git a/rtgui/filebrowserentry.cc b/rtgui/filebrowserentry.cc index ef813410e..b31d6b454 100644 --- a/rtgui/filebrowserentry.cc +++ b/rtgui/filebrowserentry.cc @@ -56,14 +56,20 @@ FileBrowserEntry::FileBrowserEntry (Thumbnail* thm, const Glib::ustring& fname) FileBrowserEntry::~FileBrowserEntry () { - thumbImageUpdater.removeJobs (this); + // so jobs arriving now do nothing + if (feih->pending) + { + feih->destroyed = true; + } + else + { + delete feih; + feih = 0; + } + + thumbImageUpdater->removeJobs (this); if (thumbnail) thumbnail->removeThumbnailListener (this); - - if (feih->pending) - feih->destroyed = true; - else - delete feih; } void FileBrowserEntry::refreshThumbnailImage () { @@ -71,8 +77,16 @@ void FileBrowserEntry::refreshThumbnailImage () { if (!thumbnail) return; - thumbImageUpdater.add (thumbnail, thumbnail->getProcParams(), preh, &updatepriority, this); - thumbImageUpdater.process (); + thumbImageUpdater->add (thumbnail, thumbnail->getProcParams(), preh, &updatepriority, false, this); +} + +void FileBrowserEntry::refreshQuickThumbnailImage () { + // Only make a (slow) processed preview if the picture has been edited at all + if ( thumbnail && + thumbnail->isQuick() && (!options.internalThumbIfUntouched || thumbnail->isPParamsValid()) ) + { + thumbImageUpdater->add(thumbnail, thumbnail->getProcParams(), preh, &updatepriority, true, this); + } } void FileBrowserEntry::calcThumbnailSize () { @@ -134,10 +148,11 @@ struct tiupdate { int fbeupdate (void* data) { - gdk_threads_enter (); tiupdate* params = (tiupdate*)data; FileBrowserEntryIdleHelper* feih = params->feih; + GThreadLock lock; + if (feih->destroyed) { if (feih->pending == 1) delete feih; @@ -145,14 +160,12 @@ int fbeupdate (void* data) { feih->pending--; params->img->free (); delete params; - gdk_threads_leave (); return 0; } feih->fbentry->_updateImage (params->img, params->scale, params->cropParams); feih->pending--; - gdk_threads_leave (); delete params; return 0; @@ -160,8 +173,19 @@ int fbeupdate (void* data) { void FileBrowserEntry::updateImage (rtengine::IImage8* img, double scale, rtengine::procparams::CropParams cropParams) { - redrawRequests++; - feih->pending++; + { + GThreadLock lock; + + if ( feih == 0 || + feih->destroyed ) + { + return; + } + + redrawRequests++; + feih->pending++; + } + tiupdate* param = new tiupdate (); param->feih = feih; param->img = img; diff --git a/rtgui/filebrowserentry.h b/rtgui/filebrowserentry.h index 53a1fcf16..490e0d100 100644 --- a/rtgui/filebrowserentry.h +++ b/rtgui/filebrowserentry.h @@ -76,6 +76,7 @@ public: FileThumbnailButtonSet* getThumbButtonSet (); void refreshThumbnailImage (); + void refreshQuickThumbnailImage (); void calcThumbnailSize (); std::vector > getIconsOnImageArea (); diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc index 36cd269c2..26866cee4 100644 --- a/rtgui/filecatalog.cc +++ b/rtgui/filecatalog.cc @@ -44,10 +44,8 @@ int _directoryUpdater (void* cat) { } #endif -FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb) : listener(NULL), fslistener(NULL), hasValidCurrentEFS(false), filterPanel(NULL), coarsePanel(cp), toolBar(tb) { +FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb) : selectedDirectoryId(1), listener(NULL), fslistener(NULL), hasValidCurrentEFS(false), filterPanel(NULL), coarsePanel(cp), toolBar(tb) { - previewLoader.setPreviewLoaderListener (this); - // construct and initialize thumbnail browsers fileBrowser = new FileBrowser(); fileBrowser->setFileBrowserListener (this); @@ -220,19 +218,23 @@ void FileCatalog::closeDir () { wdMonitor = NULL; } #endif + + // ignore old requests + ++selectedDirectoryId; + // terminate thumbnail preview loading - previewLoader.terminate (); + previewLoader->removeAllJobs (); // terminate thumbnail updater - thumbImageUpdater.terminate (); + thumbImageUpdater->removeAllJobs (); // remove entries + selectedDirectory = ""; fileBrowser->close (); fileNameList.clear (); dirEFS.clear (); hasValidCurrentEFS = false; - selectedDirectory = ""; redrawAll (); } @@ -268,7 +270,6 @@ void FileCatalog::dirSelected (const Glib::ustring& dirname, const Glib::ustring } _refreshProgressBar (); - previewLoader.process (); #ifdef _WIN32 wdMonitor = new WinDirMonitor (selectedDirectory, this); @@ -310,7 +311,14 @@ int refreshpb (void* data) { return 0; } -void FileCatalog::previewReady (FileBrowserEntry* fdn) { +void FileCatalog::previewReady (int dir_id, FileBrowserEntry* fdn) { + + GThreadLock lock; + + if ( dir_id != selectedDirectoryId ) + { + return; + } // put it into the "full directory" browser fdn->setImageAreaToolListener (iatlistener); @@ -366,44 +374,24 @@ void FileCatalog::_previewsFinished () { filterPanel->setFilter ( currentEFS,false ); } } + // restart anything that might have been loaded low quality + fileBrowser->refreshQuickThumbImages(); } -void FileCatalog::previewsFinished () { +void FileCatalog::previewsFinished (int dir_id) { + + GThreadLock lock; + + if ( dir_id != selectedDirectoryId ) + { + return; + } if (!hasValidCurrentEFS) currentEFS = dirEFS; g_idle_add (prevfinished, this); } -void PreviewLoader::remove (Glib::ustring fname) { - std::list::iterator i; - for (i=jqueue.begin(); i!=jqueue.end(); i++) - if (i->fullName==fname) - break; - if (i!=jqueue.end()) - jqueue.erase (i); -} - -void PreviewLoader::start () { - - jqueue.sort (); -} - -void PreviewLoader::process (DirEntry& current) { - - if (Glib::file_test (current.fullName, Glib::FILE_TEST_EXISTS)) { - Thumbnail* tmb = cacheMgr.getEntry (current.fullName); - if (tmb && pl) - pl->previewReady (new FileBrowserEntry (tmb, current.fullName)); - } -} - -void PreviewLoader::end () { - - if (pl) - pl->previewsFinished (); -} - void FileCatalog::setEnabled (bool e) { enabled = e; @@ -422,15 +410,11 @@ void FileCatalog::refreshAll () { void FileCatalog::_openImage (std::vector tmb) { if (enabled && listener!=NULL) { - previewLoader.stop (); - thumbImageUpdater.stop (); for (size_t i=0; igetFileName())==editedFiles.end()) listener->fileSelected (tmb[i]); tmb[i]->decreaseRef (); } - previewLoader.process (); - thumbImageUpdater.process (); } } @@ -475,7 +459,7 @@ void FileCatalog::deleteRequested (std::vector tbe) { // t->thumbnail->decreaseRef (); delete t; // remove from cache - cacheMgr.deleteEntry (fname); + cacheMgr->deleteEntry (fname); // delete from file system ::g_remove (fname.c_str()); // delete paramfile if found @@ -492,7 +476,6 @@ void FileCatalog::deleteRequested (std::vector tbe) { void FileCatalog::developRequested (std::vector tbe) { if (listener) { - thumbImageUpdater.stop (); for (size_t i=0; ithumbnail->getProcParams(); rtengine::ProcessingJob* pjob = rtengine::ProcessingJob::create (tbe[i]->filename, tbe[i]->thumbnail->getType()==FT_Raw, params); @@ -512,7 +495,6 @@ void FileCatalog::developRequested (std::vector tbe) { listener->addBatchQueueJob (new BatchQueueEntry (pjob, params, tbe[i]->filename, NULL, pw, ph, tbe[i]->thumbnail)); } } - thumbImageUpdater.process (); } } @@ -538,7 +520,7 @@ void FileCatalog::renameRequested (std::vector tbe) { nBaseName += "." + getExtension (baseName); Glib::ustring nfname = Glib::build_filename (dirName, nBaseName); if (!::g_rename (ofname.c_str(), nfname.c_str())) { - cacheMgr.renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname); + cacheMgr->renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname); reparseDirectory (); } renameDlg->hide (); @@ -584,7 +566,7 @@ void FileCatalog::renameRequested (std::vector tbe) { } Glib::ustring nfname = Glib::build_filename (dirName, nBaseName); if (!::g_rename (ofname.c_str(), nfname.c_str())) { - cacheMgr.renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname); + cacheMgr->renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname); // the remaining part (removing old and adding new entry) is done by the directory monitor reparseDirectory (); // on_dir_changed (Gio::File::create_for_path (nfname), Gio::File::create_for_path (nfname), Gio::FILE_MONITOR_EVENT_CHANGED, true); @@ -749,7 +731,7 @@ int FileCatalog::reparseDirectory () { fileNamesToDel.push_back (t[i]->filename); for (size_t i=0; idelEntry (fileNamesToDel[i]); - cacheMgr.deleteEntry (fileNamesToDel[i]); + cacheMgr->deleteEntry (fileNamesToDel[i]); } // check if a new file has been added @@ -761,10 +743,8 @@ int FileCatalog::reparseDirectory () { break; } if (!found) { - previewLoader.stop (); checkAndAddFile (Gio::File::create_for_parse_name (nfileNameList[i])); _refreshProgressBar (); - previewLoader.process (); } } @@ -801,7 +781,7 @@ void FileCatalog::checkAndAddFile (Glib::RefPtr file) { if (info && info->get_file_type() != Gio::FILE_TYPE_DIRECTORY && (!info->is_hidden() || !options.fbShowHidden)) { int lastdot = info->get_name().find_last_of ('.'); if (options.is_extention_enabled(lastdot!=(int)Glib::ustring::npos ? info->get_name().substr (lastdot+1) : "")){ - previewLoader.add (DirEntry (file->get_parse_name())); + previewLoader->add (selectedDirectoryId,file->get_parse_name(),this); previewsToLoad++; } } @@ -820,10 +800,10 @@ void FileCatalog::addAndOpenFile (const Glib::ustring& fname) { int lastdot = info->get_name().find_last_of ('.'); if (options.is_extention_enabled(lastdot!=(int)Glib::ustring::npos ? info->get_name().substr (lastdot+1) : "")){ // if supported, load thumbnail first - Thumbnail* tmb = cacheMgr.getEntry (file->get_parse_name()); + Thumbnail* tmb = cacheMgr->getEntry (file->get_parse_name()); if (tmb) { FileBrowserEntry* entry = new FileBrowserEntry (tmb, file->get_parse_name()); - previewReady (entry); + previewReady (selectedDirectoryId,entry); // open the file FCOIParams* params = new FCOIParams; params->catalog = this; @@ -856,25 +836,15 @@ bool FileCatalog::trashIsEmpty () { void FileCatalog::zoomIn () { - bool pLoad = previewLoader.runs(); - if (pLoad) - previewLoader.stop (); fileBrowser->zoomIn (); - if (pLoad) - previewLoader.process (); } void FileCatalog::zoomOut () { - bool pLoad = previewLoader.runs(); - if (pLoad) - previewLoader.stop (); fileBrowser->zoomOut (); - if (pLoad) - previewLoader.process (); } void FileCatalog::refreshEditedState (const std::set& efiles) { @@ -969,4 +939,3 @@ bool FileCatalog::handleShortcutKey (GdkEventKey* event) { return false; } - diff --git a/rtgui/filecatalog.h b/rtgui/filecatalog.h index f7e95ac4f..85e669c23 100644 --- a/rtgui/filecatalog.h +++ b/rtgui/filecatalog.h @@ -33,12 +33,7 @@ #include #include #include - -class PreviewLoaderListener { - public: - virtual void previewReady (FileBrowserEntry* fd) {} - virtual void previewsFinished () {} -}; +#include class DirEntry { @@ -52,21 +47,6 @@ class DirEntry { } }; -class PreviewLoader : public ProcessingThread { - - protected: - PreviewLoaderListener* pl; - - public: - PreviewLoader () : pl(NULL) { ProcessingThread(); } - void setPreviewLoaderListener (PreviewLoaderListener* p) { pl = p; } - void start (); - void process () { ProcessingThread::process (); } - void process (DirEntry& current); - void remove (Glib::ustring fname); - void end (); -}; - class FileCatalog : public Gtk::VBox, public DirSelectionListener, public PreviewLoaderListener, @@ -80,9 +60,12 @@ class FileCatalog : public Gtk::VBox, Gtk::HBox* hBox; Glib::ustring selectedDirectory; + int selectedDirectoryId; bool enabled; - PreviewLoader previewLoader; + // Restore PreviewLoader if the new threadsafe is not that threadsafe ;-) + //PreviewLoader previewLoader; + //PreviewMultiLoader previewLoader; FileSelectionListener* listener; FileSelectionChangeListener* fslistener; ImageAreaToolListener* iatlistener; @@ -151,8 +134,8 @@ class FileCatalog : public Gtk::VBox, void refreshEditedState (const std::set& efiles); // previewloaderlistener interface - void previewReady (FileBrowserEntry* fdn); - void previewsFinished (); + void previewReady (int dir_id, FileBrowserEntry* fdn); + void previewsFinished (int dir_id); void _previewsFinished (); void _refreshProgressBar (); diff --git a/rtgui/guiutils.h b/rtgui/guiutils.h index 6c4a9423c..126733a64 100644 --- a/rtgui/guiutils.h +++ b/rtgui/guiutils.h @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ -#ifndef __UTILS_ -#define __UTILS_ +#ifndef __GUI_UTILS_ +#define __GUI_UTILS_ #include #include @@ -28,4 +28,47 @@ Glib::ustring removeExtension (const Glib::ustring& filename); Glib::ustring getExtension (const Glib::ustring& filename); void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int imh, int startx, int starty, double scale, const rtengine::procparams::CropParams& cparams); +/** + * @brief Lock GTK for critical section. + * + * Will unlock on destruction. To use: + * + * + * { + * GThreadLock lock; + * // critical code + * } + * + */ +class GThreadLock +{ +public: + GThreadLock() + { + gdk_threads_enter(); + } + ~GThreadLock() + { + gdk_threads_leave(); + } +}; + +/** + * @brief Unlock GTK critical section. + * + * Will relock on destruction. + */ +class GThreadUnLock +{ +public: + GThreadUnLock() + { + gdk_threads_leave(); + } + ~GThreadUnLock() + { + gdk_threads_enter(); + } +}; + #endif diff --git a/rtgui/options.cc b/rtgui/options.cc index 9a52d6d45..18ca9af0f 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -241,6 +241,7 @@ if (keyFile.has_group ("File Browser")) { if (keyFile.has_key ("File Browser", "ThumbnailZoomRatios"))thumbnailZoomRatios= keyFile.get_double_list ("File Browser", "ThumbnailZoomRatios"); if (keyFile.has_key ("File Browser", "OverlayedFileNames")) overlayedFileNames = keyFile.get_boolean ("File Browser", "OverlayedFileNames"); if (keyFile.has_key ("File Browser", "ShowFileNames")) showFileNames = keyFile.get_boolean ("File Browser", "ShowFileNames"); + if (keyFile.has_key ("File Browser", "InternalThumbIfUntouched")) internalThumbIfUntouched = keyFile.get_boolean ("File Browser", "InternalThumbIfUntouched"); } if (keyFile.has_group ("Clipping Indication")) { @@ -359,7 +360,8 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_double_list ("File Browser", "ThumbnailZoomRatios", ptzoom); keyFile.set_boolean ("File Browser", "OverlayedFileNames", overlayedFileNames); keyFile.set_boolean ("File Browser", "ShowFileNames", showFileNames ); - + keyFile.set_boolean ("File Browser", "InternalThumbIfUntouched", internalThumbIfUntouched ); + keyFile.set_integer ("Clipping Indication", "HighlightThreshold", highlightThreshold); keyFile.set_integer ("Clipping Indication", "ShadowThreshold", shadowThreshold); keyFile.set_boolean ("Clipping Indication", "BlinkClipped", blinkClipped); diff --git a/rtgui/options.h b/rtgui/options.h index 3b313c6f9..c56475948 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -123,8 +123,9 @@ class Options { std::vector favoriteDirs; std::vector renameTemplates; bool renameUseTemplates; - bool overwriteOutputFile; - + bool internalThumbIfUntouched; + bool overwriteOutputFile; + std::vector thumbnailZoomRatios; bool overlayedFileNames; bool showFileNames; diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 41ddd4a68..ced7e0dde 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -107,6 +107,9 @@ void ParamsEdited::set (bool v) { cacorrection.blue = v; vignetting.amount = v; vignetting.radius = v; + vignetting.strength = v; + vignetting.centerX = v; + vignetting.centerY = v; chmixer.red[0] = v; chmixer.red[1] = v; chmixer.red[2] = v; @@ -234,6 +237,9 @@ void ParamsEdited::initFrom (const std::vector cacorrection.blue = cacorrection.blue && p.cacorrection.blue == other.cacorrection.blue; vignetting.amount = vignetting.amount && p.vignetting.amount == other.vignetting.amount; vignetting.radius = vignetting.radius && p.vignetting.radius == other.vignetting.radius; + vignetting.strength = vignetting.strength && p.vignetting.strength == other.vignetting.strength; + vignetting.centerX = vignetting.centerX && p.vignetting.centerX == other.vignetting.centerX; + vignetting.centerY = vignetting.centerY && p.vignetting.centerY == other.vignetting.centerY; chmixer.red[0] = chmixer.red[0] && p.chmixer.red[0] == other.chmixer.red[0]; chmixer.red[1] = chmixer.red[1] && p.chmixer.red[1] == other.chmixer.red[1]; chmixer.red[2] = chmixer.red[2] && p.chmixer.red[2] == other.chmixer.red[2]; @@ -352,6 +358,9 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten if (cacorrection.blue) toEdit.cacorrection.blue = options.baBehav[ADDSET_CA] ? toEdit.cacorrection.blue + mods.cacorrection.blue : mods.cacorrection.blue; if (vignetting.amount) toEdit.vignetting.amount = options.baBehav[ADDSET_VIGN_AMOUNT] ? toEdit.vignetting.amount + mods.vignetting.amount : mods.vignetting.amount; if (vignetting.radius) toEdit.vignetting.radius = mods.vignetting.radius; + if (vignetting.strength) toEdit.vignetting.strength = mods.vignetting.strength; + if (vignetting.centerX) toEdit.vignetting.centerX = mods.vignetting.centerX; + if (vignetting.centerY) toEdit.vignetting.centerY = mods.vignetting.centerY; if (chmixer.red[0]) toEdit.chmixer.red[0] = mods.chmixer.red[0]; if (chmixer.red[1]) toEdit.chmixer.red[1] = mods.chmixer.red[1]; if (chmixer.red[2]) toEdit.chmixer.red[2] = mods.chmixer.red[2]; diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 11f060d0f..affa2166a 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -191,6 +191,9 @@ class VignettingParamsEdited { public: bool amount; bool radius; + bool strength; + bool centerX; + bool centerY; }; class ChannelMixerParamsEdited { diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 62a1b970d..b6483285d 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -1185,34 +1185,16 @@ void Preferences::delExtPressed () { void Preferences::clearProfilesPressed () { - Gtk::MessageDialog md (*this, M("PREFERENCES_CLEARDLG_LINE1"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE, true); - md.set_secondary_text (M("PREFERENCES_CLEARDLG_LINE2")); - md.set_title (M("PREFERENCES_CLEARDLG_TITLE")); - md.show_all (); - while (gtk_events_pending ()) gtk_main_iteration (); - cacheMgr.clearProfiles (); - md.hide (); + cacheMgr->clearProfiles (); } void Preferences::clearThumbImagesPressed () { - Gtk::MessageDialog md (*this, M("PREFERENCES_CLEARDLG_LINE1"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE, true); - md.set_secondary_text (M("PREFERENCES_CLEARDLG_LINE2")); - md.set_title (M("PREFERENCES_CLEARDLG_TITLE")); - md.show_all (); - while (gtk_events_pending ()) gtk_main_iteration (); - cacheMgr.clearThumbImages (); - md.hide (); + cacheMgr->clearThumbImages (); } void Preferences::clearAllPressed () { - Gtk::MessageDialog md (*this, M("PREFERENCES_CLEARDLG_LINE1"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE, true); - md.set_secondary_text (M("PREFERENCES_CLEARDLG_LINE2")); - md.set_title (M("PREFERENCES_CLEARDLG_TITLE")); - md.show_all (); - while (gtk_events_pending ()) gtk_main_iteration (); - cacheMgr.clearAll (); - md.hide (); + cacheMgr->clearAll (); } diff --git a/rtgui/previewloader.cc b/rtgui/previewloader.cc new file mode 100644 index 000000000..c8a5fa337 --- /dev/null +++ b/rtgui/previewloader.cc @@ -0,0 +1,170 @@ +/* + * 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 . + */ + +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +#define DEBUG(format,args...) +//#define DEBUG(format,args...) printf("PreviewLoader::%s: " format "\n", __FUNCTION__, ## args) + +class PreviewLoader::Impl +{ +public: + struct Job + { + Job(int dir_id, const Glib::ustring& dir_entry, PreviewLoaderListener* listener): + dir_id_(dir_id), + dir_entry_(dir_entry), + listener_(listener) + {} + + Job(): + dir_id_(0), + listener_(0) + {} + + int dir_id_; + Glib::ustring dir_entry_; + PreviewLoaderListener* listener_; + }; + + struct JobCompare + { + bool operator()(const Job& lhs, const Job& rhs) + { + if ( lhs.dir_id_ == rhs.dir_id_ ) + { + return lhs.dir_entry_ < rhs.dir_entry_; + } + return lhs.dir_id_ < rhs.dir_id_; + } + }; + + typedef std::set JobSet; + + Impl() + { + int threadCount=1; + #ifdef _OPENMP + threadCount=omp_get_num_procs(); + #endif + + threadPool_=new Glib::ThreadPool(threadCount,0); + } + + Glib::ThreadPool* threadPool_; + + Glib::Mutex mutex_; + + JobSet jobs_; + + void + processNextJob(void) + { + Job j; + { + Glib::Mutex::Lock lock(mutex_); + + // nothing to do; could be jobs have been removed + if ( jobs_.empty() ) + { + DEBUG("processing: nothing to do"); + return; + } + + // copy and remove front job + j = *jobs_.begin(); + jobs_.erase(jobs_.begin()); + DEBUG("processing %s",j.dir_entry_.c_str()); + DEBUG("%d job(s) remaining",jobs_.size()); + } + + // unlock and do processing; will relock on block exit, then call listener + // if something got + Thumbnail* tmb = 0; + { + if (Glib::file_test(j.dir_entry_, Glib::FILE_TEST_EXISTS)) + { + tmb = cacheMgr->getEntry(j.dir_entry_); + } + } + + // we got something so notify listener + if ( tmb ) + { + j.listener_->previewReady(j.dir_id_,new FileBrowserEntry(tmb,j.dir_entry_)); + } + + // signal at end + if ( jobs_.empty() ) + { + j.listener_->previewsFinished(j.dir_id_); + } + } +}; + +PreviewLoader::PreviewLoader(): + impl_(new Impl()) +{ +} + +PreviewLoader* +PreviewLoader::getInstance(void) +{ + // this will not be deleted... + static PreviewLoader* instance_ = 0; + if ( instance_ == 0 ) + { + instance_ = new PreviewLoader(); + } + return instance_; +} + +void +PreviewLoader::add(int dir_id, const Glib::ustring& dir_entry, PreviewLoaderListener* l) +{ + // somebody listening? + if ( l != 0 ) + { + Glib::Mutex::Lock lock(impl_->mutex_); + + // create a new job and append to queue + DEBUG("saving job %s",dir_entry.c_str()); + impl_->jobs_.insert(Impl::Job(dir_id,dir_entry,l)); + + // queue a run request + DEBUG("adding run request %s",dir_entry.c_str()); + impl_->threadPool_->push(sigc::mem_fun(*impl_, &PreviewLoader::Impl::processNextJob)); + } +} + +void +PreviewLoader::removeAllJobs(void) +{ + DEBUG("stop"); + + impl_->jobs_.clear(); +} + + diff --git a/rtgui/previewloader.h b/rtgui/previewloader.h new file mode 100644 index 000000000..b113d590d --- /dev/null +++ b/rtgui/previewloader.h @@ -0,0 +1,93 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#ifndef _PREVIEWLOADER_ +#define _PREVIEWLOADER_ + +#include +#include +#include + +class PreviewLoaderListener +{ +public: + + /** + * @brief a preview is ready + * + * @param dir_id directory ID this is for + * @param fd entry + */ + virtual void previewReady (int dir_id, FileBrowserEntry* fd) {} + + /** + * @brief all previews have finished loading + */ + virtual void previewsFinished (int dir_id_) {} +}; + +class PreviewLoader +{ + public: + + /** + * @brief Singleton entry point. + * + * @note expects to be called inside gtk thread lock + * + * @return Pointer to thumbnail image updater. + */ + static PreviewLoader* getInstance(void); + + /** + * @brief Add an thumbnail image update request. + * + * Code will add the request to the queue and, if needed, start a pool + * thread to process it. + * + * @param dir_id directory we're looking at + * @param dir_entry entry in it + * @param l listener + */ + void add(int dir_id, const Glib::ustring& dir_entry, PreviewLoaderListener* l); + + /** + * @brief Stop processing and remove all jobs. + * + * Will not return till all jobs have completed. + * + * @note expects to be called inside gtk thread lock + */ + void removeAllJobs(void); + + private: + + PreviewLoader(); + + class Impl; + Impl* impl_; +}; + +/** + * @brief Singleton boiler plate. + * + * To use: \c previewLoader->start() , + */ +#define previewLoader PreviewLoader::getInstance() + +#endif diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index b3c4f0e2f..6dd3cfe40 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -23,7 +23,7 @@ RTWindow::RTWindow () { - cacheMgr.init (); + cacheMgr->init (); #ifdef GLIBMM_EXCEPTIONS_ENABLED try { set_default_icon_from_file (argv0+"/images/logoicon16.png"); @@ -248,7 +248,7 @@ bool RTWindow::on_delete_event(GdkEventAny* event) { if (options.startupDir==STARTUPDIR_LAST && fileBrowser->lastSelectedDir ()!="") options.startupPath = fileBrowser->lastSelectedDir (); fileBrowser->close (); - cacheMgr.closeCache (); + cacheMgr->closeCache (); options.lastScale = editorPanel->zoomBar->getScale (); diff --git a/rtgui/thumbbrowserbase.cc b/rtgui/thumbbrowserbase.cc index ad464c3a7..cea0bbc83 100644 --- a/rtgui/thumbbrowserbase.cc +++ b/rtgui/thumbbrowserbase.cc @@ -498,6 +498,12 @@ void ThumbBrowserBase::refreshThumbImages () { redraw (); } +void ThumbBrowserBase::refreshQuickThumbImages () { + for (int i=0; irefreshQuickThumbnailImage (); + } +} + void ThumbBrowserBase::refreshEditedState (const std::set& efiles) { editedFiles = efiles; diff --git a/rtgui/thumbbrowserbase.h b/rtgui/thumbbrowserbase.h index e8ffe4b53..801a96756 100644 --- a/rtgui/thumbbrowserbase.h +++ b/rtgui/thumbbrowserbase.h @@ -95,6 +95,7 @@ class ThumbBrowserBase : public Gtk::VBox { void styleChanged (const Glib::RefPtr& style); void redraw (); // arrange files and draw area void refreshThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw + void refreshQuickThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw void refreshEditedState (const std::set& efiles); void initEntry (ThumbBrowserEntryBase* entry); diff --git a/rtgui/thumbbrowserentrybase.h b/rtgui/thumbbrowserentrybase.h index 20f8dda77..6562633d0 100644 --- a/rtgui/thumbbrowserentrybase.h +++ b/rtgui/thumbbrowserentrybase.h @@ -109,6 +109,7 @@ protected: bool operator< (ThumbBrowserEntryBase& other) { return shortname.casefold()>other.shortname.casefold(); } virtual void refreshThumbnailImage () {} + virtual void refreshQuickThumbnailImage () {} virtual void calcThumbnailSize () {} virtual void drawProgressBar (Glib::RefPtr win, Glib::RefPtr gc, const Gdk::Color& foregr, const Gdk::Color& backgr, int x, int w, int y, int h) {} diff --git a/rtgui/thumbimageupdater.cc b/rtgui/thumbimageupdater.cc index cbdc6313b..38720095f 100644 --- a/rtgui/thumbimageupdater.cc +++ b/rtgui/thumbimageupdater.cc @@ -16,150 +16,270 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ + +#include #include #include +#include -#define threadNum 1 -ThumbImageUpdater thumbImageUpdater; +#ifdef _OPENMP +#include +#endif -ThumbImageUpdater::ThumbImageUpdater () - : tostop(false), stopped(true), qMutex(NULL), startMutex(NULL), threadPool(NULL) { - -} +#define DEBUG(format,args...) +//#define DEBUG(format,args...) printf("ThumbImageUpdate::%s: " format "\n", __FUNCTION__, ## args) -ThumbImageUpdater::~ThumbImageUpdater () +class +ThumbImageUpdater::Impl { - delete threadPool; +public: + + struct Job + { + Job(Thumbnail* thumbnail, const rtengine::procparams::ProcParams& pparams, + int height, bool* priority, bool upgrade, + ThumbImageUpdateListener* listener): + thumbnail_(thumbnail), + pparams_(pparams), + height_(height), + priority_(priority), + upgrade_(upgrade), + listener_(listener) + {} + + Job(): + thumbnail_(0), + listener_(0) + {} + + Thumbnail* thumbnail_; + rtengine::procparams::ProcParams pparams_; + int height_; + bool* priority_; + bool upgrade_; + ThumbImageUpdateListener* listener_; + }; + + typedef std::list JobList; + + Impl(): + active_(0), + inactive_waiting_(false) + { + int threadCount=1; + #ifdef _OPENMP + threadCount=omp_get_num_procs(); + #endif + + threadPool_=new Glib::ThreadPool(threadCount,0); + } + + Glib::ThreadPool* threadPool_; + + Glib::Mutex mutex_; + + JobList jobs_; + + unsigned int active_; + + bool inactive_waiting_; + + Glib::Cond inactive_; + + void + processNextJob(void) + { + Job j; + + { + Glib::Mutex::Lock lock(mutex_); + + // nothing to do; could be jobs have been removed + if ( jobs_.empty() ) + { + DEBUG("processing: nothing to do (%d,%d)",paused_,jobs_.empty()); + return; + } + + JobList::iterator i; + + // see if any priority jobs exist + for ( i = jobs_.begin(); i != jobs_.end(); ++i) + { + if ( *(i->priority_) ) + { + DEBUG("processing(priority) %s",i->thumbnail_->getFileName().c_str()); + break; + } + } + + // see if any none upgrade jobs exist + for ( i = jobs_.begin(); i != jobs_.end(); ++i) + { + if ( !i->upgrade_ ) + { + DEBUG("processing(not-upgrade) %s",i->thumbnail_->getFileName().c_str()); + break; + } + } + + // if none, then use first + if ( i == jobs_.end() ) + { + i = jobs_.begin(); + DEBUG("processing(first) %s",i->thumbnail_->getFileName().c_str()); + } + + // copy found job + j = *i; + + // remove so not run again + jobs_.erase(i); + DEBUG("%d job(s) remaining",jobs_.size()); + + ++active_; + } + + // unlock and do processing; will relock on block exit, then call listener + double scale = 1.0; + rtengine::IImage8* img = 0; + + if ( j.upgrade_ ) + { + if ( j.thumbnail_->isQuick() ) + { + img = j.thumbnail_->upgradeThumbImage(j.pparams_, j.height_, scale); + } + } + else + { + img = j.thumbnail_->processThumbImage(j.pparams_, j.height_, scale); + } + + if (img) + { + DEBUG("pushing image %s",j.thumbnail_->getFileName().c_str()); + j.listener_->updateImage(img, scale, j.pparams_.crop); + } + + { + Glib::Mutex::Lock lock(mutex_); + + if ( --active_ == 0 && + inactive_waiting_ ) + { + inactive_waiting_ = false; + inactive_.signal(); + } + } + } +}; + +ThumbImageUpdater* +ThumbImageUpdater::getInstance(void) +{ + // this will not be deleted... + static ThumbImageUpdater* instance_ = 0; + if ( instance_ == 0 ) + { + instance_ = new ThumbImageUpdater(); + } + return instance_; } -void ThumbImageUpdater::add (Thumbnail* t, const rtengine::procparams::ProcParams& params, int height, bool* priority, ThumbImageUpdateListener* l) { +ThumbImageUpdater::ThumbImageUpdater(): + impl_(new Impl()) +{ +} - if (!qMutex) - qMutex = new Glib::Mutex (); - if (!startMutex) - startMutex = new Glib::Mutex (); +void +ThumbImageUpdater::add(Thumbnail* t, const rtengine::procparams::ProcParams& params, + int height, bool* priority, bool upgrade, ThumbImageUpdateListener* l) +{ + // nobody listening? + if ( l == 0 ) + { + return; + } + + Glib::Mutex::Lock lock(impl_->mutex_); - qMutex->lock (); // look up if an older version is in the queue - std::list::iterator i; - for (i=jqueue.begin(); i!=jqueue.end(); i++) - if (i->thumbnail==t && i->listener==l) { - i->pparams = params; - i->height = height; - i->priority = priority; - break; - } - // not found, create and append new job - if (i==jqueue.end ()) { - Job j; - j.thumbnail = t; - j.pparams = params; - j.height = height; - j.listener = l; - j.priority = priority; - jqueue.push_back (j); - } - qMutex->unlock (); -} + Impl::JobList::iterator i(impl_->jobs_.begin()); + for ( ; i != impl_->jobs_.end(); ++i ) + { + if ( i->thumbnail_ == t && + i->listener_ == l && + i->upgrade_ == upgrade ) + { + DEBUG("updating job %s",t->getFileName().c_str()); + // we have one, update queue entry, will be picked up by thread when processed + i->pparams_ = params; + i->height_ = height; + i->priority_ = priority; + return; + } + } -void ThumbImageUpdater::process () { + // create a new job and append to queue + DEBUG("queing job %s",t->getFileName().c_str()); + impl_->jobs_.push_back(Impl::Job(t,params,height,priority,upgrade,l)); - if (stopped) { - stopped = false; - - if(!threadPool) - threadPool = new Glib::ThreadPool(threadNum,1); - - //thread = Glib::Thread::create (sigc::mem_fun(*this, &ThumbImageUpdater::process_), (unsigned long int)0, true, true, Glib::THREAD_PRIORITY_NORMAL); - process_(); - } -} - -void ThumbImageUpdater::process_ () { - - stopped = false; - tostop = false; - - while (!tostop && !jqueue.empty ()) { - - std::list::iterator i; - for (i=jqueue.begin (); i!=jqueue.end(); i++) - if (*(i->priority)) - break; - - if (i==jqueue.end()) - i = jqueue.begin(); - - Job current = *i; - if (current.listener) - threadPool->push(sigc::bind(sigc::mem_fun(*this, &ThumbImageUpdater::processJob), current)); - - jqueue.erase (i); - } - - stopped = true; - //printf("Threads # %d \n", threadPool->get_num_threads()); - -} - -void ThumbImageUpdater::processJob (Job current) { - - if (current.listener) { - double scale = 1.0; - rtengine::IImage8* img = current.thumbnail->processThumbImage (current.pparams, current.height, scale); - if (img) - current.listener->updateImage (img, scale, current.pparams.crop); - } - -} - -void ThumbImageUpdater::stop () { - - gdk_threads_leave(); - tostop = true; - - if (threadPool) { - threadPool->shutdown(TRUE); - threadPool = NULL; - } - gdk_threads_enter(); -} - -void ThumbImageUpdater::removeJobs () { - - if (!qMutex) - return; - - qMutex->lock (); - while (!jqueue.empty()) - jqueue.pop_front (); - qMutex->unlock (); -} - -void ThumbImageUpdater::removeJobs (ThumbImageUpdateListener* listener) { - - if (!qMutex) - return; - - qMutex->lock (); - bool ready = false; - while (!ready) { - ready = true; - std::list::iterator i; - for (i=jqueue.begin(); i!=jqueue.end(); i++) - if (i->listener == listener) { - jqueue.erase (i); - ready = false; - break; - } - } - qMutex->unlock (); -} - -void ThumbImageUpdater::terminate () { - - stop (); - removeJobs (); + DEBUG("adding run request %s",t->getFileName().c_str()); + impl_->threadPool_->push(sigc::mem_fun(*impl_, &ThumbImageUpdater::Impl::processNextJob)); } +void +ThumbImageUpdater::removeJobs(ThumbImageUpdateListener* listener) +{ + DEBUG("removeJobs(%p)",listener); + + Glib::Mutex::Lock lock(impl_->mutex_); + + for( Impl::JobList::iterator i(impl_->jobs_.begin()); i != impl_->jobs_.end(); ) + { + if (i->listener_ == listener) + { + DEBUG("erasing specific job"); + Impl::JobList::iterator e(i++); + impl_->jobs_.erase(e); + } + else + { + ++i; + } + } + + while ( impl_->active_ != 0 ) + { + // XXX this is nasty... it would be nicer if we weren't called with + // this lock held + GThreadUnLock unlock; + DEBUG("waiting for running jobs1"); + impl_->inactive_waiting_ = true; + impl_->inactive_.wait(impl_->mutex_); + } +} + +void +ThumbImageUpdater::removeAllJobs(void) +{ + DEBUG("stop"); + + + Glib::Mutex::Lock lock(impl_->mutex_); + + impl_->jobs_.clear(); + + while ( impl_->active_ != 0 ) + { + // XXX this is nasty... it would be nicer if we weren't called with + // this lock held + GThreadUnLock unlock; + DEBUG("waiting for running jobs2"); + impl_->inactive_waiting_ = true; + impl_->inactive_.wait(impl_->mutex_); + } +} + diff --git a/rtgui/thumbimageupdater.h b/rtgui/thumbimageupdater.h index 909b90754..6fdce5bc1 100644 --- a/rtgui/thumbimageupdater.h +++ b/rtgui/thumbimageupdater.h @@ -26,45 +26,76 @@ class ThumbImageUpdateListener { - public: - virtual void updateImage (rtengine::IImage8* img, double scale, rtengine::procparams::CropParams cropParams) {} +public: + + /** + * @brief Called when thumbnail image is update + * + * @param img new thumbnail image + * @param scale scale (??) + * @param cropParams how it was cropped (??) + * + * @note no locks are held when called back + */ + virtual void updateImage (rtengine::IImage8* img, double scale, rtengine::procparams::CropParams cropParams) {} }; class ThumbImageUpdater { - struct Job { - Thumbnail* thumbnail; - rtengine::procparams::ProcParams pparams; - int height; - bool* priority; - ThumbImageUpdateListener* listener; - }; - - protected: - bool tostop; - bool stopped; - std::list jqueue; - Glib::Thread* thread; - Glib::Mutex* qMutex; - Glib::Mutex* startMutex; - //Glib::Thread **threadPool; - Glib::ThreadPool * threadPool; - public: - ThumbImageUpdater (); - ~ThumbImageUpdater (); - void add (Thumbnail* t, const rtengine::procparams::ProcParams& params, int height, bool* priority, ThumbImageUpdateListener* l); - void process (); - void stop (); - void removeJobs (); - void removeJobs (ThumbImageUpdateListener* listener); - void terminate (); + /** + * @brief Singleton entry point. + * + * @return Pointer to thumbnail image updater. + */ + static ThumbImageUpdater* getInstance(void); - void process_ (); - void processJob (Job current); + /** + * @brief Add an thumbnail image update request. + * + * Code will add the request to the queue and, if needed, start a pool + * thread to process it. + * + * @param t thumbnail + * @param params processing params (?) + * @param height how big + * @param priority if \c true then run as soon as possible + * @param l listener waiting on update + */ + void add(Thumbnail* t, const rtengine::procparams::ProcParams& params, + int height, bool* priority, bool upgrade, ThumbImageUpdateListener* l); + + /** + * @brief Remove jobs associated with listener \c l. + * + * Jobs being processed will be finished. Will not return till all jobs for + * \c l have been completed. + * + * @param listener jobs associated with this will be stopped + */ + void removeJobs(ThumbImageUpdateListener* listener); + + /** + * @brief Stop processing and remove all jobs. + * + * Will not return till all running jobs have completed. + */ + void removeAllJobs(void); + + private: + + ThumbImageUpdater(); + + class Impl; + Impl* impl_; }; -extern ThumbImageUpdater thumbImageUpdater; +/** + * @brief Singleton boiler plate. + * + * To use: \c thumbImageUpdater->start() , + */ +#define thumbImageUpdater ThumbImageUpdater::getInstance() #endif diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 3f9cd2e72..0ffe641de 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -34,86 +34,106 @@ using namespace rtengine::procparams; Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageData* cf) : fname(fname), cfs(*cf), cachemgr(cm), ref(1), enqueueNumber(0), tpp(NULL), - pparamsValid(false), needsReProcessing(true), lastImg(NULL) { + pparamsValid(false), needsReProcessing(true), lastImg(NULL), + quick_(false), initial_(false) { - mutex = new Glib::Mutex (); cfs.load (getCacheFileName ("data")+".txt"); loadProcParams (); - loadThumbnail (); + _loadThumbnail (); generateExifDateTimeStrings (); } Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, const std::string& md5) : fname(fname), cachemgr(cm), ref(1), enqueueNumber(0), tpp(NULL), pparamsValid(false), - needsReProcessing(true), lastImg(NULL) { + needsReProcessing(true), lastImg(NULL), + quick_(false), initial_(true) { - mutex = new Glib::Mutex (); cfs.md5 = md5; - generateThumbnailImage (); + _generateThumbnailImage (); loadProcParams (); cfs.recentlySaved = false; + + initial_ = false; } -void Thumbnail::generateThumbnailImage (bool internal) { +void Thumbnail::_generateThumbnailImage () { - if (!internal) - mutex->lock (); + // delete everything loaded into memory + delete tpp; + tpp = NULL; + delete [] lastImg; + lastImg = NULL; + tw = -1; + th = options.maxThumbnailHeight; -// delete everything loaded into memory - delete tpp; - tpp = NULL; - delete [] lastImg; - lastImg = NULL; - tw = -1; - th = options.maxThumbnailHeight; + // generate thumbnail image -// generate thumbnail image + Glib::ustring ext = getExtension (fname); + if (ext=="") + return; + cfs.supported = false; + cfs.exifValid = false; + cfs.timeValid = false; - Glib::ustring ext = getExtension (fname); - if (ext=="") - return; - cfs.supported = false; - cfs.exifValid = false; - cfs.timeValid = false; - - delete tpp; - tpp = NULL; - if (ext.lowercase()=="jpg" || ext.lowercase()=="png" || ext.lowercase()=="tif" || ext.lowercase()=="tiff") - tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1); - if (tpp) { - if (ext.lowercase()=="jpg") { - cfs.format = FT_Jpeg; - infoFromImage (fname); - } - else if (ext.lowercase()=="png") - cfs.format = FT_Png; - else if (ext.lowercase()=="tif" || ext.lowercase()=="tiff") { - cfs.format = FT_Tiff; - infoFromImage (fname); - } - } - else { - rtengine::RawMetaDataLocation ri; - tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1); - if (tpp) { - cfs.format = FT_Raw; - infoFromImage (fname, &ri); - } - } - if (tpp) { - // save thumbnail image to cache - saveThumbnail (); - cfs.supported = true; - } - needsReProcessing = true; + delete tpp; + tpp = NULL; + if (ext.lowercase()=="jpg" || ext.lowercase()=="png" || ext.lowercase()=="tif" || ext.lowercase()=="tiff") + tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1); + if (tpp) { + if (ext.lowercase()=="jpg") { + cfs.format = FT_Jpeg; + infoFromImage (fname); + } + else if (ext.lowercase()=="png") + cfs.format = FT_Png; + else if (ext.lowercase()=="tif" || ext.lowercase()=="tiff") { + cfs.format = FT_Tiff; + infoFromImage (fname); + } + } + else { + // RAW works like this: + // 1. if we are here it's because we aren't in the cache so load the JPG + // image out of the RAW. Mark as "quick". + // 2. if we don't find that then just grab the real image. + rtengine::RawMetaDataLocation ri; + if ( initial_ ) + { + quick_ = true; + tpp = rtengine::Thumbnail::loadQuickFromRaw (fname, ri, tw, th, 1); + } + if ( tpp == 0 ) + { + quick_ = false; + tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1); + } + if (tpp) { + cfs.format = FT_Raw; + infoFromImage (fname, &ri); + } + } + if (tpp ) + { + if ( !quick_ ) + { + _saveThumbnail (); + } + cfs.supported = true; + } + needsReProcessing = true; - cfs.save (getCacheFileName ("data")+".txt"); + if ( !quick_ ) + { + cfs.save (getCacheFileName ("data")+".txt"); + } - generateExifDateTimeStrings (); - - if (!internal) - mutex->unlock (); + generateExifDateTimeStrings (); +} + +void Thumbnail::generateThumbnailImage () { + Glib::Mutex::Lock lock(mutex); + _generateThumbnailImage(); } bool Thumbnail::isSupported () { @@ -231,8 +251,24 @@ bool Thumbnail::isEnqueued () { return enqueueNumber > 0; } -void Thumbnail::increaseRef () { ref++; } -void Thumbnail::decreaseRef () { ref--; if (!ref) cachemgr->closeThumbnail (this); } +void Thumbnail::increaseRef () +{ + Glib::Mutex::Lock lock(mutex); + ++ref; +} + +void Thumbnail::decreaseRef () +{ + Glib::Mutex::Lock lock(mutex); + if ( ref != 0 ) + { + --ref; + if ( ref == 0 ) + { + cachemgr->closeThumbnail (this); + } + } +} void Thumbnail::getThumbnailSize (int &w, int &h) { @@ -244,15 +280,33 @@ void Thumbnail::getThumbnailSize (int &w, int &h) { rtengine::IImage8* Thumbnail::processThumbImage (const rtengine::procparams::ProcParams& pparams, int h, double& scale) { - mutex->lock (); + Glib::Mutex::Lock lock(mutex); if (!tpp) return NULL; - rtengine::IImage8* res = tpp->processImage (pparams, h, rtengine::TI_Bilinear, scale); + if ( quick_ ) + { + return tpp->quickProcessImage (pparams, h, rtengine::TI_Nearest, scale); + } + else + { + return tpp->processImage (pparams, h, rtengine::TI_Bilinear, scale); + } +} - mutex->unlock (); - return res; +rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::ProcParams& pparams, int h, double& scale) { + + Glib::Mutex::Lock lock(mutex); + + if ( !quick_ ) + { + return 0; + } + + quick_ = false; + _generateThumbnailImage(); + return tpp->processImage (pparams, h, rtengine::TI_Bilinear, scale); } void Thumbnail::generateExifDateTimeStrings () { @@ -340,10 +394,7 @@ void Thumbnail::infoFromImage (const Glib::ustring& fname, rtengine::RawMetaData delete idata; } -void Thumbnail::loadThumbnail (bool internal, bool firstTrial) { - - if (!internal) - mutex->lock (); +void Thumbnail::_loadThumbnail(bool firstTrial) { needsReProcessing = true; delete tpp; @@ -357,9 +408,9 @@ void Thumbnail::loadThumbnail (bool internal, bool firstTrial) { succ = succ && tpp->readImage (getCacheFileName ("images")); if (!succ && firstTrial) { - generateThumbnailImage (true); + _generateThumbnailImage (); if (cfs.supported && firstTrial) - loadThumbnail (true, false); + _loadThumbnail (false); } else if (!succ) { delete tpp; @@ -375,11 +426,14 @@ void Thumbnail::loadThumbnail (bool internal, bool firstTrial) { tpp->init (); } - if (!internal) - mutex->unlock (); } -void Thumbnail::saveThumbnail () { +void Thumbnail::loadThumbnail (bool firstTrial) { + Glib::Mutex::Lock lock(mutex); + _loadThumbnail(firstTrial); +} + +void Thumbnail::_saveThumbnail () { if (!tpp) return; @@ -406,6 +460,12 @@ void Thumbnail::saveThumbnail () { tpp->writeData (getCacheFileName ("data")+".txt"); } +void Thumbnail::saveThumbnail () +{ + Glib::Mutex::Lock lock(mutex); + _saveThumbnail(); +} + void Thumbnail::updateCache () { if (pparamsValid) { diff --git a/rtgui/thumbnail.h b/rtgui/thumbnail.h index fc57d3b6a..0373f4f3d 100644 --- a/rtgui/thumbnail.h +++ b/rtgui/thumbnail.h @@ -31,7 +31,7 @@ class CacheManager; class Thumbnail { - Glib::Mutex* mutex; + Glib::Mutex mutex; Glib::ustring fname; // file name corresponding to the thumbnail CacheImageData cfs; // cache entry corresponding to the thumbnail @@ -58,12 +58,17 @@ class Thumbnail { Glib::ustring exifString; Glib::ustring dateTimeString; + bool initial_; + bool quick_; + // vector of listeners std::vector listeners; + void _loadThumbnail (bool firstTrial=true); + void _saveThumbnail (); + void _generateThumbnailImage (); void infoFromImage (const Glib::ustring& fname, rtengine::RawMetaDataLocation* rml=NULL); - void loadThumbnail (bool internal=false, bool firstTrial=true); - void saveThumbnail (); + void loadThumbnail (bool firstTrial=true); void generateExifDateTimeStrings (); Glib::ustring getCacheFileName (Glib::ustring subdir); @@ -79,6 +84,8 @@ class Thumbnail { void clearProcParams (int whoClearedIt=-1); void loadProcParams (); + bool isQuick() { return quick_; } + bool isPParamsValid() { return pparamsValid; } bool isRecentlySaved (); void imageDeveloped (); void imageEnqueued (); @@ -87,11 +94,11 @@ class Thumbnail { // unsigned char* getThumbnailImage (int &w, int &h, int fixwh=1); // fixwh = 0: fix w and calculate h, =1: fix h and calculate w rtengine::IImage8* processThumbImage (const rtengine::procparams::ProcParams& pparams, int h, double& scale); - void processThumbImage2 (const rtengine::procparams::ProcParams& pparams, int h, rtengine::IImage8*& img, double& scale) { img = processThumbImage(pparams, h, scale); } + rtengine::IImage8* upgradeThumbImage (const rtengine::procparams::ProcParams& pparams, int h, double& scale); void getThumbnailSize (int &w, int &h); void getFinalSize (const rtengine::procparams::ProcParams& pparams, int& w, int& h) { if (tpp) tpp->getFinalSize (pparams, w, h); } - void generateThumbnailImage (bool internal=false); + void generateThumbnailImage (); const Glib::ustring& getExifString (); const Glib::ustring& getDateTimeString (); @@ -122,7 +129,7 @@ class Thumbnail { void decreaseRef (); void updateCache (); - void reSaveThumbnail () { mutex->lock (); saveThumbnail(); mutex->unlock(); } + void saveThumbnail (); }; diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc index fed471bb1..f0f194844 100644 --- a/rtgui/tonecurve.cc +++ b/rtgui/tonecurve.cc @@ -57,7 +57,7 @@ ToneCurve::ToneCurve () : ToolPanel(), expAdd(false), blackAdd(false), brAdd(fal //----------- Black Level ---------------------------------- black = Gtk::manage (new Adjuster (M("TP_EXPOSURE_BLACKLEVEL"), 0, 16384, 1, 0)); pack_start (*black); - shcompr = Gtk::manage (new Adjuster (M("TP_EXPOSURE_COMPRSHADOWS"), -100, 100, 1, 0)); + shcompr = Gtk::manage (new Adjuster (M("TP_EXPOSURE_COMPRSHADOWS"), 0, 100, 1, 25)); pack_start (*shcompr); pack_start (*Gtk::manage (new Gtk::HSeparator())); diff --git a/rtgui/vignetting.cc b/rtgui/vignetting.cc index 622885f19..3839326c9 100644 --- a/rtgui/vignetting.cc +++ b/rtgui/vignetting.cc @@ -30,8 +30,20 @@ Vignetting::Vignetting () : vigAdd(false) { radius = Gtk::manage (new Adjuster (M("TP_VIGNETTING_RADIUS"), 0, 100, 1, 50)); radius->setAdjusterListener (this); + strength = Gtk::manage (new Adjuster (M("TP_VIGNETTING_STRENGTH"), 1, 100, 1, 1)); + strength->setAdjusterListener (this); + + centerX = Gtk::manage (new Adjuster (M("TP_VIGNETTING_CENTER_X"), -100, 100, 1, 0)); + centerX->setAdjusterListener (this); + + centerY = Gtk::manage (new Adjuster (M("TP_VIGNETTING_CENTER_Y"), -100, 100, 1, 0)); + centerY->setAdjusterListener (this); + pack_start (*amount); pack_start (*radius); + pack_start (*strength); + pack_start (*centerX); + pack_start (*centerY); show_all(); } @@ -45,6 +57,9 @@ void Vignetting::read (const ProcParams* pp, const ParamsEdited* pedited) { amount->setValue (pp->vignetting.amount); radius->setValue (pp->vignetting.radius); + strength->setValue (pp->vignetting.strength); + centerX->setValue (pp->vignetting.centerX); + centerY->setValue (pp->vignetting.centerY); enableListener (); } @@ -53,6 +68,9 @@ void Vignetting::write (ProcParams* pp, ParamsEdited* pedited) { pp->vignetting.amount = (int)amount->getValue (); pp->vignetting.radius = (int)radius->getValue (); + pp->vignetting.strength = (int)strength->getValue (); + pp->vignetting.centerX = (int)centerX->getValue (); + pp->vignetting.centerY = (int)centerY->getValue (); if (pedited) pedited->vignetting.amount = amount->getEditedState (); @@ -62,6 +80,9 @@ void Vignetting::setDefaults (const ProcParams* defParams, const ParamsEdited* p amount->setDefault (defParams->vignetting.amount); radius->setDefault (defParams->vignetting.radius); + strength->setDefault (defParams->vignetting.strength); + centerX->setDefault (defParams->vignetting.centerX); + centerY->setDefault (defParams->vignetting.centerY); if (pedited) amount->setDefaultEditedState (pedited->vignetting.amount ? Edited : UnEdited); @@ -72,7 +93,7 @@ void Vignetting::setDefaults (const ProcParams* defParams, const ParamsEdited* p void Vignetting::adjusterChanged (Adjuster* a, double newval) { if (listener) - listener->panelChanged (EvVignetting, Glib::ustring::compose ("%1=%3\n%2=%4", M("TP_VIGNETTING_AMOUNT"), M("TP_VIGNETTING_RADIUS"), (int)amount->getValue(), (int)radius->getValue())); + listener->panelChanged (EvVignetting, Glib::ustring::compose ("%1=%5\n%2=%6\n%3=%7\n%4=%8 %9", M("TP_VIGNETTING_AMOUNT"), M("TP_VIGNETTING_RADIUS"), M("TP_VIGNETTING_STRENGTH"), M("TP_VIGNETTING_CENTER"), (int)amount->getValue(), (int)radius->getValue(), (int)strength->getValue(), (int)centerX->getValue(), (int)centerY->getValue())); } void Vignetting::setAdjusterBehavior (bool bvadd) { diff --git a/rtgui/vignetting.h b/rtgui/vignetting.h index a4fc0dcd3..64d51c75d 100644 --- a/rtgui/vignetting.h +++ b/rtgui/vignetting.h @@ -28,6 +28,9 @@ class Vignetting : public Gtk::VBox, public AdjusterListener, public ToolPanel { protected: Adjuster* amount; Adjuster* radius; + Adjuster* strength; + Adjuster* centerX; + Adjuster* centerY; bool vigAdd; public: