diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 279c5e423..860e99fc7 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -597,6 +597,7 @@ MAIN_TAB_EXPORT;Export Rapide MAIN_TAB_EXPOSURE;Exposition MAIN_TAB_EXPOSURE_TOOLTIP;Raccourci:Alt-e MAIN_TAB_FILTER; Filtrer +MAIN_TAB_INSPECT; Inspecter MAIN_TAB_IPTC;IPTC MAIN_TAB_METADATA;Métadonnées MAIN_TAB_METADATA_TOOLTIP;Raccourci:Alt-m @@ -796,6 +797,9 @@ PREFERENCES_INTENT_RELATIVE;Colorimétrie relative PREFERENCES_INTENT_SATURATION;Saturation PREFERENCES_INTERNALTHUMBIFUNTOUCHED;Afficher vignette incluse dans fichier RAW si non édité PREFERENCES_LANGAUTODETECT;Utiliser les paramètres linguistiques de l'OS +PREFERENCES_INSPECT_LABEL;Inspecter +PREFERENCES_INSPECT_MAXBUFFERS_LABEL;Nombre maxi de mémoire tampon +PREFERENCES_INSPECT_MAXBUFFERS_TOOLTIP;Règle le nombre maximum d'images mis en cache lors de leur survol dans le Navigateur de Fichier ; les systèmes avec peu de mémoire RAM (2 Go) devraient garder cette valeur à 1 ou 2. PREFERENCES_MENUGROUPEXTPROGS;Groupe "Ouvrir avec" PREFERENCES_MENUGROUPFILEOPERATIONS;Opérations sur les fichiers PREFERENCES_MENUGROUPLABEL;Label diff --git a/rtdata/languages/default b/rtdata/languages/default index 7dd6b7b4b..144f44144 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -607,6 +607,7 @@ MAIN_TAB_EXPORT; Fast Export MAIN_TAB_EXPOSURE;Exposure MAIN_TAB_EXPOSURE_TOOLTIP;Shortcut: Alt-e MAIN_TAB_FILTER; Filter +MAIN_TAB_INSPECT; Inspect MAIN_TAB_IPTC;IPTC MAIN_TAB_METADATA;Metadata MAIN_TAB_METADATA_TOOLTIP;Shortcut: Alt-m @@ -816,6 +817,9 @@ PREFERENCES_LEVAUTDN;Denoising level PREFERENCES_LEVDN;Cell size PREFERENCES_LISS;Auto multi-zone smoothing PREFERENCES_MAX;Maxi (Tile) +PREFERENCES_INSPECT_LABEL;Inspect +PREFERENCES_INSPECT_MAXBUFFERS_LABEL;Maximum number of buffer +PREFERENCES_INSPECT_MAXBUFFERS_TOOLTIP;Set the maximum number of images set in cache when hovering them in the File Browser ; systems with few RAM memory (2 Gb) should keep this value set to 1 ot 2. PREFERENCES_MED;Medium (Tile/2) PREFERENCES_MENUGROUPEXTPROGS;Group "Open with" PREFERENCES_MENUGROUPFILEOPERATIONS;Group "File operations" diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index b2699a9ef..72a238273 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -16,7 +16,7 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc imagedimensions.cc jpeg_memsrc.cc jdatasrc.cc iimage.cc EdgePreservingDecomposition.cc cplx_wavelet_dec.cc FTblockDN.cc - PF_correct_RT.cc + PF_correct_RT.cc previewimage.cc dirpyr_equalizer.cc calc_distort.cc lcp.cc dcp.cc cJSON.c camconst.cc diff --git a/rtengine/color.cc b/rtengine/color.cc index 3203642e2..c0e6bdb0f 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -126,7 +126,7 @@ namespace rtengine { int maxindex = 65536; cachef(maxindex,LUT_CLIP_BELOW); - gamma2curve(maxindex,0); + gamma2curve(maxindex,LUT_CLIP_BELOW|LUT_CLIP_ABOVE); for (int i=0; ieps_max) { diff --git a/rtengine/iimage.h b/rtengine/iimage.h index c460b00ee..562aead75 100644 --- a/rtengine/iimage.h +++ b/rtengine/iimage.h @@ -93,23 +93,23 @@ namespace rtengine { }; template <> - inline void ImageDatas::convertTo (unsigned short srcValue, unsigned char &dstValue) { + inline void ImageDatas::convertTo (const unsigned short srcValue, unsigned char &dstValue) { dstValue = (unsigned char)(srcValue >> 8); } template <> - inline void ImageDatas::convertTo (unsigned char srcValue, int &dstValue) { + inline void ImageDatas::convertTo (const unsigned char srcValue, int &dstValue) { dstValue = (int)(srcValue) << 8; } template <> - inline void ImageDatas::convertTo (unsigned char srcValue, unsigned short &dstValue) { + inline void ImageDatas::convertTo (const unsigned char srcValue, unsigned short &dstValue) { dstValue = (unsigned short)(srcValue) << 8; } template <> - inline void ImageDatas::convertTo (float srcValue, unsigned char &dstValue) { + inline void ImageDatas::convertTo (const float srcValue, unsigned char &dstValue) { dstValue = (unsigned char)( (unsigned short)(srcValue) >> 8 ); } template <> - inline void ImageDatas::convertTo (unsigned char srcValue, float &dstValue) { + inline void ImageDatas::convertTo (const unsigned char srcValue, float &dstValue) { dstValue = float( (unsigned short)(srcValue) << 8 ); } @@ -350,7 +350,15 @@ namespace rtengine { template void resizeImgTo (int nw, int nh, TypeInterpolation interp, PlanarWhateverData *imgPtr) { //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height); - if (interp == TI_Nearest) { + if (width==nw && height==nh) { + // special case where no resizing is necessary, just type conversion.... + for (int i=0; iv(i,j)); + } + } + } + else if (interp == TI_Nearest) { for (int i=0; i void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) { //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height); - if (interp == TI_Nearest) { + if (width==nw && height==nh) { + // special case where no resizing is necessary, just type conversion.... + for (int i=0; ir(i,j)); + convertTo(g(i,j), imgPtr->g(i,j)); + convertTo(b(i,j), imgPtr->b(i,j)); + } + } + } + else if (interp == TI_Nearest) { for (int i=0; i void resizeImgTo (int nw, int nh, TypeInterpolation interp, IC *imgPtr) { //printf("resizeImgTo: resizing %s image data (%d x %d) to %s (%d x %d)\n", getType(), width, height, imgPtr->getType(), imgPtr->width, imgPtr->height); - if (interp == TI_Nearest) { + if (width==nw && height==nh) { + // special case where no resizing is necessary, just type conversion.... + for (int i=0; ir(i,j)); + convertTo(g(i,j), imgPtr->g(i,j)); + convertTo(b(i,j), imgPtr->b(i,j)); + } + } + } + else if (interp == TI_Nearest) { for (int i=0; i + * + * 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 "previewimage.h" +#include "iimage.h" +#include "utils.h" +#include "iimage.h" +#include "rtthumbnail.h" +#include "rawimagesource.h" +#include "StopWatch.h" + +using namespace rtengine; +using namespace procparams; + +PreviewImage::PreviewImage (const Glib::ustring &fname, const Glib::ustring &ext, const PreviewImageMode mode) { + rtengine::Thumbnail* tpp=NULL; + + if (mode==PIM_EmbeddedPreviewOnly || mode==PIM_EmbeddedOrRaw) { + + const unsigned char *data = NULL; + + int width=-1, height=-1; + if (ext.lowercase()=="jpg" || ext.lowercase()=="jpeg") { + // int deg = infoFromImage (fname); + tpp = rtengine::Thumbnail::loadFromImage (fname, width, height, 1, 1., true); + if (tpp) + data = tpp->getImage8Data(); + } + else if (ext.lowercase()=="png") { + tpp = rtengine::Thumbnail::loadFromImage (fname, width, height, 1, 1., true); + if (tpp) + data = tpp->getImage8Data(); + } + else if (ext.lowercase()=="tif" || ext.lowercase()=="tiff") { + // int deg = infoFromImage (fname); + tpp = rtengine::Thumbnail::loadFromImage (fname, width, height, 1, 1., true); + if (tpp) + data = tpp->getImage8Data(); + } + else { + rtengine::RawMetaDataLocation ri; + tpp = rtengine::Thumbnail::loadQuickFromRaw (fname, ri, width, height, 1, true, true); + if (tpp) + data = tpp->getImage8Data(); + } + + if (tpp) { + if (data) { + int w, h; + double scale = 1.; + if (tpp) + tpp->getDimensions(w, h, scale); + previewImage = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24, w, h); + previewImage->flush(); + +#pragma omp parallel +{ + const unsigned char *src; + unsigned char *dst; +#pragma omp for schedule(static,10) + for (unsigned int i=0; i<(unsigned int)(h); i++) { + src = data + i*w*3; + dst = previewImage->get_data() + i*w*4; + for (unsigned int j=0; j<(unsigned int)(w); j++) { + unsigned char r = *(src++); + unsigned char g = *(src++); + unsigned char b = *(src++); + +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + *(dst++) = b; + *(dst++) = g; + *(dst++) = r; + *(dst++) = 0; +#else + *(dst++) = 0; + *(dst++) = r; + *(dst++) = g; + *(dst++) = b; +#endif + } + } +} + previewImage->mark_dirty(); + } + } + } + + if ((mode==PIM_EmbeddedOrRaw && !tpp) || mode==PIM_ForceRaw) { + RawImageSource rawImage; + int error = rawImage.load(fname, true); + if (!error) { + rtengine::Image8 *output = NULL; + const unsigned char *data = NULL; + int fw, fh; + procparams::ProcParams params; + /*rtengine::RAWParams raw; + rtengine::LensProfParams lensProf; + rtengine::procparams::ToneCurveParams toneCurve; + rtengine::procparams::ColorManagementParams icm; + rtengine::CoarseTransformParams coarse;*/ + ColorTemp wb = rawImage.getWB (); + rawImage.getFullSize (fw, fh, TR_NONE); + PreviewProps pp (0, 0, fw, fh, 1); + params.icm.input = Glib::ustring("(embedded)"); + params.raw.bayersensor.method = RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::fast]; + params.raw.deadPixelFilter = false; + params.raw.ca_autocorrect = false; + params.raw.xtranssensor.method = RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::fast]; + rawImage.preprocess(params.raw, params.lensProf, params.coarse); + rawImage.demosaic(params.raw); + Imagefloat* image = new rtengine::Imagefloat (fw, fh); + rawImage.getImage (wb, TR_NONE, image, pp, params.toneCurve, params.icm, params.raw); + output = new Image8(fw, fh); + rawImage.convertColorSpace(image, params.icm, wb, params.raw); + StopWatch Stop1("inspector loop"); +#pragma omp parallel for schedule(dynamic, 10) + for (int i=0; ir(i,j) = Color::gamma2curve[image->r(i,j)]; + image->g(i,j) = Color::gamma2curve[image->g(i,j)]; + image->b(i,j) = Color::gamma2curve[image->b(i,j)]; + } + Stop1.stop(); + + image->resizeImgTo(fw, fh, TI_Nearest, output); + data = output->getData(); + + + if (data) { + int w, h; + double scale = 1.; + w = output->getWidth(); + h = output->getHeight(); + previewImage = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24, w, h); + previewImage->flush(); + +#pragma omp parallel +{ + const unsigned char *src; + unsigned char *dst; +#pragma omp for schedule(static,10) + for (unsigned int i=0; i<(unsigned int)(h); i++) { + src = data + i*w*3; + dst = previewImage->get_data() + i*w*4; + for (unsigned int j=0; j<(unsigned int)(w); j++) { + unsigned char r = *(src++); + unsigned char g = *(src++); + unsigned char b = *(src++); +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + *(dst++) = b; + *(dst++) = g; + *(dst++) = r; + *(dst++) = 0; +#else + *(dst++) = 0; + *(dst++) = r; + *(dst++) = g; + *(dst++) = b; +#endif + } + } +} + if (output) + delete output; + previewImage->mark_dirty(); + } + } + } + + if (tpp) + delete tpp; + +} + +Cairo::RefPtr PreviewImage::getImage() { + return previewImage; +} diff --git a/rtengine/previewimage.h b/rtengine/previewimage.h new file mode 100644 index 000000000..6f78dc960 --- /dev/null +++ b/rtengine/previewimage.h @@ -0,0 +1,54 @@ +/* + * 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 _PREVIEWIMAGE_ +#define _PREVIEWIMAGE_ + +#include +#include "cairomm/cairomm.h" + +namespace rtengine { + +/** @brief Get a quick preview image out of a raw or standard file + * + * This class reads the full size preview image (at least the biggest one available) from the raw file, + * or the fast demosaiced version if no suitable embedded preview is found. + * + * For standard image, it simply read it with fast conversion for 32 bits images + */ +class PreviewImage { + +private: + Cairo::RefPtr previewImage; + +public: + typedef enum mode { + PIM_EmbeddedPreviewOnly, /// Get the embedded image only, fail if doesn't exist + PIM_EmbeddedOrRaw, /// Get the embedded image if it exist, or use the raw file otherwise + PIM_ForceRaw /// Get a preview of the raw file, even if an embedded image exist + } PreviewImageMode; + + PreviewImage (const Glib::ustring &fname, const Glib::ustring &ext, const PreviewImageMode mode); + + Cairo::RefPtr getImage(); + +}; + +} + +#endif diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 145ea04b3..e47c4c8e2 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -112,7 +112,7 @@ void CropParams::mapToResized(int resizedWidth, int resizedHeight, int scale, in } ColorToningParams::ColorToningParams () : hlColSat(60, 80, false), shadowsColSat(80, 208, false) { - setDefault(); + setDefaults(); } void ColorToningParams::getDefaultColorCurve(std::vector &curve) { @@ -158,7 +158,7 @@ void ColorToningParams::getDefaultCL2Curve(std::vector &curve) { curve.at(i) = v[i-1]; } -void ColorToningParams::setDefault() { +void ColorToningParams::setDefaults() { enabled = false; autosat=true; method = "Lab"; @@ -416,7 +416,7 @@ void ColorToningParams::getCurves(ColorGradientCurve &colorCurveLUT, OpacityCurv DirPyrDenoiseParams::DirPyrDenoiseParams () { - setDefault (); + setDefaults (); } void DirPyrDenoiseParams::getDefaultNoisCurve(std::vector &curve) { @@ -433,7 +433,7 @@ void DirPyrDenoiseParams::getDefaultCCCurve(std::vector &curve) { // 0.60, 0.05,0.35,0.35}; double v[8]= { 0.05, 0.50,0.35,0.35, 0.35, 0.05,0.35,0.35}; - + curve.resize(9); curve.at(0 ) = double(FCT_MinMaxCPoints); for (size_t i=1; i &curve) { } -void DirPyrDenoiseParams::setDefault() { +void DirPyrDenoiseParams::setDefaults() { - getDefaultNoisCurve(lcurve); - getDefaultCCCurve(cccurve); - - enabled = false; - enhance = false; - median = false; - autochroma = false; - luma = 0; - passes = 1; - dmethod = "Lab"; - Lmethod = "CUR"; - Cmethod = "MAN"; - C2method = "AUTO"; - smethod = "shal"; - medmethod = "soft"; - methodmed = "none"; - rgbmethod = "soft"; - Ldetail = 50; - chroma = 15; - redchro = 0; - bluechro = 0; - gamma = 1.7; + getDefaultNoisCurve(lcurve); + getDefaultCCCurve(cccurve); + + enabled = false; + enhance = false; + median = false; + autochroma = false; + luma = 0; + passes = 1; + dmethod = "Lab"; + Lmethod = "CUR"; + Cmethod = "MAN"; + C2method = "AUTO"; + smethod = "shal"; + medmethod = "soft"; + methodmed = "none"; + rgbmethod = "soft"; + Ldetail = 50; + chroma = 15; + redchro = 0; + bluechro = 0; + gamma = 1.7; } void DirPyrDenoiseParams::getCurves(NoiseCurve &lCurve, NoiseCurve &cCurve) const { - lCurve.Set(this->lcurve); - cCurve.Set(this->cccurve); + lCurve.Set(this->lcurve); + cCurve.Set(this->cccurve); } +void ToneCurveParams::setDefaults() { + autoexp = false; + clip = 0.02; + expcomp = 0; + brightness = 0; + contrast = 0; + saturation = 0; + black = 0; + hlcompr = 0; + hlcomprthresh = 33; + shcompr = 50; + curve.clear (); + curve.push_back(DCT_Linear); + curve2.clear (); + curve2.push_back(DCT_Linear); + curveMode = ToneCurveParams::TC_MODE_STD; + curveMode2 = ToneCurveParams::TC_MODE_STD; + hrenabled = false; + method = "Blend"; +} +void LensProfParams::setDefaults() { + lcpFile=""; + useDist=useVign=true; + useCA=false; +} + +void CoarseTransformParams::setDefaults() { + rotate = 0; + hflip = false; + vflip = false; +} + +void RAWParams::setDefaults() { + bayersensor.method = RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze]; + bayersensor.ccSteps = 0; + bayersensor.dcb_iterations = 2; + bayersensor.dcb_enhance = false; + //bayersensor.all_enhance = false; + bayersensor.lmmse_iterations = 2; + bayersensor.black0 = 0.0; + bayersensor.black1 = 0.0; + bayersensor.black2 = 0.0; + bayersensor.black3 = 0.0; + bayersensor.twogreen = true; + bayersensor.linenoise = 0; + bayersensor.greenthresh = 0; + + xtranssensor.method = RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::threePass]; + xtranssensor.ccSteps = 0; + xtranssensor.blackred = 0.0; + xtranssensor.blackgreen = 0.0; + xtranssensor.blackblue = 0.0; + + expos=1.0; + preser=0.0; + df_autoselect = false; + ff_AutoSelect = false; + ff_BlurRadius = 32; + ff_BlurType = RAWParams::ff_BlurTypestring[RAWParams::area_ff]; + ff_AutoClipControl = false; + ff_clipControl = 0; + cared = 0; + cablue = 0; + ca_autocorrect = false; + hotPixelFilter = false; + deadPixelFilter = false; + hotdeadpix_thresh = 40; +} + +void ColorManagementParams::setDefaults() { + input = "(cameraICC)"; + blendCMSMatrix = false; + toneCurve = false; + dcpIlluminant = 0; + working = "ProPhoto"; + output = "RT_sRGB"; + gamma = "default"; + gampos =2.22; + slpos=4.5; + freegamma = false; +} ProcParams::ProcParams () { @@ -501,30 +582,13 @@ void ProcParams::destroy (ProcParams* pp) { void ProcParams::setDefaults () { - toneCurve.autoexp = false; - toneCurve.clip = 0.02; - toneCurve.expcomp = 0; - toneCurve.brightness = 0; - toneCurve.contrast = 0; - toneCurve.saturation = 0; - toneCurve.black = 0; - toneCurve.hlcompr = 0; - toneCurve.hlcomprthresh = 33; - toneCurve.shcompr = 50; - toneCurve.curve.clear (); - toneCurve.curve.push_back(DCT_Linear); - toneCurve.curve2.clear (); - toneCurve.curve2.push_back(DCT_Linear); - toneCurve.curveMode = ToneCurveParams::TC_MODE_STD; - toneCurve.curveMode2 = ToneCurveParams::TC_MODE_STD; - toneCurve.hrenabled = false; - toneCurve.method = "Blend"; + toneCurve.setDefaults(); + labCurve.brightness = 0; labCurve.contrast = 0; labCurve.chromaticity = 0; labCurve.avoidcolorshift = false; labCurve.lcredsk = true; - labCurve.rstprotection = 0; labCurve.lcurve.clear (); labCurve.lcurve.push_back(DCT_Linear); @@ -554,7 +618,7 @@ void ProcParams::setDefaults () { rgbCurves.bcurve.clear (); rgbCurves.bcurve.push_back(DCT_Linear); - colorToning.setDefault(); + colorToning.setDefaults(); sharpenEdge.enabled = false; sharpenEdge.passes = 2; @@ -662,26 +726,9 @@ void ProcParams::setDefaults () { defringe.huecurve.at(22) = 0.; defringe.huecurve.at(23) = 0.35; defringe.huecurve.at(24) = 0.35; -/* - dirpyrDenoise.enabled = false; - dirpyrDenoise.enhance = false; - // dirpyrDenoise.perform = false; - dirpyrDenoise.median = true; - dirpyrDenoise.luma = 0; - dirpyrDenoise.lcurve.clear (); - dirpyrDenoise.lcurve.push_back(DCT_Linear); - dirpyrDenoise.Ldetail = 50; - dirpyrDenoise.chroma = 15; - dirpyrDenoise.redchro = 0; - dirpyrDenoise.bluechro = 0; - dirpyrDenoise.gamma = 1.7; - dirpyrDenoise.passes = 1; - dirpyrDenoise.dmethod = "RGB"; - dirpyrDenoise.smethod = "shal"; - dirpyrDenoise.medmethod = "soft"; - dirpyrDenoise.methodmed = "none"; - dirpyrDenoise.rgbmethod = "soft"; -*/ + + dirpyrDenoise.setDefaults(); + epd.enabled = false; epd.strength = 0.25; epd.edgeStopping = 1.4; @@ -707,9 +754,7 @@ void ProcParams::setDefaults () { crop.orientation= "Landscape"; crop.guide = "Rule of thirds"; - coarse.rotate = 0; - coarse.hflip = false; - coarse.vflip = false; + coarse.setDefaults(); commonTrans.autofill = true; @@ -742,9 +787,7 @@ void ProcParams::setDefaults () { vignetting.centerX = 0; vignetting.centerY = 0; - lensProf.lcpFile=""; - lensProf.useDist=lensProf.useVign=true; - lensProf.useCA=false; + lensProf.setDefaults(); chmixer.red[0] = 100; chmixer.red[1] = 0; @@ -791,16 +834,7 @@ void ProcParams::setDefaults () { resize.width = 900; resize.height = 900; - icm.input = "(cameraICC)"; - icm.blendCMSMatrix = false; - icm.toneCurve = false; - icm.dcpIlluminant = 0; - icm.working = "ProPhoto"; - icm.output = "RT_sRGB"; - icm.gamma = "default"; - icm.gampos =2.22; - icm.slpos=4.5; - icm.freegamma = false; + icm.setDefaults(); dirpyrequalizer.enabled = false; dirpyrequalizer.gamutlab = false; @@ -822,40 +856,8 @@ void ProcParams::setDefaults () { filmSimulation.setDefaults(); - raw.bayersensor.method = RAWParams::BayerSensor::methodstring[RAWParams::BayerSensor::amaze]; - raw.bayersensor.ccSteps = 0; - raw.bayersensor.dcb_iterations = 2; - raw.bayersensor.dcb_enhance = false; - //raw.bayersensor.all_enhance = false; - raw.bayersensor.lmmse_iterations = 2; - raw.bayersensor.black0 = 0.0; - raw.bayersensor.black1 = 0.0; - raw.bayersensor.black2 = 0.0; - raw.bayersensor.black3 = 0.0; - raw.bayersensor.twogreen = true; - raw.bayersensor.linenoise = 0; - raw.bayersensor.greenthresh = 0; + raw.setDefaults(); - raw.xtranssensor.method = RAWParams::XTransSensor::methodstring[RAWParams::XTransSensor::threePass]; - raw.xtranssensor.ccSteps = 0; - raw.xtranssensor.blackred = 0.0; - raw.xtranssensor.blackgreen = 0.0; - raw.xtranssensor.blackblue = 0.0; - - raw.expos=1.0; - raw.preser=0.0; - raw.df_autoselect = false; - raw.ff_AutoSelect = false; - raw.ff_BlurRadius = 32; - raw.ff_BlurType = RAWParams::ff_BlurTypestring[RAWParams::area_ff]; - raw.ff_AutoClipControl = false; - raw.ff_clipControl = 0; - raw.cared = 0; - raw.cablue = 0; - raw.ca_autocorrect = false; - raw.hotPixelFilter = false; - raw.deadPixelFilter = false; - raw.hotdeadpix_thresh = 40; exif.clear (); iptc.clear (); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 2105d8a23..95751ffc4 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -211,6 +211,10 @@ class ToneCurveParams { int hlcompr; // Highlight Recovery's compression int hlcomprthresh; // Highlight Recovery's threshold + ToneCurveParams () { + setDefaults(); + } + void setDefaults(); static bool HLReconstructionNecessary(LUTu &histRedRaw, LUTu &histGreenRaw, LUTu &histBlueRaw); }; @@ -301,7 +305,7 @@ class ColorToningParams { bool lumamode; ColorToningParams (); - void setDefault(); // SHOULD BE GENERALIZED TO ALL CLASSES! + void setDefaults(); // SHOULD BE GENERALIZED TO ALL CLASSES! /// @brief Transform the mixer values to their curve equivalences void mixerToCurve(std::vector &colorCurve, std::vector &opacityCurve) const; /// @brief Specifically transform the sliders values to their curve equivalences @@ -568,7 +572,7 @@ class DirPyrDenoiseParams { int passes; DirPyrDenoiseParams (); - void setDefault(); // SHOULD BE GENERALIZED TO ALL CLASSES! + void setDefaults(); // SHOULD BE GENERALIZED TO ALL CLASSES! void getCurves(NoiseCurve &lCurve, NoiseCurve &cCurve) const; static void getDefaultNoisCurve(std::vector &curve); @@ -631,6 +635,11 @@ class CoarseTransformParams { int rotate; bool hflip; bool vflip; + + CoarseTransformParams() { + setDefaults(); + } + void setDefaults(); }; /** @@ -666,6 +675,11 @@ class LensProfParams { public: Glib::ustring lcpFile; bool useDist, useVign, useCA; + + LensProfParams() { + setDefaults(); + } + void setDefaults(); }; /** @@ -818,6 +832,11 @@ class ColorManagementParams { double gampos; double slpos; bool freegamma; + + ColorManagementParams() { + setDefaults(); + } + void setDefaults(); }; /** @@ -956,6 +975,11 @@ class RAWParams { bool hotPixelFilter; bool deadPixelFilter; int hotdeadpix_thresh; + + RAWParams() { + setDefaults(); + } + void setDefaults(); }; /** diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 2c5fd2c85..8f178ee73 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -46,7 +46,7 @@ extern Options options; namespace rtengine { using namespace procparams; -Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq) { +Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq, bool inspectorMode) { StdImageSource imgSrc; if (imgSrc.load(fname)) { @@ -73,60 +73,88 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, 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; + if (inspectorMode) { + // Special case, meaning that we want a full sized thumbnail image (e.g. for the Inspector feature) + w = img->width; + h = img->height; + tpp->scale = 1.; } else { - h = w * img->height / img->width; - tpp->scale = (double)img->width / w; + 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; + } } - // bilinear interpolation - if (tpp->thumbImg) delete tpp->thumbImg; tpp->thumbImg = NULL; - tpp->thumbImg = resizeToSameType(w, h, TI_Bilinear, img); - - // histogram computation - tpp->aeHistCompression = 3; - tpp->aeHistogram(65536>>tpp->aeHistCompression); - - double avg_r = 0; - double avg_g = 0; - double avg_b = 0; - int n = 0; - - if (img->getType() == rtengine::sImage8) { - Image8 *image = static_cast(img); - image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression); + if (tpp->thumbImg) { + delete tpp->thumbImg; + tpp->thumbImg = NULL; } - else if (img->getType() == sImage16) { - Image16 *image = static_cast(img); - image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression); - } - else if (img->getType() == sImagefloat) { - Imagefloat *image = static_cast(img); - image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression); + if (inspectorMode) { + // we want an Image8 + if (img->getType() == rtengine::sImage8) { + // copy the image + Image8 *srcImg = static_cast(img); + Image8 *thImg = new Image8 (w, h); + srcImg->copyData(thImg); + tpp->thumbImg = thImg; + } + else { + // copy the image with a conversion + tpp->thumbImg = resizeTo(w, h, TI_Bilinear, img); + } } else { - printf("loadFromImage: Unsupported image type \"%s\"!\n", img->getType()); + // we want the same image type than the source file + tpp->thumbImg = resizeToSameType(w, h, TI_Bilinear, img); + + // histogram computation + tpp->aeHistCompression = 3; + tpp->aeHistogram(65536>>tpp->aeHistCompression); + + double avg_r = 0; + double avg_g = 0; + double avg_b = 0; + int n = 0; + + if (img->getType() == rtengine::sImage8) { + Image8 *image = static_cast(img); + image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression); + } + else if (img->getType() == sImage16) { + Image16 *image = static_cast(img); + image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression); + } + else if (img->getType() == sImagefloat) { + Imagefloat *image = static_cast(img); + image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression); + } + else { + printf("loadFromImage: Unsupported image type \"%s\"!\n", img->getType()); + } + + if (n>0) { + ColorTemp cTemp; + + tpp->redAWBMul = avg_r/double(n); + tpp->greenAWBMul = avg_g/double(n); + tpp->blueAWBMul = avg_b/double(n); + tpp->wbEqual = wbEq; + + cTemp.mul2temp (tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen); + } + + tpp->init (); } - if (n>0) { - ColorTemp cTemp; - - tpp->redAWBMul = avg_r/double(n); - tpp->greenAWBMul = avg_g/double(n); - tpp->blueAWBMul = avg_b/double(n); - tpp->wbEqual = wbEq; - - cTemp.mul2temp (tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen); - } - - tpp->init (); return tpp; } -Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate) +Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode) { RawImage *ri= new RawImage(fname); int r = ri->loadRaw(false,false); @@ -188,18 +216,34 @@ Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataL 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; + if (inspectorMode) { + // Special case, meaning that we want a full sized thumbnail image (e.g. for the Inspector feature) + w = img->width; + h = img->height; + tpp->scale = 1.; } else { - h = w * img->height / img->width; - tpp->scale = (double)img->width / w; + 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; + } } - if (tpp->thumbImg) delete tpp->thumbImg; tpp->thumbImg = NULL; - tpp->thumbImg = resizeTo(w, h, TI_Nearest, img); - delete img; + if (tpp->thumbImg) { + delete tpp->thumbImg; + tpp->thumbImg = NULL; + } + if (inspectorMode) { + tpp->thumbImg = img; + } + else { + tpp->thumbImg = resizeTo(w, h, TI_Nearest, img); + delete img; + } if (rotate && ri->get_rotateDegree() > 0) { std::string fname = ri->get_filename(); @@ -214,7 +258,9 @@ Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataL } } - tpp->init (); + if (!inspectorMode) + tpp->init (); + delete ri; return tpp; @@ -1614,4 +1660,14 @@ bool Thumbnail::writeAEHistogram (const Glib::ustring& fname) { return false; } +unsigned char* Thumbnail::getImage8Data() { + if (thumbImg && thumbImg->getType()==rtengine::sImage8) { + Image8* img8 = static_cast(thumbImg); + return img8->data; + } + return NULL; +} + + + } diff --git a/rtengine/rtthumbnail.h b/rtengine/rtthumbnail.h index 669b33083..393241eb7 100644 --- a/rtengine/rtthumbnail.h +++ b/rtengine/rtthumbnail.h @@ -80,9 +80,9 @@ namespace rtengine { int getImageWidth (const procparams::ProcParams& pparams, int rheight, float &ratio); void getDimensions (int& w, int& h, double& scaleFac); - static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate); + static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode=false); static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate); - static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq); + static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq, bool inspectorMode=false); static RawMetaDataLocation loadMetaDataFromRaw (const Glib::ustring& fname); void getCamWB (double& temp, double& green); @@ -104,6 +104,7 @@ namespace rtengine { bool readAEHistogram (const Glib::ustring& fname); bool writeAEHistogram (const Glib::ustring& fname); + unsigned char* getImage8Data(); // accessor to the 8bit image if it is one, which should be the case for the "Inspector" mode. // Hombre: ... let's hope that proper template can make this cleaner diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index fa13d78ef..e394c6626 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -14,7 +14,7 @@ set (BASESOURCEFILES crophandler.cc dirbrowser.cc curveeditor.cc curveeditorgroup.cc diagonalcurveeditorsubgroup.cc flatcurveeditorsubgroup.cc filecatalog.cc extprog.cc - previewloader.cc rtimage.cc + previewloader.cc rtimage.cc inspector.cc histogrampanel.cc history.cc imagearea.cc imageareapanel.cc iptcpanel.cc labcurve.cc main.cc multilangmgr.cc mycurve.cc myflatcurve.cc mydiagonalcurve.cc options.cc diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc index 77bd3f690..983474ae5 100644 --- a/rtgui/batchqueuepanel.cc +++ b/rtgui/batchqueuepanel.cc @@ -179,12 +179,15 @@ BatchQueuePanel::BatchQueuePanel () { } } - -void BatchQueuePanel::updateTab (int qsize) +// it is expected to have a non null forceOrientation value on Preferences update only. In this case, qsize is ingored and computed automatically +void BatchQueuePanel::updateTab (int qsize, int forceOrientation) { Gtk::Notebook *nb =(Gtk::Notebook *)(this->get_parent()); - if (options.mainNBVertical) { + if (forceOrientation > 0) + qsize = batchQueue->getEntries().size(); + + if ((forceOrientation==0 && options.mainNBVertical) || (forceOrientation==2)) { Gtk::VBox* vbb = Gtk::manage (new Gtk::VBox ()); Gtk::Label* l; diff --git a/rtgui/batchqueuepanel.h b/rtgui/batchqueuepanel.h index 1adb3c3be..033960894 100644 --- a/rtgui/batchqueuepanel.h +++ b/rtgui/batchqueuepanel.h @@ -69,7 +69,7 @@ class BatchQueuePanel : public Gtk::VBox, void pathFolderChanged (); void pathFolderButtonPressed (); void formatChanged (Glib::ustring f); - void updateTab (int qsize); + void updateTab (int qsize, int forceOrientation=0); // forceOrientation=0: base on options / 1: horizontal / 2: vertical bool handleShortcutKey (GdkEventKey* event); }; diff --git a/rtgui/coloredbar.cc b/rtgui/coloredbar.cc index 2e48034c8..de41b8312 100644 --- a/rtgui/coloredbar.cc +++ b/rtgui/coloredbar.cc @@ -26,9 +26,9 @@ ColoredBar::ColoredBar (eRTOrientation orient) { } /* - * Redraw the bar to a Cairo::Surface + * Redraw the bar to a Cairo::ImageSurface */ -void ColoredBar::expose(Cairo::RefPtr destSurface) { +void ColoredBar::expose(Cairo::RefPtr destSurface) { // look out if the Surface has to be redrawn if (!surfaceCreated() || !destSurface) return; diff --git a/rtgui/coloredbar.h b/rtgui/coloredbar.h index 657896abd..8e62e20ed 100644 --- a/rtgui/coloredbar.h +++ b/rtgui/coloredbar.h @@ -39,7 +39,7 @@ class ColoredBar : public BackBuffer, public ColorCaller { ColoredBar (eRTOrientation orient); void expose(Glib::RefPtr destWindow); - void expose(Cairo::RefPtr destSurface); + void expose(Cairo::RefPtr destSurface); void expose(BackBuffer *backBuffer); bool canGetColors(); diff --git a/rtgui/coord.h b/rtgui/coord.h new file mode 100644 index 000000000..91fc40562 --- /dev/null +++ b/rtgui/coord.h @@ -0,0 +1,163 @@ +/* + * 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 _COORD_H_ +#define _COORD_H_ + +class PolarCoord; + +// Do not confuse with rtengine::Coord2D, Coord is for the GUI +class Coord { +public: + int x; + int y; + + Coord() : x(-1), y(-1) {} + Coord(int x, int y) : x(x), y(y) {} + + void set (int x, int y) { + this->x = x; + this->y = y; + } + + void setFromPolar(PolarCoord polar); + + /// @brief Clip the coord to stay in the width x height bounds + /// @return true if the x or y coordinate has changed + bool clip(int width, int height) { + int trimmedX = rtengine::LIM(x, 0, width); + int trimmedY = rtengine::LIM(y, 0, height); + bool retval = trimmedX!=x || trimmedY!=y; + x = trimmedX; + y = trimmedY; + return retval; + } + + void operator+=(const Coord & rhs) { + x += rhs.x; + y += rhs.y; + } + void operator-=(const Coord & rhs) { + x -= rhs.x; + y -= rhs.y; + } + void operator*=(double scale) { + x *= scale; + y *= scale; + } + Coord operator+(Coord & rhs) { + Coord result(x+rhs.x, y+rhs.y); + return result; + } + Coord operator-(Coord & rhs) { + Coord result(x-rhs.x, y-rhs.y); + return result; + } + Coord operator*(double scale) { + Coord result(x*scale, y*scale); + return result; + } +}; + +class PolarCoord { +public: + double radius; + double angle; // degree + + PolarCoord() : radius(1.), angle(0.) {} + PolarCoord(double radius, double angle) : radius(radius), angle(angle) {} + + void set (double radius, double angle) { + this->radius = radius; + this->angle = angle; + } + + void setFromCartesian(Coord start, Coord end) { + Coord delta(end.x-start.x, end.y-start.y); + setFromCartesian(delta); + } + + void setFromCartesian(Coord delta) { + if (!delta.x && !delta.y) { + // null vector, we set to a default value + radius = 1.; + angle = 0.; + return; + } + double x_ = double(delta.x); + double y_ = double(delta.y); + radius = sqrt(x_*x_+y_*y_); + if (delta.x>0.) { + if (delta.y>=0.) + angle = atan(y_/x_)/(2*M_PI)*360.; + else if (delta.y<0.) + angle = (atan(y_/x_)+2*M_PI)/(2*M_PI)*360.; + } + else if (delta.x<0.) + angle = (atan(y_/x_)+M_PI)/(2*M_PI)*360.; + else if (delta.x==0.) { + if (delta.y>0.) + angle = 90.; + else + angle = 270.; + } + } + + void operator+=(const PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord += rhsCoord; + setFromCartesian(thisCoord); + } + void operator-=(const PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord -= rhsCoord; + setFromCartesian(thisCoord); + } + void operator*=(double scale) { + radius *= scale; + } + PolarCoord operator+(PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord += rhsCoord; + PolarCoord result; + result.setFromCartesian(thisCoord); + return result; + } + PolarCoord operator-(PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord -= rhsCoord; + PolarCoord result; + result.setFromCartesian(thisCoord); + return result; + } + Coord operator*(double scale) { + Coord result(radius*scale, angle); + return result; + } + +}; + +#endif diff --git a/rtgui/edit.h b/rtgui/edit.h index 37b876907..1e7159384 100644 --- a/rtgui/edit.h +++ b/rtgui/edit.h @@ -24,6 +24,7 @@ #include "editid.h" #include "cursormanager.h" #include "../rtengine/rt_math.h" +#include "coord.h" class EditDataProvider; @@ -60,146 +61,6 @@ class EditBuffer; * */ -class PolarCoord; - -// Do not confuse with rtengine::Coord2D, this one is for the GUI -class Coord { -public: - int x; - int y; - - Coord() : x(-1), y(-1) {} - Coord(int x, int y) : x(x), y(y) {} - - void set (int x, int y) { - this->x = x; - this->y = y; - } - - void setFromPolar(PolarCoord polar); - - /// @brief Clip the coord to stay in the width x height bounds - /// @return true if the x or y coordinate has changed - bool clip(int width, int height) { - int trimmedX = rtengine::LIM(x, 0, width); - int trimmedY = rtengine::LIM(y, 0, height); - bool retval = trimmedX!=x || trimmedY!=y; - x = trimmedX; - y = trimmedY; - return retval; - } - - void operator+=(const Coord & rhs) { - x += rhs.x; - y += rhs.y; - } - void operator-=(const Coord & rhs) { - x -= rhs.x; - y -= rhs.y; - } - void operator*=(double scale) { - x *= scale; - y *= scale; - } - Coord operator+(Coord & rhs) { - Coord result(x+rhs.x, y+rhs.y); - return result; - } - Coord operator-(Coord & rhs) { - Coord result(x-rhs.x, y-rhs.y); - return result; - } - Coord operator*(double scale) { - Coord result(x*scale, y*scale); - return result; - } -}; - -class PolarCoord { -public: - double radius; - double angle; // degree - - PolarCoord() : radius(1.), angle(0.) {} - PolarCoord(double radius, double angle) : radius(radius), angle(angle) {} - - void set (double radius, double angle) { - this->radius = radius; - this->angle = angle; - } - - void setFromCartesian(Coord start, Coord end) { - Coord delta(end.x-start.x, end.y-start.y); - setFromCartesian(delta); - } - - void setFromCartesian(Coord delta) { - if (!delta.x && !delta.y) { - // null vector, we set to a default value - radius = 1.; - angle = 0.; - return; - } - double x_ = double(delta.x); - double y_ = double(delta.y); - radius = sqrt(x_*x_+y_*y_); - if (delta.x>0.) { - if (delta.y>=0.) - angle = atan(y_/x_)/(2*M_PI)*360.; - else if (delta.y<0.) - angle = (atan(y_/x_)+2*M_PI)/(2*M_PI)*360.; - } - else if (delta.x<0.) - angle = (atan(y_/x_)+M_PI)/(2*M_PI)*360.; - else if (delta.x==0.) { - if (delta.y>0.) - angle = 90.; - else - angle = 270.; - } - } - - void operator+=(const PolarCoord & rhs) { - Coord thisCoord, rhsCoord; - thisCoord.setFromPolar(*this); - rhsCoord.setFromPolar(rhs); - thisCoord += rhsCoord; - setFromCartesian(thisCoord); - } - void operator-=(const PolarCoord & rhs) { - Coord thisCoord, rhsCoord; - thisCoord.setFromPolar(*this); - rhsCoord.setFromPolar(rhs); - thisCoord -= rhsCoord; - setFromCartesian(thisCoord); - } - void operator*=(double scale) { - radius *= scale; - } - PolarCoord operator+(PolarCoord & rhs) { - Coord thisCoord, rhsCoord; - thisCoord.setFromPolar(*this); - rhsCoord.setFromPolar(rhs); - thisCoord += rhsCoord; - PolarCoord result; - result.setFromCartesian(thisCoord); - return result; - } - PolarCoord operator-(PolarCoord & rhs) { - Coord thisCoord, rhsCoord; - thisCoord.setFromPolar(*this); - rhsCoord.setFromPolar(rhs); - thisCoord -= rhsCoord; - PolarCoord result; - result.setFromCartesian(thisCoord); - return result; - } - Coord operator*(double scale) { - Coord result(radius*scale, angle); - return result; - } - -}; /** @brief Coordinate system where the widgets will be drawn * diff --git a/rtgui/filebrowser.h b/rtgui/filebrowser.h index a939f38ed..7e51866b0 100644 --- a/rtgui/filebrowser.h +++ b/rtgui/filebrowser.h @@ -45,6 +45,7 @@ class FileBrowserListener { virtual void copyMoveRequested (std::vector tbe, bool moveRequested) {} virtual void selectionChanged (std::vector tbe) {} virtual void clearFromCacheRequested(std::vector tbe, bool leavenotrace) {} + virtual bool isInTabMode () { return false; } }; struct FileBrowserIdleHelper { @@ -166,6 +167,8 @@ class FileBrowser : public ThumbBrowserBase, void saveThumbnailHeight (int height); int getThumbnailHeight (); + bool isInTabMode() { return tbl ? tbl->isInTabMode() : false; } + void openNextImage (); void openPrevImage (); void copyProfile (); diff --git a/rtgui/filebrowserentry.cc b/rtgui/filebrowserentry.cc index c98c57cbe..1e3976019 100644 --- a/rtgui/filebrowserentry.cc +++ b/rtgui/filebrowserentry.cc @@ -23,6 +23,7 @@ #include "guiutils.h" #include "threadutils.h" #include "../rtengine/safegtk.h" +#include "inspector.h" #include @@ -34,7 +35,7 @@ Glib::RefPtr FileBrowserEntry::recentlySavedIcon; Glib::RefPtr FileBrowserEntry::enqueuedIcon; FileBrowserEntry::FileBrowserEntry (Thumbnail* thm, const Glib::ustring& fname) - : ThumbBrowserEntryBase (fname), iatlistener(NULL), cropgl(NULL), state(SNormal) { + : ThumbBrowserEntryBase (fname), wasInside(false), iatlistener(NULL), cropgl(NULL), state(SNormal) { thumbnail=thm; feih = new FileBrowserEntryIdleHelper; @@ -261,6 +262,25 @@ bool FileBrowserEntry::motionNotify (int x, int y) { int ix = x - startx - ofsX; int iy = y - starty - ofsY; + Inspector* inspector = parent->getInspector(); + if (inspector && inspector->isActive() && !parent->isInTabMode()) { + rtengine::Coord2D coord(-1.,-1.); + getPosInImgSpace(x, y, coord); + if (coord.x != -1.) { + if (!wasInside) { + inspector->switchImage(filename); + } + wasInside = true; + inspector->mouseMove(coord, 0); + } + else { + if (wasInside) { + wasInside = false; + rtengine::Coord2D coord(-1,-1); + } + } + } + if (inside (x,y)) updateCursor (ix, iy); diff --git a/rtgui/filebrowserentry.h b/rtgui/filebrowserentry.h index 4f5a559e5..bc52273a0 100644 --- a/rtgui/filebrowserentry.h +++ b/rtgui/filebrowserentry.h @@ -45,6 +45,7 @@ class FileBrowserEntry : public ThumbBrowserEntryBase, double scale; static bool iconsLoaded; + bool wasInside; ImageAreaToolListener* iatlistener; int press_x, press_y, action_x, action_y; double rot_deg; diff --git a/rtgui/filecatalog.h b/rtgui/filecatalog.h index 9d25c55ba..7a0558e6b 100644 --- a/rtgui/filecatalog.h +++ b/rtgui/filecatalog.h @@ -60,7 +60,7 @@ class FilePanel; class FileCatalog : public Gtk::VBox, public DirSelectionListener, public PreviewLoaderListener, - public FilterPanelListener, + public FilterPanelListener, public FileBrowserListener, public ExportPanelListener #ifdef WIN32 @@ -181,6 +181,10 @@ class FileCatalog : public Gtk::VBox, void previewsFinishedUI (); void _refreshProgressBar (); + void setInspector(Inspector* inspector) { if (fileBrowser) fileBrowser->setInspector(inspector); } + void disableInspector() { if (fileBrowser) fileBrowser->disableInspector(); } + void enableInspector() { if (fileBrowser) fileBrowser->enableInspector(); } + // filterpanel interface void exifFilterChanged (); @@ -245,6 +249,8 @@ class FileCatalog : public Gtk::VBox, bool handleShortcutKey (GdkEventKey* event); + bool isInTabMode() { return inTabMode; } + bool CheckSidePanelsVisibility(); void toggleSidePanels(); void toggleLeftPanel(); diff --git a/rtgui/filepanel.cc b/rtgui/filepanel.cc index 21118327f..22988d8e8 100644 --- a/rtgui/filepanel.cc +++ b/rtgui/filepanel.cc @@ -20,6 +20,7 @@ #include "filepanel.h" #include "rtwindow.h" #include "../rtengine/safegtk.h" +#include "inspector.h" int FilePanelInitUI (void* data) { (static_cast(data))->init (); @@ -68,6 +69,7 @@ FilePanel::FilePanel () : parent(NULL) { rightBox = Gtk::manage ( new Gtk::HBox () ); rightBox->set_size_request(50,100); rightNotebook = Gtk::manage ( new Gtk::Notebook () ); + rightNotebook->signal_switch_page().connect_notify( sigc::mem_fun(*this, &FilePanel::on_NB_switch_page) ); //Gtk::VBox* taggingBox = Gtk::manage ( new Gtk::VBox () ); history = Gtk::manage ( new History (false) ); @@ -80,6 +82,9 @@ FilePanel::FilePanel () : parent(NULL) { sFilterPanel->add (*filterPanel); sFilterPanel->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + inspectorPanel = new Inspector(); + fileCatalog->setInspector(inspectorPanel); + Gtk::ScrolledWindow* sExportPanel = Gtk::manage ( new Gtk::ScrolledWindow() ); exportPanel = Gtk::manage ( new ExportPanel () ); sExportPanel->add (*exportPanel); @@ -96,6 +101,8 @@ FilePanel::FilePanel () : parent(NULL) { Gtk::Label* devLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_DEVELOP")) ); devLab->set_angle (90); + Gtk::Label* inspectLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_INSPECT")) ); + inspectLab->set_angle (90); Gtk::Label* filtLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_FILTER")) ); filtLab->set_angle (90); //Gtk::Label* tagLab = Gtk::manage ( new Gtk::Label (M("MAIN_TAB_TAGGING")) ); @@ -108,6 +115,7 @@ FilePanel::FilePanel () : parent(NULL) { tpcPaned->pack2 (*history, true, true); rightNotebook->append_page (*tpcPaned, *devLab); + rightNotebook->append_page (*inspectorPanel, *inspectLab); rightNotebook->append_page (*sFilterPanel, *filtLab); //rightNotebook->append_page (*taggingBox, *tagLab); commented out: currently the tab is empty ... rightNotebook->append_page (*sExportPanel, *exportLab); @@ -125,6 +133,11 @@ FilePanel::FilePanel () : parent(NULL) { show_all (); } +FilePanel::~FilePanel () { + if (inspectorPanel) + delete inspectorPanel; +} + void FilePanel::setAspect () { int winW, winH; parent->get_size(winW, winH); @@ -160,7 +173,18 @@ void FilePanel::init () { } } } -} +} + +void FilePanel::on_NB_switch_page(GtkNotebookPage* page, guint page_num) { + if (page_num == 1) { + // switching the inspector "on" + fileCatalog->enableInspector(); + } + else { + // switching the inspector "off" + fileCatalog->disableInspector(); + } +} bool FilePanel::fileSelected (Thumbnail* thm) { diff --git a/rtgui/filepanel.h b/rtgui/filepanel.h index d38aa3d7d..aab1d7168 100755 --- a/rtgui/filepanel.h +++ b/rtgui/filepanel.h @@ -44,11 +44,12 @@ class FilePanel : public Gtk::HPaned, RecentBrowser* recentBrowser; // FileCatalog* fileCatalog; // filecatalog is the file browser with the button bar above it + Inspector* inspectorPanel; Gtk::VPaned* tpcPaned; BatchToolPanelCoordinator* tpc; History* history; - //FilterPanel* filterPanel; - RTWindow* parent; + //FilterPanel* filterPanel; + RTWindow* parent; Gtk::Notebook* rightNotebook; struct pendingLoad { @@ -56,12 +57,16 @@ class FilePanel : public Gtk::HPaned, ProgressConnector *pc; Thumbnail *thm; }; - MyMutex pendingLoadMutex; + MyMutex pendingLoadMutex; std::vector pendingLoads; int error; + + void on_NB_switch_page(GtkNotebookPage* page, guint page_num); + public: FilePanel (); + ~FilePanel (); Gtk::Paned* placespaned; Gtk::HPaned* dirpaned; @@ -80,10 +85,10 @@ class FilePanel : public Gtk::HPaned, void open (const Glib::ustring& d); // open a file or a directory void refreshEditedState (const std::set& efiles) { fileCatalog->refreshEditedState (efiles); } void loadingThumbs(Glib::ustring str, double rate); - - // call this before closeing rt: it saves file browser relating things into options + + // call this before closing RT: it saves file browser's related things into options void saveOptions (); - + // interface fileselectionlistener bool fileSelected (Thumbnail* thm); bool addBatchQueueJobs ( std::vector &entries ); diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc index e8d09045e..27dbc031e 100644 --- a/rtgui/guiutils.cc +++ b/rtgui/guiutils.cc @@ -19,6 +19,7 @@ #include "guiutils.h" #include "options.h" +#include "../rtengine/rt_math.h" #include "../rtengine/utils.h" #include "../rtengine/safegtk.h" #include "rtimage.h" @@ -540,12 +541,18 @@ void TextOrIcon::switchTo(TOITypes type) { show_all(); } -BackBuffer::BackBuffer() { - x = y = w = h = 0; - dirty = true; +BackBuffer::BackBuffer() : x(0), y(0), w(0), h(0), offset(0,0), dirty(true) {} + +void BackBuffer::setSrcOffset(int x, int y) { + // values will be clamped when used... + offset.x = x; + offset.y = y; } -bool BackBuffer::setDrawRectangle(Glib::RefPtr window, int newX, int newY, int newW, int newH) { +// Note: newW & newH must be > 0 +bool BackBuffer::setDrawRectangle(Glib::RefPtr window, int newX, int newY, int newW, int newH, bool updateBackBufferSize) { + assert(newW && newH); + bool newSize = w!=newW || h!=newH; x = newX; @@ -554,17 +561,31 @@ bool BackBuffer::setDrawRectangle(Glib::RefPtr window, int newX, in h = newH; // WARNING: we're assuming that the surface type won't change during all the execution time of RT. I guess it may be wrong when the user change the gfx card display settings!? - if (newSize && window) { + if (updateBackBufferSize && newSize && window) { // allocate a new Surface - if (newW>0 && newH>0) { - surface = window->create_similar_surface(Cairo::CONTENT_COLOR, w, h); - } - else { - // at least one dimension is null, so we delete the Surface - surface.clear(); - // and we reset all dimensions - x = y = w = h = 0; - } + surface.clear(); // ... don't know if this is necessary? + surface = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24, w, h); + dirty = true; + } + return dirty; +} + +// Note: newW & newH must be > 0 +bool BackBuffer::setDrawRectangle(Cairo::Format format, int newX, int newY, int newW, int newH, bool updateBackBufferSize) { + assert(!newW && !newH); + + bool newSize = w!=newW || h!=newH; + + x = newX; + y = newY; + w = newW; + h = newH; + + // WARNING: we're assuming that the surface type won't change during all the execution time of RT. I guess it may be wrong when the user change the gfx card display settings!? + if (updateBackBufferSize && newSize) { + // allocate a new Surface + surface.clear(); // ... don't know if this is necessary? + surface = Cairo::ImageSurface::create(format, w, h); dirty = true; } return dirty; @@ -579,9 +600,13 @@ void BackBuffer::copySurface(Glib::RefPtr window, GdkRectangle *rec Cairo::RefPtr crSrc = window->create_cairo_context(); Cairo::RefPtr destSurface = crSrc->get_target(); + // compute the source offset + int offsetX = rtengine::LIM(offset.x,0, surface->get_width()); + int offsetY = rtengine::LIM(offset.y,0, surface->get_height()); + // now copy the off-screen Surface to the destination Surface Cairo::RefPtr crDest = Cairo::Context::create(destSurface); - crDest->set_source(surface, x, y); + crDest->set_source(surface, x-offsetX, y-offsetY); crDest->set_line_width(0.); if (rectangle) crDest->rectangle(rectangle->x, rectangle->y, rectangle->width, rectangle->height); @@ -596,9 +621,13 @@ void BackBuffer::copySurface(Glib::RefPtr window, GdkRectangle *rec */ void BackBuffer::copySurface(BackBuffer *destBackBuffer, GdkRectangle *rectangle) { if (surface && destBackBuffer) { + // compute the source offset + int offsetX = rtengine::LIM(offset.x,0, surface->get_width()); + int offsetY = rtengine::LIM(offset.y,0, surface->get_height()); + // now copy the off-screen Surface to the destination Surface Cairo::RefPtr crDest = Cairo::Context::create(destBackBuffer->getSurface()); - crDest->set_source(surface, x, y); + crDest->set_source(surface, x-offsetX, y-offsetY); crDest->set_line_width(0.); if (rectangle) crDest->rectangle(rectangle->x, rectangle->y, rectangle->width, rectangle->height); @@ -611,11 +640,15 @@ void BackBuffer::copySurface(BackBuffer *destBackBuffer, GdkRectangle *rectangle /* * Copy the BackBuffer to another Cairo::Surface */ -void BackBuffer::copySurface(Cairo::RefPtr destSurface, GdkRectangle *rectangle) { +void BackBuffer::copySurface(Cairo::RefPtr destSurface, GdkRectangle *rectangle) { if (surface && destSurface) { + // compute the source offset + int offsetX = rtengine::LIM(offset.x,0, surface->get_width()); + int offsetY = rtengine::LIM(offset.y,0, surface->get_height()); + // now copy the off-screen Surface to the destination Surface Cairo::RefPtr crDest = Cairo::Context::create(destSurface); - crDest->set_source(surface, x, y); + crDest->set_source(surface, x-offsetX, y-offsetY); crDest->set_line_width(0.); if (rectangle) crDest->rectangle(rectangle->x, rectangle->y, rectangle->width, rectangle->height); diff --git a/rtgui/guiutils.h b/rtgui/guiutils.h index 3dae41d1e..41a2d99cc 100644 --- a/rtgui/guiutils.h +++ b/rtgui/guiutils.h @@ -265,29 +265,34 @@ class BackBuffer { protected: int x, y, w, h; // Rectangle where the colored bar has to be drawn - Cairo::RefPtr surface; + Point offset; // Offset of the source region to draw, relative to the top left corner + Cairo::RefPtr surface; bool dirty; // mean that the Surface has to be (re)allocated public: BackBuffer(); // set the destination drawing rectangle; return true if the dimensions are different - bool setDrawRectangle(Glib::RefPtr window, int newX, int newY, int newW, int newH); + // Note: newW & newH must be > 0 + bool setDrawRectangle(Glib::RefPtr window, int newX, int newY, int newW, int newH, bool updateBackBufferSize=true); + bool setDrawRectangle(Cairo::Format format, int newX, int newY, int newW, int newH, bool updateBackBufferSize=true); + void setSrcOffset(int x, int y); void copySurface(Glib::RefPtr window, GdkRectangle *rectangle=NULL); void copySurface(BackBuffer *destBackBuffer, GdkRectangle *rectangle=NULL); - void copySurface(Cairo::RefPtr destSurface, GdkRectangle *rectangle=NULL); + void copySurface(Cairo::RefPtr destSurface, GdkRectangle *rectangle=NULL); void setDirty(bool isDirty) { dirty = isDirty; if (!dirty && !surface) dirty = true; } bool isDirty() { return dirty; } // you have to check if the surface is created thanks to surfaceCreated before starting to draw on it bool surfaceCreated() { return surface; } - Cairo::RefPtr getSurface() { return surface; } - void deleteSurface() { surface.clear(); dirty=true; } + Cairo::RefPtr getSurface() { return surface; } + void setSurface(Cairo::RefPtr surf) { surface = surf; } + void deleteSurface() { if (surface) surface.clear(); dirty=true; } // will let you get a Cairo::Context for Cairo drawing operations Cairo::RefPtr getContext() { return Cairo::Context::create(surface); } - int getWidth() { return w; } - int getHeight() { return h; } + int getWidth() { return surface ? surface->get_width() : 0; } + int getHeight() { return surface ? surface->get_height() : 0; } }; diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc new file mode 100644 index 000000000..00ae1f5cb --- /dev/null +++ b/rtgui/inspector.cc @@ -0,0 +1,262 @@ +/* + * 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 "inspector.h" +#include "guiutils.h" +#include +#include "cursormanager.h" +#include "guiutils.h" +#include "options.h" +#include "../rtengine/safegtk.h" +#include "../rtengine/previewimage.h" + +extern Options options; + +InspectorBuffer::InspectorBuffer(const Glib::ustring &imagePath) : currTransform(0), fromRaw(false) { + if (!imagePath.empty() && safe_file_test(imagePath, Glib::FILE_TEST_EXISTS) && !safe_file_test(imagePath, Glib::FILE_TEST_IS_DIR)) { + imgPath = imagePath; + + // generate thumbnail image + Glib::ustring ext = getExtension (imagePath); + if (ext=="") { + imgPath.clear(); + return; + } + + rtengine::PreviewImage pi(imagePath, ext, rtengine::PreviewImage::PIM_EmbeddedOrRaw); + Cairo::RefPtr imageSurface = pi.getImage(); + + if (imageSurface) { + imgBuffer.setSurface(imageSurface); + fromRaw = true; + } + else { + imgPath.clear(); + } + } +} + +/* +InspectorBuffer::~InspectorBuffer() { +} +*/ + +int InspectorBuffer::infoFromImage (const Glib::ustring& fname) { + + rtengine::ImageMetaData* idata = rtengine::ImageMetaData::fromFile (fname, NULL); + if (!idata) + return 0; + + int deg = 0; + if (idata->hasExif()) { + if (idata->getOrientation()=="Rotate 90 CW" ) deg = 90; + else if (idata->getOrientation()=="Rotate 180" ) deg = 180; + else if (idata->getOrientation()=="Rotate 270 CW") deg = 270; + } + delete idata; + return deg; +} + +Inspector::Inspector () : currImage(NULL), zoom(0.0), active(false) {} + +Inspector::~Inspector() { + deleteBuffers(); +} + +bool Inspector::on_expose_event (GdkEventExpose* event) { + + Glib::RefPtr win = get_window(); + if (!win) + return false; + + if (!active) { + active = true; + } + + + // cleanup the region + + + if (currImage && currImage->imgBuffer.surfaceCreated()) { + // this will eventually create/update the off-screen pixmap + + // compute the displayed area + Coord availableSize; + Coord topLeft; + Coord displayedSize; + Coord dest(0, 0); + win->get_size(availableSize.x, availableSize.y); + int imW = currImage->imgBuffer.getWidth(); + int imH = currImage->imgBuffer.getHeight(); + + if (imW < availableSize.x) { + // center the image in the available space along X + topLeft.x = 0; + displayedSize.x = availableSize.x; + dest.x = (availableSize.x - imW) / 2; + } + else { + // partial image display + // double clamp + topLeft.x = center.x + availableSize.x/2; + topLeft.x = rtengine::min(topLeft.x, imW); + topLeft.x -= availableSize.x; + topLeft.x = rtengine::max(topLeft.x, 0); + } + + if (imH < availableSize.y) { + // center the image in the available space along Y + topLeft.y = 0; + displayedSize.y = availableSize.y; + dest.y = (availableSize.y - imH) / 2; + } + else { + // partial image display + // double clamp + topLeft.y = center.y + availableSize.y/2; + topLeft.y = rtengine::min(topLeft.y, imH); + topLeft.y -= availableSize.y; + topLeft.y = rtengine::max(topLeft.y, 0); + } + + //printf("center: %d, %d (img: %d, %d) (availableSize: %d, %d) (topLeft: %d, %d)\n", center.x, center.y, imW, imH, availableSize.x, availableSize.y, topLeft.x, topLeft.y); + + // define the destination area + currImage->imgBuffer.setDrawRectangle(win, dest.x, dest.y, rtengine::min(availableSize.x-dest.x, imW), rtengine::min(availableSize.y-dest.y,imH), false); + currImage->imgBuffer.setSrcOffset(topLeft.x, topLeft.y); + + if (!currImage->imgBuffer.surfaceCreated()) { + return false; + } + + // Draw! + + Gdk::Color c; + Cairo::RefPtr cr = win->create_cairo_context(); + Glib::RefPtr style = get_style(); + + // draw the background + c = style->get_bg (Gtk::STATE_NORMAL); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + cr->set_line_width (0); + cr->rectangle (0, 0, availableSize.x, availableSize.y); + cr->fill (); + + currImage->imgBuffer.copySurface(win); + + // draw the frame + c = style->get_fg (Gtk::STATE_NORMAL); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + cr->set_line_width (1); + cr->rectangle (0.5, 0.5, availableSize.x-1, availableSize.y-1); + cr->stroke (); + } + return true; +} + +void Inspector::mouseMove (rtengine::Coord2D pos, int transform) { + if (!active) + return; + + if (currImage) + center.set(int(rtengine::LIM01(pos.x)*double(currImage->imgBuffer.getWidth())), int(rtengine::LIM01(pos.y)*double(currImage->imgBuffer.getHeight()))); + else + center.set(0,0); + queue_draw(); +} + +void Inspector::switchImage (const Glib::ustring &fullPath) { + if (!active) + return; + + // we first check the size of the list, it may have been changed in Preference + if (images.size() > size_t(options.maxInspectorBuffers)) { + // deleting the last entries + for (size_t i=images.size()-1; i>size_t(options.maxInspectorBuffers-1); --i) { + delete images.at(i); + images.at(i) = NULL; + } + // resizing down + images.resize(options.maxInspectorBuffers); + } + + if (fullPath.empty()) { + currImage = NULL; + queue_draw(); + return; + } + else { + bool found = false; + for (size_t i=0; iimgPath==fullPath) { + currImage = images.at(i); + + // rolling the list 1 step to the beginning + for (size_t j=i; jimgPath.empty()) { + images.push_back(iBuffer); + currImage = images.at(images.size()-1); + } + else { + currImage = NULL; + } + } + } +} + +void Inspector::deleteBuffers () { + for (size_t i=0; i + * + * 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 _INSPECTOR_ +#define _INSPECTOR_ + +#include +#include "guiutils.h" +#include "coord.h" + +class InspectorBuffer { + private: + int infoFromImage (const Glib::ustring& fname); + + public: + BackBuffer imgBuffer; + Glib::ustring imgPath; + int currTransform; // coarse rotation from RT, not from shot orientation + bool fromRaw; + + InspectorBuffer(const Glib::ustring &imgagePath); + //~InspectorBuffer(); +}; + +class Inspector : public Gtk::DrawingArea { + + private: + Coord center; + std::vector images; + InspectorBuffer* currImage; + double zoom; + bool active; + + bool on_expose_event (GdkEventExpose* event); + void deleteBuffers(); + + public: + Inspector(); + ~Inspector(); + + /** @brief Mouse movement to a new position + * @param pos Location of the mouse, in percentage (i.e. [0;1] range) relative to the full size image ; -1,-1 == out of the image + * @param transform H/V flip and coarse rotation transformation + */ + void mouseMove (rtengine::Coord2D pos, int transform); + + /** @brief A new image is being flown over + * @param fullPath Full path of the image that is being hovered inspect, or an empty string if out of any image. + */ + void switchImage (const Glib::ustring &fullPath); + + /** @brief Set the new coarse rotation transformation + * @param transform A semi-bitfield coarse transformation using #defines from iimage.h + */ + void setTransformation (int transform); + + /** @brief Use this method to flush all image buffer whenever the Inspector panel is hidden + */ + void flushBuffers (); + + /** @brief Set the inspector on/off + * @param state true if to activate the Inspector, false to disable it and flush the buffers + */ + void setActive(bool state); + + /** @brief Get the on/off state + */ + bool isActive() { return active; }; +}; + +#endif diff --git a/rtgui/options.cc b/rtgui/options.cc index 400d99ce2..f350af0a4 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -360,11 +360,12 @@ void Options::setDefaults () { rgbDenoiseThreadLimit = 0; #if defined( _OPENMP ) && defined( __x86_64__ ) - clutCacheSize = omp_get_num_procs(); + clutCacheSize = omp_get_num_procs(); #else - clutCacheSize = 1; + clutCacheSize = 1; #endif - filledProfile = false; + filledProfile = false; + maxInspectorBuffers = 2; // a rather conservative value for low specced systems... showProfileSelector = true; FileBrowserToolbarSingleRow = false; @@ -735,17 +736,18 @@ if (keyFile.has_group ("Clipping Indication")) { } if (keyFile.has_group ("Performance")) { - if (keyFile.has_key ("Performance", "RgbDenoiseThreadLimit")) rgbDenoiseThreadLimit = keyFile.get_integer ("Performance", "RgbDenoiseThreadLimit"); - if( keyFile.has_key ("Performance", "NRauto")) rtSettings.nrauto = keyFile.get_double("Performance", "NRauto"); - if( keyFile.has_key ("Performance", "NRautomax")) rtSettings.nrautomax = keyFile.get_double("Performance", "NRautomax"); - if( keyFile.has_key ("Performance", "NRhigh")) rtSettings.nrhigh = keyFile.get_double("Performance", "NRhigh"); - if( keyFile.has_key ("Performance", "NRWavlevel")) rtSettings.nrwavlevel = keyFile.get_integer("Performance", "NRWavlevel"); - if (keyFile.has_key ("Performance", "LevNR")) rtSettings.leveldnv = keyFile.get_integer("Performance", "LevNR"); - if (keyFile.has_key ("Performance", "LevNRTI")) rtSettings.leveldnti = keyFile.get_integer("Performance", "LevNRTI"); - if (keyFile.has_key ("Performance", "LevNRAUT")) rtSettings.leveldnaut = keyFile.get_integer("Performance", "LevNRAUT"); - if (keyFile.has_key ("Performance", "LevNRLISS")) rtSettings.leveldnliss = keyFile.get_integer("Performance", "LevNRLISS"); - if (keyFile.has_key ("Performance", "SIMPLNRAUT")) rtSettings.leveldnautsimpl = keyFile.get_integer("Performance", "SIMPLNRAUT"); - if (keyFile.has_key ("Performance", "ClutCacheSize")) clutCacheSize = keyFile.get_integer ("Performance", "ClutCacheSize"); + if (keyFile.has_key ("Performance", "RgbDenoiseThreadLimit")) rgbDenoiseThreadLimit = keyFile.get_integer ("Performance", "RgbDenoiseThreadLimit"); + if( keyFile.has_key ("Performance", "NRauto")) rtSettings.nrauto = keyFile.get_double ("Performance", "NRauto"); + if( keyFile.has_key ("Performance", "NRautomax")) rtSettings.nrautomax = keyFile.get_double ("Performance", "NRautomax"); + if( keyFile.has_key ("Performance", "NRhigh")) rtSettings.nrhigh = keyFile.get_double ("Performance", "NRhigh"); + if( keyFile.has_key ("Performance", "NRWavlevel")) rtSettings.nrwavlevel = keyFile.get_integer ("Performance", "NRWavlevel"); + if (keyFile.has_key ("Performance", "LevNR")) rtSettings.leveldnv = keyFile.get_integer ("Performance", "LevNR"); + if (keyFile.has_key ("Performance", "LevNRTI")) rtSettings.leveldnti = keyFile.get_integer ("Performance", "LevNRTI"); + if (keyFile.has_key ("Performance", "LevNRAUT")) rtSettings.leveldnaut = keyFile.get_integer ("Performance", "LevNRAUT"); + if (keyFile.has_key ("Performance", "LevNRLISS")) rtSettings.leveldnliss = keyFile.get_integer ("Performance", "LevNRLISS"); + if (keyFile.has_key ("Performance", "SIMPLNRAUT")) rtSettings.leveldnautsimpl = keyFile.get_integer ("Performance", "SIMPLNRAUT"); + if (keyFile.has_key ("Performance", "ClutCacheSize")) clutCacheSize = keyFile.get_integer ("Performance", "ClutCacheSize"); + if (keyFile.has_key ("Performance", "MaxInspectorBuffers")) maxInspectorBuffers = keyFile.get_integer ("Performance", "MaxInspectorBuffers"); } if (keyFile.has_group ("GUI")) { @@ -811,12 +813,12 @@ if (keyFile.has_group ("Color Management")) { if (keyFile.has_key ("Color Management", "Intent")) rtSettings.colorimetricIntent = keyFile.get_integer("Color Management", "Intent"); if (keyFile.has_key ("Color Management", "CRI")) rtSettings.CRI_color = keyFile.get_integer("Color Management", "CRI"); - if (keyFile.has_key ("Color Management", "DenoiseLabgamma"))rtSettings.denoiselabgamma = keyFile.get_integer("Color Management", "DenoiseLabgamma"); + if (keyFile.has_key ("Color Management", "DenoiseLabgamma"))rtSettings.denoiselabgamma = keyFile.get_integer("Color Management", "DenoiseLabgamma"); if (keyFile.has_key ("Color Management", "view")) rtSettings.viewingdevice = keyFile.get_integer("Color Management", "view"); if (keyFile.has_key ("Color Management", "grey")) rtSettings.viewingdevicegrey = keyFile.get_integer("Color Management", "grey"); - if (keyFile.has_key ("Color Management", "greySc")) rtSettings.viewinggreySc = keyFile.get_integer("Color Management", "greySc"); + if (keyFile.has_key ("Color Management", "greySc")) rtSettings.viewinggreySc = keyFile.get_integer("Color Management", "greySc"); if (keyFile.has_key ("Color Management", "CBDLArtif")) rtSettings.artifact_cbdl = keyFile.get_double("Color Management", "CBDLArtif"); - if (keyFile.has_key ("Color Management", "CBDLlevel0")) rtSettings.level0_cbdl = keyFile.get_double("Color Management", "CBDLlevel0"); + if (keyFile.has_key ("Color Management", "CBDLlevel0")) rtSettings.level0_cbdl = keyFile.get_double("Color Management", "CBDLlevel0"); if (keyFile.has_key ("Color Management", "CBDLlevel123")) rtSettings.level123_cbdl = keyFile.get_double("Color Management", "CBDLlevel123"); // if (keyFile.has_key ("Color Management", "Colortoningab")) rtSettings.colortoningab = keyFile.get_double("Color Management", "Colortoningab"); // if (keyFile.has_key ("Color Management", "Decaction")) rtSettings.decaction = keyFile.get_double("Color Management", "Decaction"); @@ -1017,17 +1019,18 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_integer ("Clipping Indication", "ShadowThreshold", shadowThreshold); keyFile.set_boolean ("Clipping Indication", "BlinkClipped", blinkClipped); - keyFile.set_integer ("Performance", "RgbDenoiseThreadLimit", rgbDenoiseThreadLimit); + keyFile.set_integer ("Performance", "RgbDenoiseThreadLimit", rgbDenoiseThreadLimit); keyFile.set_double ("Performance", "NRauto", rtSettings.nrauto); keyFile.set_double ("Performance", "NRautomax", rtSettings.nrautomax); keyFile.set_double ("Performance", "NRhigh", rtSettings.nrhigh); - keyFile.set_integer ("Performance", "NRWavlevel", rtSettings.nrwavlevel); - keyFile.set_integer ("Performance", "LevNR", rtSettings.leveldnv); - keyFile.set_integer ("Performance", "LevNRTI", rtSettings.leveldnti); - keyFile.set_integer ("Performance", "LevNRAUT", rtSettings.leveldnaut); - keyFile.set_integer ("Performance", "LevNRLISS", rtSettings.leveldnliss); - keyFile.set_integer ("Performance", "SIMPLNRAUT", rtSettings.leveldnautsimpl); + keyFile.set_integer ("Performance", "NRWavlevel", rtSettings.nrwavlevel); + keyFile.set_integer ("Performance", "LevNR", rtSettings.leveldnv); + keyFile.set_integer ("Performance", "LevNRTI", rtSettings.leveldnti); + keyFile.set_integer ("Performance", "LevNRAUT", rtSettings.leveldnaut); + keyFile.set_integer ("Performance", "LevNRLISS", rtSettings.leveldnliss); + keyFile.set_integer ("Performance", "SIMPLNRAUT", rtSettings.leveldnautsimpl); keyFile.set_integer ("Performance", "ClutCacheSize", clutCacheSize); + keyFile.set_integer ("Performance", "MaxInspectorBuffers", maxInspectorBuffers); keyFile.set_string ("Output", "Format", saveFormat.format); keyFile.set_integer ("Output", "JpegQuality", saveFormat.jpegQuality); @@ -1128,8 +1131,8 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_boolean ("Color Management", "RGBcurvesLumamode_Gamut", rtSettings.rgbcurveslumamode_gamut); keyFile.set_integer ("Color Management", "Intent", rtSettings.colorimetricIntent); keyFile.set_integer ("Color Management", "view", rtSettings.viewingdevice); - keyFile.set_integer ("Color Management", "grey", rtSettings.viewingdevicegrey); - keyFile.set_integer ("Color Management", "greySc", rtSettings.viewinggreySc); + keyFile.set_integer ("Color Management", "grey", rtSettings.viewingdevicegrey); + keyFile.set_integer ("Color Management", "greySc", rtSettings.viewinggreySc); keyFile.set_string ("Color Management", "AdobeRGB", rtSettings.adobe); keyFile.set_string ("Color Management", "ProPhoto", rtSettings.prophoto); diff --git a/rtgui/options.h b/rtgui/options.h index 79d30ded6..52422eec2 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -219,7 +219,8 @@ class Options { // Performance options int rgbDenoiseThreadLimit; // maximum number of threads for the denoising tool ; 0 = use the maximum available - int clutCacheSize; + int maxInspectorBuffers; // maximum number of buffers (i.e. images) for the Inspector feature + int clutCacheSize; bool filledProfile; // Used as reminder for the ProfilePanel "mode" bool menuGroupRank; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 9d87ddb31..93d631c62 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -493,14 +493,14 @@ Gtk::Widget* Preferences::getProcParamsPanel () { } Gtk::Widget* Preferences::getPerformancePanel () { - Gtk::VBox* mvbsd = Gtk::manage( new Gtk::VBox () ); + Gtk::VBox* vbdenoise = Gtk::manage( new Gtk::VBox () ); Gtk::Frame* fdenoise = Gtk::manage( new Gtk::Frame (M("PREFERENCES_NOISE")) ); - Gtk::VBox* mainContainer = Gtk::manage( new Gtk::VBox () ); + Gtk::VBox* mainContainer = Gtk::manage( new Gtk::VBox () ); mainContainer->set_border_width (4); - - mainContainer->set_spacing(4); + + mainContainer->set_spacing(4); Gtk::HBox* threadLimitHB = Gtk::manage( new Gtk::HBox () ); threadLimitHB->set_border_width(4); @@ -579,9 +579,13 @@ Gtk::Widget* Preferences::getPerformancePanel () { colon->attach (*dnwavlab, 0, 1, 5, 6, Gtk::FILL, Gtk::SHRINK, 2, 2); colon->attach (*dnwavlev, 1, 2, 5, 6, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); colon->attach (*restartNeeded7, 2, 3, 5, 6, Gtk::FILL, Gtk::SHRINK, 2, 2); - - mainContainer->pack_start (*colon, Gtk::PACK_SHRINK, 4); - + + vbdenoise->pack_start (*colon, Gtk::PACK_SHRINK, 4); + + vbdenoise->pack_start(*threadLimitHB, Gtk::PACK_SHRINK, 4); + fdenoise->add (*vbdenoise); + mainContainer->pack_start (*fdenoise, Gtk::PACK_SHRINK, 4); + /* Gtk::Label* dntilab = Gtk::manage (new Gtk::Label (M("PREFERENCES_TINB")+":", Gtk::ALIGN_LEFT)); dnti = Gtk::manage (new Gtk::ComboBoxText ()); @@ -591,14 +595,9 @@ Gtk::Widget* Preferences::getPerformancePanel () { colon2->attach (*dntilab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); colon2->attach (*dnti, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); colon2->attach (*restartNeeded4, 2, 3, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); - mainContainer->pack_start (*colon2, Gtk::PACK_SHRINK, 4); + vbdenoise->pack_start (*colon2, Gtk::PACK_SHRINK, 4); */ - - mainContainer->pack_start(*threadLimitHB, Gtk::PACK_SHRINK, 4); - fdenoise->add (*mainContainer); - mvbsd->pack_start (*fdenoise, Gtk::PACK_SHRINK, 4); - - + Gtk::Frame* fclut = Gtk::manage( new Gtk::Frame (M("PREFERENCES_CLUTSCACHE")) ); Gtk::HBox* clutCacheSizeHB = Gtk::manage( new Gtk::HBox () ); @@ -619,14 +618,29 @@ Gtk::Widget* Preferences::getPerformancePanel () { clutCacheSizeHB->pack_start (*CLUTLl, Gtk::PACK_SHRINK, 0); clutCacheSizeHB->pack_end (*clutCacheSizeSB, Gtk::PACK_SHRINK, 0); - fclut->add (*clutCacheSizeHB); - - - mvbsd->pack_start (*fclut, Gtk::PACK_SHRINK, 4); - // return mainContainer; - return mvbsd; - + fclut->add (*clutCacheSizeHB); + mainContainer->pack_start (*fclut, Gtk::PACK_SHRINK, 4); + + Gtk::Frame* finspect = Gtk::manage( new Gtk::Frame (M("PREFERENCES_INSPECT_LABEL")) ); + + Gtk::HBox* maxIBuffersHB = Gtk::manage( new Gtk::HBox () ); + maxIBuffersHB->set_border_width(4); + maxIBuffersHB->set_spacing(4); + maxIBuffersHB->set_tooltip_text(M("PREFERENCES_INSPECT_MAXBUFFERS_TOOLTIP")); + Gtk::Label* maxIBufferLbl = Gtk::manage( new Gtk::Label (M("PREFERENCES_INSPECT_MAXBUFFERS_LABEL") + ":", Gtk::ALIGN_LEFT)); + maxInspectorBuffersSB = Gtk::manage( new Gtk::SpinButton () ); + maxInspectorBuffersSB->set_digits (0); + maxInspectorBuffersSB->set_increments (1, 5); + maxInspectorBuffersSB->set_max_length(2); + maxInspectorBuffersSB->set_range (1, 12); // ... we have to set a limit, 12 seem to be enough even for systems with tons of RAM + maxIBuffersHB->pack_start (*maxIBufferLbl, Gtk::PACK_SHRINK, 0); + maxIBuffersHB->pack_end (*maxInspectorBuffersSB, Gtk::PACK_SHRINK, 0); + finspect->add(*maxIBuffersHB); + + mainContainer->pack_start(*finspect, Gtk::PACK_SHRINK, 4); + + return mainContainer; } Gtk::Widget* Preferences::getColorManagementPanel () { @@ -1405,8 +1419,9 @@ void Preferences::storePreferences () { moptions.overwriteOutputFile = chOverwriteOutputFile->get_active (); moptions.UseIconNoText = ckbUseIconNoText->get_active(); - moptions.rgbDenoiseThreadLimit = rgbDenoiseTreadLimitSB->get_value_as_int(); + moptions.rgbDenoiseThreadLimit = rgbDenoiseTreadLimitSB->get_value_as_int(); moptions.clutCacheSize = clutCacheSizeSB->get_value_as_int(); + moptions.maxInspectorBuffers = maxInspectorBuffersSB->get_value_as_int(); // Sounds only on Windows and Linux #if defined(WIN32) || defined(__linux__) @@ -1552,7 +1567,9 @@ void Preferences::fillPreferences () { ckbUseIconNoText->set_active(moptions.UseIconNoText); rgbDenoiseTreadLimitSB->set_value(moptions.rgbDenoiseThreadLimit); - clutCacheSizeSB->set_value(moptions.clutCacheSize); + clutCacheSizeSB->set_value(moptions.clutCacheSize); + maxInspectorBuffersSB->set_value(moptions.maxInspectorBuffers); + //darkFrameDir->set_filename( moptions.rtSettings.darkFramesPath ); //updateDFinfos(); darkFrameDir->set_current_folder( moptions.rtSettings.darkFramesPath ); diff --git a/rtgui/preferences.h b/rtgui/preferences.h index 4c0f58bd0..7893f2efe 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -125,6 +125,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener { Gtk::SpinButton* rgbDenoiseTreadLimitSB; Gtk::SpinButton* clutCacheSizeSB; + Gtk::SpinButton* maxInspectorBuffersSB; Gtk::CheckButton* ckbmenuGroupRank; Gtk::CheckButton* ckbmenuGroupLabel; diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index 057db0bf9..76fb49a9e 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -75,9 +75,9 @@ RTWindow::RTWindow () :mainNB(NULL) ,bpanel(NULL) ,splash(NULL) +,btn_fullscreen(NULL) ,epanel(NULL) ,fpanel(NULL) -,btn_fullscreen(NULL) { cacheMgr->init (); @@ -284,6 +284,8 @@ RTWindow::~RTWindow() #if defined(__APPLE__) g_object_unref (osxApp); #endif + if (fpanel) + delete fpanel; } void RTWindow::findVerNumbers(int* numbers, Glib::ustring versionStr) { @@ -705,6 +707,7 @@ void RTWindow::MoveFileBrowserToEditor() { FileCatalog *fCatalog = fpanel->fileCatalog; fpanel->ribbonPane->remove(*fCatalog); + fCatalog->disableInspector(); epanel->catalogPane->add(*fCatalog); epanel->showTopPanel(options.editorFilmStripOpened); fCatalog->enableTabMode(true); @@ -783,4 +786,4 @@ bool RTWindow::isEditorPanel(Widget* panel) { bool RTWindow::isEditorPanel(guint pageNum) { return isEditorPanel(mainNB->get_nth_page(pageNum)); -} \ No newline at end of file +} diff --git a/rtgui/thumbbrowserbase.cc b/rtgui/thumbbrowserbase.cc index cf7cde8d7..ceb566b22 100644 --- a/rtgui/thumbbrowserbase.cc +++ b/rtgui/thumbbrowserbase.cc @@ -25,7 +25,7 @@ using namespace std; ThumbBrowserBase::ThumbBrowserBase () - : lastClicked(NULL), previewHeight(options.thumbSize), numOfCols(1) { + : lastClicked(NULL), previewHeight(options.thumbSize), numOfCols(1), inspector(NULL), isInspectorActive(false) { location = THLOC_FILEBROWSER; inW = -1; inH = -1; @@ -546,6 +546,15 @@ void ThumbBrowserBase::arrangeFiles () { } } +void ThumbBrowserBase::disableInspector() { + if (inspector) + inspector->setActive(false); +} + +void ThumbBrowserBase::enableInspector() { + if (inspector) + inspector->setActive(true); +} void ThumbBrowserBase::Internal::on_realize() { // Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave) diff --git a/rtgui/thumbbrowserbase.h b/rtgui/thumbbrowserbase.h index 81458de7d..046afda22 100644 --- a/rtgui/thumbbrowserbase.h +++ b/rtgui/thumbbrowserbase.h @@ -24,6 +24,7 @@ #include #include "options.h" #include "guiutils.h" +#include "inspector.h" /* * Class handling the list of ThumbBrowserEntry objects and their position in it's allocated space @@ -48,7 +49,7 @@ class ThumbBrowserBase : public Gtk::VBox { bool on_key_press_event (GdkEventKey* event); bool on_query_tooltip (int x, int y, bool keyboard_tooltip, const Glib::RefPtr& tooltip); void setPosition (int x, int y); - + void setDirty () { dirty = true; } bool isDirty () { return dirty; } }; @@ -72,12 +73,20 @@ class ThumbBrowserBase : public Gtk::VBox { int inW, inH; + Inspector *inspector; + bool isInspectorActive; + + void resizeThumbnailArea (int w, int h); void internalAreaResized (Gtk::Allocation& req); void buttonPressed (int x, int y, int button, GdkEventType type, int state, int clx, int cly, int clw, int clh); public: + void setInspector(Inspector* inspector) { this->inspector = inspector; } + Inspector* getInspector() { return inspector; } + void disableInspector(); + void enableInspector(); enum Arrangement {TB_Horizontal, TB_Vertical}; void configScrollBars (); void scrollChanged (); @@ -89,6 +98,8 @@ class ThumbBrowserBase : public Gtk::VBox { void selectFirst (bool enlarge); void selectLast (bool enlarge); + virtual bool isInTabMode() { return false; } + eLocation getLocation() { return location; } protected: diff --git a/rtgui/thumbbrowserentrybase.cc b/rtgui/thumbbrowserentrybase.cc index 271947a05..59d669c4f 100644 --- a/rtgui/thumbbrowserentrybase.cc +++ b/rtgui/thumbbrowserentrybase.cc @@ -459,7 +459,20 @@ void ThumbBrowserEntryBase::setOffset (int x, int y) { bool ThumbBrowserEntryBase::inside (int x, int y) { return x>ofsX+startx && xofsY+starty && y=prex && x<=prex+prew && y>=prey && y<=prey+preh) { + coord.x = double(x-prex)/double(prew); + coord.y = double(y-prey)/double(preh); + } + } +} bool ThumbBrowserEntryBase::insideWindow (int x, int y, int w, int h) { diff --git a/rtgui/thumbbrowserentrybase.h b/rtgui/thumbbrowserentrybase.h index d5fede9d3..fad7f4daa 100644 --- a/rtgui/thumbbrowserentrybase.h +++ b/rtgui/thumbbrowserentrybase.h @@ -127,6 +127,7 @@ protected: int getY () const { return ofsY+starty; } bool inside (int x, int y); + void getPosInImgSpace (int x, int y, rtengine::Coord2D &coord); bool insideWindow (int x, int y, int w, int h); void setPosition (int x, int y, int w, int h); void setOffset (int x, int y);