diff --git a/rtdata/CMakeLists.txt b/rtdata/CMakeLists.txt index 2a87a74d4..b104fb3a3 100644 --- a/rtdata/CMakeLists.txt +++ b/rtdata/CMakeLists.txt @@ -4,6 +4,7 @@ file (GLOB LANGUAGEFILES "languages/*") file (GLOB SOUNDFILES "sounds/*") file (GLOB INPUTICCFILES "iccprofiles/input/*") file (GLOB OUTPUTICCFILES "iccprofiles/output/*") +file (GLOB DCPFILES "dcpprofiles/*") # THEMEDIR includes subfolders for image resources for some themes; doing the normal glob won't work. set (THEMEDIR "themes") set (IMAGESDIR "images") @@ -33,6 +34,7 @@ install (FILES ${PROFILEFILES} DESTINATION ${DATADIR}/profiles) install (FILES ${SOUNDFILES} DESTINATION ${DATADIR}/sounds) install (FILES ${INPUTICCFILES} DESTINATION ${DATADIR}/iccprofiles/input) install (FILES ${OUTPUTICCFILES} DESTINATION ${DATADIR}/iccprofiles/output) +install (FILES ${DCPFILES} DESTINATION ${DATADIR}/dcpprofiles) install (DIRECTORY ${THEMEDIR} DESTINATION ${DATADIR}) install (DIRECTORY ${IMAGESDIR} DESTINATION ${DATADIR} FILES_MATCHING PATTERN "index.theme") install (DIRECTORY ${IMAGESDIR} DESTINATION ${DATADIR} FILES_MATCHING PATTERN "*.png") diff --git a/rtdata/dcpprofiles/Canon EOS 20D.dcp b/rtdata/dcpprofiles/Canon EOS 20D.dcp new file mode 100644 index 000000000..0dfc1c80b Binary files /dev/null and b/rtdata/dcpprofiles/Canon EOS 20D.dcp differ diff --git a/rtdata/dcpprofiles/Canon EOS 40D.dcp b/rtdata/dcpprofiles/Canon EOS 40D.dcp new file mode 100644 index 000000000..98cf8ce09 Binary files /dev/null and b/rtdata/dcpprofiles/Canon EOS 40D.dcp differ diff --git a/rtdata/dcpprofiles/Canon EOS 450D.dcp b/rtdata/dcpprofiles/Canon EOS 450D.dcp new file mode 100644 index 000000000..1db7ee1cd Binary files /dev/null and b/rtdata/dcpprofiles/Canon EOS 450D.dcp differ diff --git a/rtdata/dcpprofiles/Canon EOS 5D.dcp b/rtdata/dcpprofiles/Canon EOS 5D.dcp new file mode 100644 index 000000000..11fab1900 Binary files /dev/null and b/rtdata/dcpprofiles/Canon EOS 5D.dcp differ diff --git a/rtdata/dcpprofiles/Canon EOS-1D Mark III.dcp b/rtdata/dcpprofiles/Canon EOS-1D Mark III.dcp new file mode 100644 index 000000000..0bbaceb2c Binary files /dev/null and b/rtdata/dcpprofiles/Canon EOS-1D Mark III.dcp differ diff --git a/rtdata/dcpprofiles/Canon PowerShot G10.dcp b/rtdata/dcpprofiles/Canon PowerShot G10.dcp new file mode 100644 index 000000000..17a36f6fb Binary files /dev/null and b/rtdata/dcpprofiles/Canon PowerShot G10.dcp differ diff --git a/rtdata/dcpprofiles/Canon PowerShot G12.dcp b/rtdata/dcpprofiles/Canon PowerShot G12.dcp new file mode 100644 index 000000000..aa7d680f7 Binary files /dev/null and b/rtdata/dcpprofiles/Canon PowerShot G12.dcp differ diff --git a/rtdata/dcpprofiles/Nikon D200.dcp b/rtdata/dcpprofiles/Nikon D200.dcp new file mode 100644 index 000000000..3a31735d3 Binary files /dev/null and b/rtdata/dcpprofiles/Nikon D200.dcp differ diff --git a/rtdata/dcpprofiles/Nikon D3000.dcp b/rtdata/dcpprofiles/Nikon D3000.dcp new file mode 100644 index 000000000..7a6d79fa5 Binary files /dev/null and b/rtdata/dcpprofiles/Nikon D3000.dcp differ diff --git a/rtdata/dcpprofiles/Nikon D3100.dcp b/rtdata/dcpprofiles/Nikon D3100.dcp new file mode 100644 index 000000000..776ad4c52 Binary files /dev/null and b/rtdata/dcpprofiles/Nikon D3100.dcp differ diff --git a/rtdata/dcpprofiles/Nikon D3S.dcp b/rtdata/dcpprofiles/Nikon D3S.dcp new file mode 100644 index 000000000..e5c5f6ab8 Binary files /dev/null and b/rtdata/dcpprofiles/Nikon D3S.dcp differ diff --git a/rtdata/dcpprofiles/Nikon D700.dcp b/rtdata/dcpprofiles/Nikon D700.dcp new file mode 100644 index 000000000..969c6b964 Binary files /dev/null and b/rtdata/dcpprofiles/Nikon D700.dcp differ diff --git a/rtdata/dcpprofiles/Olympus E-P2.dcp b/rtdata/dcpprofiles/Olympus E-P2.dcp new file mode 100644 index 000000000..bdc25cb0c Binary files /dev/null and b/rtdata/dcpprofiles/Olympus E-P2.dcp differ diff --git a/rtdata/dcpprofiles/Panasonic DMC-FZ150.dcp b/rtdata/dcpprofiles/Panasonic DMC-FZ150.dcp new file mode 100644 index 000000000..5bd3646f6 Binary files /dev/null and b/rtdata/dcpprofiles/Panasonic DMC-FZ150.dcp differ diff --git a/rtdata/dcpprofiles/Panasonic DMC-FZ38.dcp b/rtdata/dcpprofiles/Panasonic DMC-FZ38.dcp new file mode 100644 index 000000000..e8bd00d37 Binary files /dev/null and b/rtdata/dcpprofiles/Panasonic DMC-FZ38.dcp differ diff --git a/rtdata/dcpprofiles/Panasonic DMC-G1.dcp b/rtdata/dcpprofiles/Panasonic DMC-G1.dcp new file mode 100644 index 000000000..aee19766b Binary files /dev/null and b/rtdata/dcpprofiles/Panasonic DMC-G1.dcp differ diff --git a/rtdata/dcpprofiles/Panasonic DMC-G3.dcp b/rtdata/dcpprofiles/Panasonic DMC-G3.dcp new file mode 100644 index 000000000..9247f6df7 Binary files /dev/null and b/rtdata/dcpprofiles/Panasonic DMC-G3.dcp differ diff --git a/rtdata/dcpprofiles/Panasonic DMC-GH1.dcp b/rtdata/dcpprofiles/Panasonic DMC-GH1.dcp new file mode 100644 index 000000000..c7aa00ef1 Binary files /dev/null and b/rtdata/dcpprofiles/Panasonic DMC-GH1.dcp differ diff --git a/rtdata/dcpprofiles/Panasonic DMC-GH2.dcp b/rtdata/dcpprofiles/Panasonic DMC-GH2.dcp new file mode 100644 index 000000000..fda7ef83a Binary files /dev/null and b/rtdata/dcpprofiles/Panasonic DMC-GH2.dcp differ diff --git a/rtdata/dcpprofiles/Pentax K200D.dcp b/rtdata/dcpprofiles/Pentax K200D.dcp new file mode 100644 index 000000000..c1a5b7933 Binary files /dev/null and b/rtdata/dcpprofiles/Pentax K200D.dcp differ diff --git a/rtdata/dcpprofiles/Sony DSLR-A700.dcp b/rtdata/dcpprofiles/Sony DSLR-A700.dcp new file mode 100644 index 000000000..2dad1052e Binary files /dev/null and b/rtdata/dcpprofiles/Sony DSLR-A700.dcp differ diff --git a/rtdata/dcpprofiles/Sony DSLR-A900.dcp b/rtdata/dcpprofiles/Sony DSLR-A900.dcp new file mode 100644 index 000000000..828682205 Binary files /dev/null and b/rtdata/dcpprofiles/Sony DSLR-A900.dcp differ diff --git a/rtdata/dcpprofiles/Sony NEX-5N.dcp b/rtdata/dcpprofiles/Sony NEX-5N.dcp new file mode 100644 index 000000000..263733b04 Binary files /dev/null and b/rtdata/dcpprofiles/Sony NEX-5N.dcp differ diff --git a/rtdata/dcpprofiles/Sony SLT-A55V.dcp b/rtdata/dcpprofiles/Sony SLT-A55V.dcp new file mode 100644 index 000000000..ce07b30f6 Binary files /dev/null and b/rtdata/dcpprofiles/Sony SLT-A55V.dcp differ diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 98b257c1c..aa6bb96bb 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -886,14 +886,14 @@ TP_HSVEQUALIZER_VAL;V TP_ICM_BLENDCMSMATRIX;Lichter aus Matrix einmischen TP_ICM_BLENDCMSMATRIX_TOOLTIP;Stellt bei Verwendung von LUT-basierten\nICC-Profilen die Lichter wieder her TP_ICM_FILEDLGFILTERANY;Alle Dateien -TP_ICM_FILEDLGFILTERICM;ICC-Profildateien -TP_ICM_INPUTCAMERAICC;Kamera-Standard oder ICC -TP_ICM_INPUTCAMERAICC_TOOLTIP;RawTherapee Standard ICC-Farbprofil verwenden; präziser als einfache Matrix, aber nur für einige Kameras verfügbar +TP_ICM_FILEDLGFILTERICM;Profildateien +TP_ICM_INPUTCAMERAICC;Kamera-Standard oder DCP/ICC +TP_ICM_INPUTCAMERAICC_TOOLTIP;RawTherapee Standard DCP/ICC-Farbprofil verwenden; präziser als einfache Matrix, aber nur für einige Kameras verfügbar TP_ICM_INPUTCAMERA;Kamera-Standard TP_ICM_INPUTCAMERA_TOOLTIP;Einfache Standard Farbkorrektur-Matrix von DCRAW oder die im DNG eingebettete verwenden TP_ICM_INPUTCUSTOM;Benutzerdefiniert -TP_ICM_INPUTCUSTOM_TOOLTIP;Eigene ICC-Farbprofildatei für die Kamera verwenden -TP_ICM_INPUTDLGLABEL;Wähle Eingabe-ICC-Profil... +TP_ICM_INPUTCUSTOM_TOOLTIP;Eigene DCP/ICC-Farbprofildatei für die Kamera verwenden +TP_ICM_INPUTDLGLABEL;Wähle Eingabe-DCP/ICC-Profil... TP_ICM_INPUTEMBEDDED;Eingebettetes verwenden, falls möglich TP_ICM_INPUTEMBEDDED_TOOLTIP;Farbprofil verwenden, das ggf. in nicht-RAW Dateien eingebettet ist TP_ICM_INPUTNONE;Kein Profil diff --git a/rtdata/languages/default b/rtdata/languages/default index bfa94fe7e..6479d21ab 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -917,14 +917,14 @@ TP_HSVEQUALIZER_VAL;V TP_ICM_BLENDCMSMATRIX;Blend highlights with matrix TP_ICM_BLENDCMSMATRIX_TOOLTIP;Enable to recover blown highlights when using LUT based ICC profiles TP_ICM_FILEDLGFILTERANY;Any files -TP_ICM_FILEDLGFILTERICM;ICC Profile Files -TP_ICM_INPUTCAMERAICC;Auto-matched camera-specific ICC -TP_ICM_INPUTCAMERAICC_TOOLTIP;Use RawTherapee's camera-specific ICC input profile that is more precise than a simpler matrix. Available for some cameras, these profiles are stored in /iccprofiles/input directory and are automatically retrieved based on file name matching the exact model name of the camera. +TP_ICM_FILEDLGFILTERICM;Profile files +TP_ICM_INPUTCAMERAICC;Auto-matched camera-specific profile +TP_ICM_INPUTCAMERAICC_TOOLTIP;Use RawTherapee's camera-specific DCP or ICC input profile that is more precise than a simpler matrix. Available for some cameras, these profiles are stored in /iccprofiles/input directory and are automatically retrieved based on file name matching the exact model name of the camera. TP_ICM_INPUTCAMERA;Camera standard TP_ICM_INPUTCAMERA_TOOLTIP;Use simple color matrix by DCRAW, enhanced RawTherapee version (whichever is available based on camera model) or embedded in DNG. TP_ICM_INPUTCUSTOM;Custom -TP_ICM_INPUTCUSTOM_TOOLTIP;Select your own ICC color profile file for the camera -TP_ICM_INPUTDLGLABEL;Select Input ICC Profile... +TP_ICM_INPUTCUSTOM_TOOLTIP;Select your own DCP/ICC color profile file for the camera +TP_ICM_INPUTDLGLABEL;Select Input DCP/ICC Profile... TP_ICM_INPUTEMBEDDED;Use Embedded, if possible TP_ICM_INPUTEMBEDDED_TOOLTIP;Use color profile embedded in non-raw files TP_ICM_INPUTNONE;No profile diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index ea2685e8c..b4f854481 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -14,7 +14,7 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona jpeg_memsrc.cc jdatasrc.cc PF_correct_RT.cc dirpyrLab_denoise.cc dirpyrLab_equalizer.cc dirpyr_equalizer.cc - calc_distort.cc + calc_distort.cc dcp.cc klt/convolve.cc klt/error.cc klt/klt.cc klt/klt_util.cc klt/pnmio.cc klt/pyramid.cc klt/selectGoodFeatures.cc klt/storeFeatures.cc klt/trackFeatures.cc klt/writeFeatures.cc ) diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc new file mode 100644 index 000000000..8744697c4 --- /dev/null +++ b/rtengine/dcp.cc @@ -0,0 +1,458 @@ +/* +* This file is part of RawTherapee. +* +* Copyright (c) 2012 Oliver Duis +* +* RawTherapee is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* 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 "dcp.h" +#include +#include "safegtk.h" +#include "iccmatrices.h" +#include "iccstore.h" +#include "rawimagesource.h" +#include "improcfun.h" + +using namespace rtengine; +using namespace rtexif; + +#undef CLIP +#define MAXVAL 0xffff +#define CLIP(a) ((a)>0?((a)getTag(TagCalibrationIlluminant2); + bool use2nd = (tag!=NULL && tag->toInt(0,SHORT)>=20 && tag->toInt(0,SHORT)<=23); + + // Color Matrix + tag = tagDir->getTag( use2nd ? TagColorMatrix2 : TagColorMatrix1); + + for (int row=0;row<3;row++) { + for (int col=0;col<3;col++) { + mColorMatrix[col][row]=(float)tag->toDouble((col+row*3)*8); + } + } + + // LUT profile? Divisions counts + bool useSimpleLookup=false; + tag = tagDir->getTag(TagProfileHueSatMapDims); + if (tag==NULL) { + tag=tagDir->getTag(TagProfileLookTableDims); + useSimpleLookup=true; + } + + if (tag!=NULL) { + iHueDivisions=tag->toInt(0); iSatDivisions=tag->toInt(4); iValDivisions=tag->toInt(8); + + // Saturation maps. Need to be unwinded. + tag = tagDir->getTag(useSimpleLookup ? TagProfileLookTableData : ( use2nd ? TagProfileHueSatMapData2 : TagProfileHueSatMapData1)); + iArrayCount = tag->getCount()/3; + + aDeltas=new HSBModify[iArrayCount]; + + const int TIFFFloatSize=4; + for (int i=0;itoDouble((i*3)*TIFFFloatSize); + aDeltas[i].fSatScale=tag->toDouble((i*3+1)*TIFFFloatSize); + aDeltas[i].fValScale=tag->toDouble((i*3+2)*TIFFFloatSize); + } + } + + if (pFile!=NULL) fclose(pFile); + delete tagDir; + + if (iArrayCount>0) { + // Convert DNG color matrix to xyz_cam compatible matrix + int i,j,k; + + double cam_xyz[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + for (i=0; i<3; i++) + for (j=0; j<3; j++) + for (k=0; k<3; k++) + cam_xyz[i][j] += mColorMatrix[j][k] * (i==k); + + + // Multiply out XYZ colorspace + double cam_rgb[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + for (k=0; k < 3; k++) + cam_rgb[i][j] += cam_xyz[i][k] * xyz_sRGB[k][j]; + + // Normalize cam_rgb so that: cam_rgb * (1,1,1) is (1,1,1,1) + double num; + for (i=0; i<3; i++) { + for (num=j=0; j<3; j++) num += cam_rgb[i][j]; + for (j=0; j<3; j++) cam_rgb[i][j] /= num; + } + + double rgb_cam[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + RawImageSource::inverse33 (cam_rgb, rgb_cam); + + memset(mXYZCAM,0,sizeof(mXYZCAM)); + for (i=0; i<3; i++) + for (j=0; j<3; j++) + for (k=0; k<3; k++) + mXYZCAM[i][j] += xyz_sRGB[i][k] * rgb_cam[k][j]; + } +} + +DCPProfile::~DCPProfile() { + delete[] aDeltas; +} + +void DCPProfile::Apply(Imagefloat *pImg, Glib::ustring workingSpace) const { + TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace); + + if (iArrayCount==0) { + //===== No LUT- Calculate matrix for direct conversion raw>working space + 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++) + for (int k=0; k<3; k++) + mat[i][j] += mWork[i][k] * mXYZCAM[k][j]; + + // Apply the matrix part +#pragma omp parallel for + for (int y=0; yheight; y++) { + float newr, newg, newb; + for (int x=0; xwidth; x++) { + newr = mat[0][0]*pImg->r[y][x] + mat[0][1]*pImg->g[y][x] + mat[0][2]*pImg->b[y][x]; + newg = mat[1][0]*pImg->r[y][x] + mat[1][1]*pImg->g[y][x] + mat[1][2]*pImg->b[y][x]; + newb = mat[2][0]*pImg->r[y][x] + mat[2][1]*pImg->g[y][x] + mat[2][2]*pImg->b[y][x]; + + pImg->r[y][x] = newr; pImg->g[y][x] = newg; pImg->b[y][x] = newb; + } + } + } + else { + //===== LUT available- Calculate matrix for conversion raw>ProPhoto + double m2ProPhoto[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++) + for (int k=0; k<3; k++) + m2ProPhoto[i][j] += prophoto_xyz[i][k] * mXYZCAM[k][j]; + + double m2Work[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++) + for (int k=0; k<3; k++) + m2Work[i][j] += mWork[i][k] * xyz_prophoto[k][j]; + + // Preperations for LUT + float hScale = (iHueDivisions < 2) ? 0.0f : (iHueDivisions * (1.0f / 6.0f)); + float sScale = (float) (iSatDivisions - 1); + float vScale = (float) (iValDivisions - 1); + + int maxHueIndex0 = iHueDivisions - 1; + int maxSatIndex0 = iSatDivisions - 2; + int maxValIndex0 = iValDivisions - 2; + + const HSBModify *tableBase = aDeltas; + + int hueStep = iSatDivisions; + int valStep = iHueDivisions * hueStep; + + // Convert to prophoto and apply LUT +#pragma omp parallel for + for (int y=0; yheight; y++) { + float newr, newg, newb, h,s,v; + for (int x=0; xwidth; x++) { + newr = m2ProPhoto[0][0]*pImg->r[y][x] + m2ProPhoto[0][1]*pImg->g[y][x] + m2ProPhoto[0][2]*pImg->b[y][x]; + newg = m2ProPhoto[1][0]*pImg->r[y][x] + m2ProPhoto[1][1]*pImg->g[y][x] + m2ProPhoto[1][2]*pImg->b[y][x]; + newb = m2ProPhoto[2][0]*pImg->r[y][x] + m2ProPhoto[2][1]*pImg->g[y][x] + m2ProPhoto[2][2]*pImg->b[y][x]; + + ImProcFunctions::rgb2hsv(newr, newg, newb, h , s, v); + h*=6.f; // RT calculates in [0,1] + + // Apply the HueSatMap. Ported from Adobes reference implementation + float hueShift, satScale, valScale; + + if (iValDivisions < 2) // Optimize most common case of "2.5D" table. + { + float hScaled = h * hScale; + float sScaled = s * sScale; + + int hIndex0 = (int) hScaled; + int sIndex0 = (int) sScaled; + + sIndex0 = MIN (sIndex0, maxSatIndex0); + + int hIndex1 = hIndex0 + 1; + + if (hIndex0 >= maxHueIndex0) + { + hIndex0 = maxHueIndex0; + hIndex1 = 0; + } + + float hFract1 = hScaled - (float) hIndex0; + float sFract1 = sScaled - (float) sIndex0; + + float hFract0 = 1.0f - hFract1; + float sFract0 = 1.0f - sFract1; + + const HSBModify *entry00 = tableBase + hIndex0 * hueStep + + sIndex0; + + const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; + + float hueShift0 = hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift; + + float satScale0 = hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale; + + float valScale0 = hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale; + + entry00++; + entry01++; + + float hueShift1 = hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift; + + float satScale1 = hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale; + + float valScale1 = hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale; + + hueShift = sFract0 * hueShift0 + sFract1 * hueShift1; + satScale = sFract0 * satScale0 + sFract1 * satScale1; + valScale = sFract0 * valScale0 + sFract1 * valScale1; + + } else { + + float hScaled = h * hScale; + float sScaled = s * sScale; + float vScaled = v * vScale; + + int hIndex0 = (int) hScaled; + int sIndex0 = (int) sScaled; + int vIndex0 = (int) vScaled; + + sIndex0 = MIN (sIndex0, maxSatIndex0); + vIndex0 = MIN (vIndex0, maxValIndex0); + + int hIndex1 = hIndex0 + 1; + + if (hIndex0 >= maxHueIndex0) + { + hIndex0 = maxHueIndex0; + hIndex1 = 0; + } + + float hFract1 = hScaled - (float) hIndex0; + float sFract1 = sScaled - (float) sIndex0; + float vFract1 = vScaled - (float) vIndex0; + + float hFract0 = 1.0f - hFract1; + float sFract0 = 1.0f - sFract1; + float vFract0 = 1.0f - vFract1; + + const HSBModify *entry00 = tableBase + vIndex0 * valStep + + hIndex0 * hueStep + + sIndex0; + + const HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; + + const HSBModify *entry10 = entry00 + valStep; + const HSBModify *entry11 = entry01 + valStep; + + float hueShift0 = vFract0 * (hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift) + + vFract1 * (hFract0 * entry10->fHueShift + + hFract1 * entry11->fHueShift); + + float satScale0 = vFract0 * (hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale) + + vFract1 * (hFract0 * entry10->fSatScale + + hFract1 * entry11->fSatScale); + + float valScale0 = vFract0 * (hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale) + + vFract1 * (hFract0 * entry10->fValScale + + hFract1 * entry11->fValScale); + + entry00++; + entry01++; + entry10++; + entry11++; + + float hueShift1 = vFract0 * (hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift) + + vFract1 * (hFract0 * entry10->fHueShift + + hFract1 * entry11->fHueShift); + + float satScale1 = vFract0 * (hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale) + + vFract1 * (hFract0 * entry10->fSatScale + + hFract1 * entry11->fSatScale); + + float valScale1 = vFract0 * (hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale) + + vFract1 * (hFract0 * entry10->fValScale + + hFract1 * entry11->fValScale); + + hueShift = sFract0 * hueShift0 + sFract1 * hueShift1; + satScale = sFract0 * satScale0 + sFract1 * satScale1; + valScale = sFract0 * valScale0 + sFract1 * valScale1; + } + + hueShift *= (6.0f / 360.0f); // Convert to internal hue range. + + h += hueShift; + s *= satScale; // no clipping here, we are RT float :-) + v *= valScale; + + // RT range correction + if (h < 0.0f) h += 6.0f; + if (h >= 6.0f) h -= 6.0f; + h/=6.f; + ImProcFunctions::hsv2rgb( h, s, v, newr, newg, newb); + + pImg->r[y][x] = m2Work[0][0]*newr + m2Work[0][1]*newg + m2Work[0][2]*newb; + pImg->g[y][x] = m2Work[1][0]*newr + m2Work[1][1]*newg + m2Work[1][2]*newb; + pImg->b[y][x] = m2Work[2][0]*newr + m2Work[2][1]*newg + m2Work[2][2]*newb; + } + } + } +} + + +// Integer variant is legacy, only used for thumbs. Simply take the matrix here +void DCPProfile::Apply(Image16 *pImg, Glib::ustring workingSpace) const { + TMatrix mWork = iccStore->workingSpaceInverseMatrix (workingSpace); + + // Calculate matrix for direct conversion raw>working space + 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++) + for (int k=0; k<3; k++) + mat[i][j] += mWork[i][k] * mXYZCAM[k][j]; + + // Apply the matrix part +#pragma omp parallel for + for (int y=0; yheight; y++) { + float newr, newg, newb; + for (int x=0; xwidth; x++) { + newr = mat[0][0]*pImg->r[y][x] + mat[0][1]*pImg->g[y][x] + mat[0][2]*pImg->b[y][x]; + newg = mat[1][0]*pImg->r[y][x] + mat[1][1]*pImg->g[y][x] + mat[1][2]*pImg->b[y][x]; + newb = mat[2][0]*pImg->r[y][x] + mat[2][1]*pImg->g[y][x] + mat[2][2]*pImg->b[y][x]; + + pImg->r[y][x] = CLIP((int)newr); pImg->g[y][x] = CLIP((int)newg); pImg->b[y][x] = CLIP((int)newb); + } + } +} + + +// Generates as singleton +DCPStore* DCPStore::getInstance() +{ + static DCPStore* instance_ = 0; + if ( instance_ == 0 ) + { + static Glib::Mutex smutex_; + Glib::Mutex::Lock lock(smutex_); + if ( instance_ == 0 ) + { + instance_ = new DCPStore(); + } + } + return instance_; +} + +// Reads all profiles from the given profiles dir +void DCPStore::init (Glib::ustring rtProfileDir) { + Glib::Mutex::Lock lock(mtx); + + fileStdProfiles.clear(); + + Glib::ustring rootDirName=rtProfileDir; + + if (rootDirName!="") { + std::deque qDirs; + + qDirs.push_front(rootDirName); + + while (qDirs.size()) { + // process directory + Glib::ustring dirname = qDirs.back(); + qDirs.pop_back(); + + Glib::Dir* dir = NULL; + try { + if (!safe_file_test (dirname, Glib::FILE_TEST_IS_DIR)) return; + dir = new Glib::Dir (dirname); + } + catch (Glib::Exception& fe) { + return; + } + dirname = dirname + "/"; + for (Glib::DirIterator i = dir->begin(); i!=dir->end(); ++i) { + Glib::ustring fname = dirname + *i; + Glib::ustring sname = *i; + // ignore directories + if (!safe_file_test (fname, Glib::FILE_TEST_IS_DIR)) { + int lastdot = sname.find_last_of ('.'); + if (lastdot!=Glib::ustring::npos && lastdot<=(int)sname.size()-4 && (!sname.casefold().compare (lastdot, 4, ".dcp"))) { + Glib::ustring camShortName = sname.substr(0,lastdot).uppercase(); + fileStdProfiles[camShortName]=fname; // they will be loaded and cached on demand + } + } else qDirs.push_front(fname); // for later scanning + } + delete dir; + } + } +} + +DCPProfile* DCPStore::getProfile (Glib::ustring filename) { + Glib::Mutex::Lock lock(mtx); + + std::map::iterator r = profileCache.find (filename); + if (r!=profileCache.end()) return r->second; + + // Add profile + profileCache[filename]=new DCPProfile(filename); + + return profileCache[filename]; +} + +DCPProfile* DCPStore::getStdProfile(Glib::ustring camShortName) { + std::map::iterator r = fileStdProfiles.find (camShortName); + if (r==fileStdProfiles.end()) return NULL; + + return getProfile(r->second); +} + +bool DCPStore::isValidDCPFileName(Glib::ustring filename) const { + return safe_file_test (filename, Glib::FILE_TEST_EXISTS) + && !filename.casefold().compare (filename.find_last_of ('.'), 4, ".dcp"); +} \ No newline at end of file diff --git a/rtengine/dcp.h b/rtengine/dcp.h new file mode 100644 index 000000000..7ff047228 --- /dev/null +++ b/rtengine/dcp.h @@ -0,0 +1,76 @@ +/* +* This file is part of RawTherapee. +* +* Copyright (c) 2012 Oliver Duis +* +* RawTherapee is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* 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 _DCP_ +#define _DCP_ + +#include "imagefloat.h" +#include +#include +#include + +namespace rtengine { + + class DCPProfile { + struct HSBModify + { + float fHueShift; + float fSatScale; + float fValScale; + }; + + double mColorMatrix[3][3]; + double mXYZCAM[3][3]; // compatible to RTs xyz_cam + HSBModify *aDeltas; + + int iHueDivisions, iSatDivisions, iValDivisions; + + int iHueStep, iValStep, iArrayCount; + + public: + DCPProfile(Glib::ustring fname); + ~DCPProfile(); + + void Apply(Imagefloat *pImg, Glib::ustring workingSpace) const; + void Apply(Image16 *pImg, Glib::ustring workingSpace) const; + }; + + class DCPStore { + Glib::Mutex mtx; + + // these contain standard profiles from RT. keys are all in uppercase, file path is value + std::map fileStdProfiles; + + // Maps file name to profile as cache + std::map profileCache; + + public: + void init(Glib::ustring rtProfileDir); + + bool isValidDCPFileName(Glib::ustring filename) const; + + DCPProfile* getProfile(Glib::ustring filename); + DCPProfile* getStdProfile(Glib::ustring camShortName); + + static DCPStore* getInstance(); + }; + + #define dcpStore DCPStore::getInstance() +}; +#endif diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 9211da117..7f9c005e2 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -944,7 +944,7 @@ fclose(f);*/ void ImProcFunctions::hsv2rgb (float h, float s, float v, float &r, float &g, float &b) { float h1 = h*6; // sector 0 to 5 - int i = floor( h1 ); + int i = (int)h1; // floor() is very slow, and h1 is always >0 float f = h1 - i; // fractional part of h float p = v * ( 1 - s ); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 7affea848..7f1650c69 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -162,8 +162,8 @@ class ImProcFunctions { static double getAutoDistor (const Glib::ustring& fname, int thumb_size); double getTransformAutoFill (int oW, int oH); - void rgb2hsv (float r, float g, float b, float &h, float &s, float &v); - void hsv2rgb (float h, float s, float v, float &r, float &g, float &b); + static void rgb2hsv (float r, float g, float b, float &h, float &s, float &v); + static void hsv2rgb (float h, float s, float v, float &r, float &g, float &b); void xyz2srgb (float x, float y, float z, float &r, float &g, float &b); void xyz2rgb (float x, float y, float z, float &r, float &g, float &b, float rgb_xyz[3][3]); void Lab2XYZ(float L, float a, float b, float &x, float &y, float &z); diff --git a/rtengine/init.cc b/rtengine/init.cc index 597acb075..ced941921 100644 --- a/rtengine/init.cc +++ b/rtengine/init.cc @@ -18,6 +18,7 @@ */ #include "rtengine.h" #include "iccstore.h" +#include "dcp.h" #include "improcfun.h" #include "improccoordinator.h" #include "curves.h" @@ -37,6 +38,8 @@ int init (const Settings* s, Glib::ustring baseDir) { iccStore->init (s->iccDirectory, baseDir + "/iccprofiles"); iccStore->findDefaultMonitorProfile(); + dcpStore->init (baseDir + "/dcpprofiles"); + ProcParams::init (); CurveFactory::init (); ImProcFunctions::initMunsell(); diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index f915231ba..c7652e211 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -31,6 +31,7 @@ #include "slicer.h" #include #include "../rtgui/options.h" +#include "dcp.h" @@ -1680,10 +1681,16 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams //MyTime t1, t2, t3; //t1.set (); cmsHPROFILE in; - if (!findInputProfile(cmp.input, embedded, camName, in)) return; + DCPProfile *dcpProf; + + if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return; + + if (dcpProf!=NULL) { + dcpProf->Apply(im, cmp.working); + } else { // Calculate matrix for direct conversion raw>working space TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working); - float mat[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + 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++) for (int k=0; k<3; k++) @@ -1887,7 +1894,7 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams if (imgPreLCMS!=NULL) delete imgPreLCMS; } - + } //t3.set (); // printf ("ICM TIME: %d\n", t3.etime(t1)); } @@ -1897,8 +1904,13 @@ void RawImageSource::colorSpaceConversion (Imagefloat* im, ColorManagementParams // Converts raw image including ICC input profile to working space - 16bit int version void RawImageSource::colorSpaceConversion16 (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double camMatrix[3][3], std::string camName, double& defgain) { cmsHPROFILE in; - if (!findInputProfile(cmp.input, embedded, camName, in)) return; + DCPProfile *dcpProf; + if (!findInputProfile(cmp.input, embedded, camName, &dcpProf, in)) return; + + if (dcpProf!=NULL) { + dcpProf->Apply(im, cmp.working); + } else { if (in==NULL) { // Take camprofile from DCRAW // in this case we avoid using the slllllooooooowwww lcms @@ -1970,23 +1982,30 @@ TMatrix work = iccStore->workingSpaceInverseMatrix (cmp.working); cmsDeleteTransform(hTransform); } - + } //t3.set (); // printf ("ICM TIME: %d\n", t3.etime(t1)); } // Determine RAW input and output profiles. Returns TRUE on success -bool RawImageSource::findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, cmsHPROFILE& in) { +bool RawImageSource::findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in) { in=NULL; // cam will be taken on NULL + *dcpProf=NULL; if (inProfile == "(none)") return false; if (inProfile == "(embedded)" && embedded) { in = embedded; } else if (inProfile=="(cameraICC)") { - in = iccStore->getStdProfile(camName); + // DCPs have higher quality, so use them first + *dcpProf=dcpStore->getStdProfile(camName); + if (*dcpProf==NULL) in = iccStore->getStdProfile(camName); } else if (inProfile!="(camera)" && inProfile!="") { - in = iccStore->getProfile (inProfile); + Glib::ustring normalName=inProfile; + if (!inProfile.compare (0, 5, "file:")) normalName=inProfile.substr(5); + + if (dcpStore->isValidDCPFileName(normalName)) *dcpProf=dcpStore->getProfile(normalName); + if (*dcpProf==NULL) in = iccStore->getProfile (inProfile); } // "in" might be NULL because of "not found". That's ok, we take the cam profile then @@ -2561,7 +2580,7 @@ void RawImageSource::transformPosition (int x, int y, int tran, int& ttx, int& t //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -void RawImageSource::inverse33 (double (*rgb_cam)[3], double (*cam_rgb)[3]) { +void RawImageSource::inverse33 (const double (*rgb_cam)[3], double (*cam_rgb)[3]) { double nom = (rgb_cam[0][2]*rgb_cam[1][1]*rgb_cam[2][0] - rgb_cam[0][1]*rgb_cam[1][2]*rgb_cam[2][0] - rgb_cam[0][2]*rgb_cam[1][0]*rgb_cam[2][1] + rgb_cam[0][0]*rgb_cam[1][2]*rgb_cam[2][1] + rgb_cam[0][1]*rgb_cam[1][0]*rgb_cam[2][2] - rgb_cam[0][0]*rgb_cam[1][1]*rgb_cam[2][2] ); diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index 57b9d3a6a..764651883 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -21,6 +21,7 @@ #include "imagesource.h" #include +#include "dcp.h" #include "array2D.h" #include "curves.h" #include "../rtgui/cacheimagedata.h" @@ -60,7 +61,7 @@ class RawImageSource : public ImageSource { private: static LUTf invGrad; // for fast_demosaic static LUTf initInvGrad (); - static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, cmsHPROFILE& in); + static bool findInputProfile(Glib::ustring inProfile, cmsHPROFILE embedded, std::string camName, DCPProfile **dcpProf, cmsHPROFILE& in); protected: Glib::Mutex getImageMutex; // locks getImage @@ -167,7 +168,7 @@ class RawImageSource : public ImageSource { static void colorSpaceConversion16 (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName, double& defgain); static void colorSpaceConversion (Imagefloat* im, ColorManagementParams cmp, cmsHPROFILE embedded, cmsHPROFILE camprofile, double cam[3][3], std::string camName, double& defgain); - static void inverse33 (double (*coeff)[3], double (*icoeff)[3]); + static void inverse33 (const double (*coeff)[3], double (*icoeff)[3]); void boxblur2(float** src, float** dst, int H, int W, int box ); void boxblur_resamp(float **src, float **dst, float & max, int H, int W, int box, int samp ); diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index bc8c7e095..96e5a90db 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "rtexif.h" @@ -45,7 +46,7 @@ TagDirectory::TagDirectory () TagDirectory::TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border) : attribs(ta), order(border), parent(p) {} -TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border) { +TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored) { attribs = ta; order = border; @@ -66,6 +67,7 @@ TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* continue; } + if (skipIgnored) { int id = newTag->getID(); // detect and possibly ignore tags of directories belonging to the embedded thumbnail image @@ -78,6 +80,7 @@ TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* delete newTag; else addTag (newTag); + } else addTag (newTag); } } @@ -759,7 +762,7 @@ int Tag::toInt (int ofs, TagType astype) { case LONG: return (int)sget4 (value+ofs, getOrder()); case SRATIONAL: case RATIONAL: a = (int)sget4 (value+ofs+4, getOrder()); return a==0 ? 0 : (int)sget4 (value+ofs, getOrder()) / a; - case FLOAT: return (int)((float) sget4 (value+ofs, getOrder())); + case FLOAT: return (int)toDouble(ofs); case UNDEFINED: return 0; default: return 0; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR) } @@ -767,6 +770,7 @@ int Tag::toInt (int ofs, TagType astype) { } double Tag::toDouble (int ofs) { + union IntFloat { uint32_t i; float f; } conv; double ud, dd; switch (type) { @@ -778,7 +782,10 @@ double Tag::toDouble (int ofs) { case LONG: return (double)((int)sget4 (value+ofs, getOrder())); case SRATIONAL: case RATIONAL: ud = (int)sget4 (value+ofs, getOrder()); dd = (int)sget4 (value+ofs+4, getOrder()); return dd==0. ? 0. : (double)ud / (double)dd; - case FLOAT: return (float) sget4 (value+ofs, getOrder()); + case FLOAT: + conv.i=sget4 (value+ofs, getOrder()); + return conv.f; // IEEE FLOATs are already C format, they just need a recast + case UNDEFINED: return 0.; default: return 0.; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR) } @@ -1302,7 +1309,7 @@ void ExifManager::parseCIFF (FILE* f, int base, int length, TagDirectory* root) } } -TagDirectory* ExifManager::parse (FILE* f, int base) { +TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) { setlocale(LC_NUMERIC, "C"); // to set decimal point in sscanf // read tiff header fseek (f, base, SEEK_SET); @@ -1316,7 +1323,7 @@ TagDirectory* ExifManager::parse (FILE* f, int base) { fseek (f, base+firstifd, SEEK_SET); // first read the IFD directory - TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order); + TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order, skipIgnored); // fix ISO issue with nikon and panasonic cameras Tag* exif = root->getTag ("Exif"); @@ -1373,9 +1380,9 @@ TagDirectory* ExifManager::parseJPEG (FILE* f) { return NULL; } -TagDirectory* ExifManager::parseTIFF (FILE* f) { +TagDirectory* ExifManager::parseTIFF (FILE* f, bool skipIgnored) { - return parse (f, 0); + return parse (f, 0, skipIgnored); } std::vector ExifManager::defTags; diff --git a/rtexif/rtexif.h b/rtexif/rtexif.h index 58c1bbc8c..d77f4ec0d 100644 --- a/rtexif/rtexif.h +++ b/rtexif/rtexif.h @@ -69,7 +69,7 @@ class TagDirectory { public: TagDirectory (); - TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border); + TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored=true); TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border); virtual ~TagDirectory (); @@ -197,9 +197,9 @@ class ExifManager { static Tag* saveCIFFMNTag (FILE* f, TagDirectory* root, int len, const char* name); public: - static TagDirectory* parse (FILE*f, int base); + static TagDirectory* parse (FILE*f, int base, bool skipIgnored=true); static TagDirectory* parseJPEG (FILE*f); - static TagDirectory* parseTIFF (FILE*f); + static TagDirectory* parseTIFF (FILE*f, bool skipIgnored=true); static TagDirectory* parseCIFF (FILE* f, int base, int length); static void parseCIFF (FILE* f, int base, int length, TagDirectory* root); diff --git a/rtgui/icmpanel.cc b/rtgui/icmpanel.cc index a8a651c02..c4a77825f 100644 --- a/rtgui/icmpanel.cc +++ b/rtgui/icmpanel.cc @@ -22,6 +22,7 @@ #include "guiutils.h" #include "../rtengine/safegtk.h" #include "../rtengine/iccstore.h" +#include "../rtengine/dcp.h" #include "rtimage.h" using namespace rtengine; @@ -160,6 +161,8 @@ ICMPanel::ICMPanel () : Gtk::VBox(), FoldableToolPanel(this), iunchanged(NULL), Gtk::FileFilter filter_icc; filter_icc.set_name(M("TP_ICM_FILEDLGFILTERICM")); + filter_icc.add_pattern("*.dcp"); + filter_icc.add_pattern("*.DCP"); filter_icc.add_pattern("*.icc"); filter_icc.add_pattern("*.icm"); filter_icc.add_pattern("*.ICC"); @@ -437,7 +440,7 @@ void ICMPanel::setRawMeta (bool raw, const rtengine::ImageData* pMeta) { icamera->set_active (raw); iembedded->set_active (!raw); icamera->set_sensitive (raw); - icameraICC->set_sensitive (raw && iccStore->getStdProfile(pMeta->getCamera()) != NULL); + icameraICC->set_sensitive (raw && (iccStore->getStdProfile(pMeta->getCamera()) != NULL || dcpStore->getStdProfile(pMeta->getCamera()) != NULL)); iembedded->set_sensitive (!raw); enableListener ();