From a8e3f2cdfadfb4022383370375ef5ed3ffcdca8d Mon Sep 17 00:00:00 2001 From: natureh 510 Date: Wed, 22 Jan 2014 21:18:50 +0100 Subject: [PATCH] Patch from issue 227 (Work In Progress) --- rtdata/images/Dark/actions/editmodehand.png | Bin 0 -> 748 bytes rtdata/images/Light/actions/editmodehand.png | Bin 0 -> 739 bytes rtengine/CMakeLists.txt | 2 +- rtengine/alignedbuffer.h | 36 +- rtengine/curves.cc | 26 +- rtengine/curves.h | 1 + rtengine/dcrop.cc | 109 ++- rtengine/dcrop.h | 25 +- rtengine/editbuffer.cc | 208 ++++++ rtengine/editbuffer.h | 80 +++ rtengine/iimage.h | 419 +++++++++++- rtengine/improccoordinator.cc | 16 +- rtengine/improccoordinator.h | 4 +- rtengine/improcfun.cc | 634 ++++++++++++------ rtengine/improcfun.h | 7 +- rtengine/iplab2rgb.cc | 6 +- rtengine/labimage.cc | 23 +- rtengine/labimage.h | 1 + rtengine/rt_math.h | 2 +- rtengine/rtengine.h | 7 +- rtengine/rtthumbnail.cc | 10 +- rtengine/simpleprocess.cc | 4 +- rtgui/CMakeLists.txt | 2 +- rtgui/blackwhite.cc | 9 + rtgui/blackwhite.h | 1 + rtgui/crop.cc | 5 +- rtgui/crop.h | 2 +- rtgui/cropguilistener.h | 2 + rtgui/crophandler.cc | 20 +- rtgui/crophandler.h | 5 + rtgui/cropwindow.cc | 460 +++++++++---- rtgui/cropwindow.h | 38 +- rtgui/cursormanager.h | 7 +- rtgui/curveeditor.cc | 50 ++ rtgui/curveeditor.h | 14 +- rtgui/curveeditorgroup.cc | 24 +- rtgui/curveeditorgroup.h | 7 + rtgui/diagonalcurveeditorsubgroup.cc | 181 ++++- rtgui/diagonalcurveeditorsubgroup.h | 36 +- rtgui/edit.cc | 433 ++++++++++++ rtgui/edit.h | 336 ++++++++++ rtgui/editenums.h | 6 +- rtgui/editid.h | 61 ++ rtgui/editorpanel.cc | 1 + rtgui/flatcurveeditorsubgroup.cc | 85 ++- rtgui/flatcurveeditorsubgroup.h | 16 +- rtgui/hsvequalizer.cc | 9 + rtgui/hsvequalizer.h | 13 +- rtgui/imagearea.cc | 46 +- rtgui/imagearea.h | 36 +- rtgui/labcurve.cc | 9 +- rtgui/labcurve.h | 1 + rtgui/mycurve.cc | 2 +- rtgui/mycurve.h | 12 +- rtgui/mydiagonalcurve.cc | 323 ++++++++- rtgui/mydiagonalcurve.h | 8 +- rtgui/myflatcurve.cc | 221 +++++- rtgui/myflatcurve.h | 8 +- rtgui/rgbcurves.cc | 9 + rtgui/rgbcurves.h | 9 +- rtgui/tonecurve.cc | 8 + rtgui/tonecurve.h | 1 + rtgui/toolbar.cc | 58 +- rtgui/toolbar.h | 13 + rtgui/toolpanel.h | 2 + rtgui/toolpanelcoord.cc | 12 + rtgui/toolpanelcoord.h | 8 + tools/source_icons/scalable/editmodehand.file | 1 + tools/source_icons/scalable/editmodehand.svg | 581 ++++++++++++++++ 69 files changed, 4202 insertions(+), 609 deletions(-) create mode 100644 rtdata/images/Dark/actions/editmodehand.png create mode 100644 rtdata/images/Light/actions/editmodehand.png create mode 100644 rtengine/editbuffer.cc create mode 100644 rtengine/editbuffer.h create mode 100644 rtgui/edit.cc create mode 100644 rtgui/edit.h create mode 100644 rtgui/editid.h create mode 100644 tools/source_icons/scalable/editmodehand.file create mode 100644 tools/source_icons/scalable/editmodehand.svg diff --git a/rtdata/images/Dark/actions/editmodehand.png b/rtdata/images/Dark/actions/editmodehand.png new file mode 100644 index 0000000000000000000000000000000000000000..2b231db067f0bf5125feb4a3147c62c89118f69a GIT binary patch literal 748 zcmV=GXMYp8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10%=J^K~y-6wUsew6hRb*|2MOHx7pkpk{}!;WgyzxiTVWJMb5Rt{+3KC&Kh*2>8&b;2Fbt0dgpRYD4a+v*0mpG1&jvy8 z()a!6jYi|xa;y9kY%oC9yqS3(n`}@A;V_0u!Z))D=RB0 zfb?&|QQQKS$1RTIRo8W=rIgc$hlk-m0mpH?8U(?fQtFtQKl{GFX9Fi@MifPJj^peK zAp{W#DJ2OZM7Cj5N~z<;#l^b-&}=r}Nh#;G)*Y=i8)J;tnwi-cV@#T+TkGrVUmXCr z;8E^kC@5PAWfKb`>i0bn86kwwTDt%k01F|E)|#^%^&Jl>MKuV5H@#l(BLL)bxm%~F zrw=QY%KM?E7#0Ty2X`hWCSDt3`0VWLNx59E4g1Fm3;@k$^9TS+rP6I1y#oN-+uLGo zZ7n}DGvl3~pQlNZ7y#&WI=bC%tIf?#t+gKMesut#TCEnk-R|2~t2GY*T5Hx?8)MA3 z@$vC{08WfCW)!dhfQ5yHA4K$kncJ@GO4oG-fG#s{ZES3u{4$xf>wbe&DwUQH;=WSq zyHctL;87TcUoP{^Y?#^F^+4y9N~KRqsmB1GEiW$*T=XUjTkw?^WNB&X!=Hp{nr_)b eff0_|sPPkSni^`!{vN6T0000=GXMYp8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10$@o*K~y-6wUs|;6hRcmzc;hqEXlF3mk2@vNnr1fd#NM@t`t)U z7M8Xa20^f~5F4A=SP1q8(%TARnKDHZNtMgpZAcm=MA(R2;P%hFvB>7&ERs2hUp378 z_Qy9fZ)V9Dga096YYYn6`kj9#>h*eqnf+d`w>4sPob_UO-v&IAxLU2wdY<>*^SrmU zTJ7$Lv5;-zp>c6>vEuvwhsnvw+e)bk*LByb)#~hJT}mmWl;eSCW@aXs`JLl9cLsw& zk|aq6z>`w0&CSjE1(>9iWD9lxpjNA;aU8!sKR+K9hzLq4l*{E5xY!C?=n%ZQxtRmV zuUwiC)@cFDb_>IBMJY9) zA%rMSpNj4xrBo$N(}V5p?Z*JHyuAEHN?FgcEM{iI%&fIGT5D!z&hvaVilWE?0AmaR zC;<>eM8!%705P)wFueCf#Kl40D&`FlaWUqCb&>cgH5!eF)6>(Rk|g;K0IuuaJ3T#p z-syC{UAq<6DGb9$zVCkoV9w6YUhVJif4%M-D=+{oEiG*Uz|PK2%f{~j01=5+tECne z7TkWnpAQBD27qq2tB;S5Gh>Wq=CR&a2LOT~D93U9IZe|#0AyKawANfIm4438&p!ch z!puAlSO7q;*ZbqT?lWforIeCNDFL9L=lSZ<(a{M26fI?)dU!$h_V#`d(bFVJerH*h zYOP-!9v*ft>so8pT3e?cxp}+0yKQEEDTH{_ZnsAsdM;!Ow(g3{-%=Ds(T#$~)n9sL V5d;Pr1Tz2t002ovPDHLkV1icBM}GhS literal 0 HcmV?d00001 diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 03f339f97..db245ad47 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -11,7 +11,7 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona dfmanager.cc ffmanager.cc rawimage.cc image8.cc image16.cc imagefloat.cc imagedata.cc imageio.cc improcfun.cc init.cc dcrop.cc loadinitial.cc procparams.cc rawimagesource.cc demosaic_algos.cc shmap.cc simpleprocess.cc refreshmap.cc fast_demo.cc amaze_demosaic_RT.cc CA_correct_RT.cc cfa_linedn_RT.cc green_equil_RT.cc hilite_recon.cc expo_before_b.cc - stdimagesource.cc myfile.cc iccjpeg.cc hlmultipliers.cc improccoordinator.cc + stdimagesource.cc myfile.cc iccjpeg.cc hlmultipliers.cc improccoordinator.cc editbuffer.cc processingjob.cc rtthumbnail.cc utils.cc labimage.cc slicer.cc cieimage.cc iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc imagedimensions.cc jpeg_memsrc.cc jdatasrc.cc iimage.cc diff --git a/rtengine/alignedbuffer.h b/rtengine/alignedbuffer.h index 61fa08dff..78b8394ef 100644 --- a/rtengine/alignedbuffer.h +++ b/rtengine/alignedbuffer.h @@ -36,8 +36,9 @@ public: T* data ; bool inUse; - /* size is the number of elements of size T, i.e. allocated size will be sizeof(T)*size ; set it to 0 if you want to defer the allocation - * align is expressed in bytes; SSE instructions need 128 bits alignment, which mean 16 bytes, which is the default value + /** @brief Allocate aligned memory + * @param size Number of elements of size T to allocate, i.e. allocated size will be sizeof(T)*size ; set it to 0 if you want to defer the allocation + * @param align Expressed in bytes; SSE instructions need 128 bits alignment, which mean 16 bytes, which is the default value */ AlignedBuffer (size_t size=0, size_t align=16) : real(NULL), alignment(align), allocatedSize(0), data(NULL), inUse(false) { if (size) @@ -48,11 +49,17 @@ public: if (real) free(real); } - /* Allocate the the "size" amount of elements of "structSize" length each - * params: - * @size: number of elements to allocate - * @structSize: if non null, will let you override the default struct's size (unit: byte) - */ + /** @brief Return true if there's no memory allocated + */ + bool isEmpty() { + return allocatedSize==0; + } + + /** @brief Allocate the the "size" amount of elements of "structSize" length each + * @param size number of elements to allocate + * @param structSize if non null, will let you override the default struct's size (unit: byte) + * @return True is everything went fine, including freeing memory when size==0, false if the allocation failed + */ bool resize(size_t size, int structSize=0) { if (allocatedSize != size) { if (!size) { @@ -61,11 +68,24 @@ public: real = NULL; data = NULL; inUse = false; + allocatedSize = 0; } else { int sSize = structSize ? structSize : sizeof(T); + size_t oldAllocatedSize = allocatedSize; allocatedSize = size*sSize; - real = realloc(real, allocatedSize+alignment); + + // realloc were used here to limit memory fragmentation, specially when the size was smaller than the previous one. + // But realloc copies the content to the eventually new location, which is unnecessary. To avoid this performance penalty, + // we're freeing the memory and allocate it again if the new size is bigger. + + if (allocatedSize < oldAllocatedSize) + real = realloc(real, allocatedSize+alignment); + else { + if (real) free (real); + real = malloc(allocatedSize+alignment); + } + if (real) { //data = (T*)( (uintptr_t)real + (alignment-((uintptr_t)real)%alignment) ); data = (T*)( ( uintptr_t(real) + uintptr_t(alignment-1)) / alignment * alignment); diff --git a/rtengine/curves.cc b/rtengine/curves.cc index 68dab3c7a..984960bde 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -701,11 +701,7 @@ void CurveFactory::curveCL ( bool & clcutili,const std::vector& clcurveP histNeeded = true; } if (tcurve) { - if (tcurve->isIdentity()) { - delete tcurve; - tcurve = NULL; - } - else + if (!tcurve->isIdentity()) customToneCurve2.Set(tcurve); delete tcurve; tcurve = NULL; @@ -724,15 +720,10 @@ void CurveFactory::curveCL ( bool & clcutili,const std::vector& clcurveP histNeeded = true; } if (tcurve) { - if (tcurve->isIdentity()) { - delete tcurve; - tcurve = NULL; - } - else if (curveMode != procparams::ToneCurveParams::TC_MODE_STD) { + if (!tcurve->isIdentity()) customToneCurve1.Set(tcurve); - delete tcurve; - tcurve = NULL; - } + delete tcurve; + tcurve = NULL; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -779,7 +770,7 @@ void CurveFactory::curveCL ( bool & clcutili,const std::vector& clcurveP //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for (int i=0; i<=0xffff; i++) { - float val; + float val = dcurve[i];; if (histNeeded) { float fi=i; @@ -791,13 +782,6 @@ void CurveFactory::curveCL ( bool & clcutili,const std::vector& clcurveP outBeforeCCurveHistogram[hi] += histogram/*Cropped*/[i] ; } - // apply custom/parametric/NURBS curve, if any - if (tcurve) { - val = tcurve->getVal (dcurve[i]); // TODO: getVal(double) is very slow! Optimize with a LUTf - } else { - val = dcurve[i]; - } - // if inverse gamma is needed, do it (standard sRGB inverse gamma is applied) if (needigamma) val = igamma (val, gamma_, start, slope, mul, add); diff --git a/rtengine/curves.h b/rtengine/curves.h index 17efa202e..f5bc56359 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -28,6 +28,7 @@ #include "../rtgui/mydiagonalcurve.h" #include "color.h" #include "procparams.h" +#include "editbuffer.h" #include "LUT.h" diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 778300230..15b606cb5 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -29,11 +29,10 @@ namespace rtengine { extern const Settings* settings; -Crop::Crop (ImProcCoordinator* parent) - : origCrop(NULL), transCrop(NULL), laboCrop(NULL), labnCrop(NULL), - cropImg(NULL), cieCrop(NULL), cbuf_real(NULL), cshmap(NULL), - cbuffer(NULL), updating(false), newUpdatePending(false), - skip(10), +Crop::Crop (ImProcCoordinator* parent, EditDataProvider *editDataProvider) + : EditBuffer(editDataProvider), origCrop(NULL), laboCrop(NULL), labnCrop(NULL), + cropImg(NULL), cbuf_real(NULL), cshmap(NULL), transCrop(NULL), cieCrop(NULL), cbuffer(NULL), + updating(false), newUpdatePending(false), skip(10), cropx(0), cropy(0), cropw(-1), croph(-1), trafx(0), trafy(0), trafw(-1), trafh(-1), rqcropx(0), rqcropy(0), rqcropw(-1), rqcroph(-1), @@ -57,7 +56,7 @@ Crop::~Crop () { void Crop::destroy () { MyMutex::MyLock lock(cropMutex); - MyMutex::MyLock processingLock(parent->mProcessing); ///////// RETESTER MAINTENANT QUE CE VERROU EST AJOUTE!!! + MyMutex::MyLock processingLock(parent->mProcessing); freeAll(); } @@ -69,9 +68,39 @@ void Crop::setListener (DetailedCropListener* il) { } } -void Crop::update (int todo) { +EditUniqueID Crop::getCurrEditID() { + EditSubscriber *subscriber = EditBuffer::dataProvider ? EditBuffer::dataProvider->getCurrSubscriber() : NULL; + return subscriber ? subscriber->getEditID() : EUID_None; +} + +/* + * Delete the edit image buffer if there's no subscriber anymore. + * If allocation has to be done, it is deferred to Crop::update + */ +void Crop::setEditSubscriber(EditSubscriber* newSubscriber) { MyMutex::MyLock lock(cropMutex); + // At this point, editCrop.dataProvider->currSubscriber is the old subscriber + EditSubscriber *oldSubscriber = EditBuffer::dataProvider ? EditBuffer::dataProvider->getCurrSubscriber() : NULL; + if (newSubscriber == NULL || (oldSubscriber != NULL && oldSubscriber->getEditBufferType() != newSubscriber->getEditBufferType())) { + if (EditBuffer::imgFloatBuffer!=NULL) { + delete EditBuffer::imgFloatBuffer; + EditBuffer::imgFloatBuffer = NULL; + } + if (EditBuffer::LabBuffer!=NULL) { + delete EditBuffer::LabBuffer; + EditBuffer::LabBuffer = NULL; + } + if (EditBuffer::singlePlaneBuffer.getW()!=-1) { + EditBuffer::singlePlaneBuffer.flushData(); + } + } + // If oldSubscriber == NULL && newSubscriber != NULL -> the image will be allocated when necessary +} + +void Crop::update (int todo) { + MyMutex::MyLock cropLock(cropMutex); + ProcParams& params = parent->params; // No need to update todo here, since it has already been changed in ImprocCoordinator::updatePreviewImage, @@ -85,10 +114,13 @@ void Crop::update (int todo) { // re-allocate sub-images and arrays if their dimensions changed bool needsinitupdate = false; - if (!overrideWindow) + if (!overrideWindow) { needsinitupdate = setCropSizes (rqcropx, rqcropy, rqcropw, rqcroph, skip, true); - else + } + else { needsinitupdate = setCropSizes (wx, wy, ww, wh, ws, true); // this set skip=ws + } + // it something has been reallocated, all processing steps have to be performed if (needsinitupdate || (todo & M_HIGHQUAL)) todo = ALL; @@ -111,8 +143,9 @@ void Crop::update (int todo) { if (params.coarse.hflip) tr |= TR_HFLIP; if (params.coarse.vflip) tr |= TR_VFLIP; - if (!needsinitupdate) + if (!needsinitupdate) { setCropSizes (rqcropx, rqcropy, rqcropw, rqcroph, skip, true); + } PreviewProps pp (trafx, trafy, trafw*skip, trafh*skip, skip); parent->imgsrc->getImage (parent->currWB, tr, origCrop, pp, params.toneCurve, params.icm, params.raw ); //ColorTemp::CAT02 (origCrop, ¶ms) ; @@ -126,6 +159,9 @@ void Crop::update (int todo) { parent->imgsrc->convertColorSpace(origCrop, params.icm, parent->currWB, params.raw); } + // has to be called after setCropSizes! Tools prior to this point can't handle the Edit mechanism, but that shouldn't be a problem. + createBuffer(cropw, croph); + // transform if (needstransform) { if (!transCrop) @@ -167,7 +203,7 @@ void Crop::update (int todo) { double rrm, ggm, bbm; if (todo & M_RGBCURVE) - parent->ipf.rgbProc (baseCrop, laboCrop, parent->hltonecurve, parent->shtonecurve, parent->tonecurve, cshmap, + parent->ipf.rgbProc (baseCrop, laboCrop, this, parent->hltonecurve, parent->shtonecurve, parent->tonecurve, cshmap, params.toneCurve.saturation, parent->rCurve, parent->gCurve, parent->bCurve, parent->customToneCurve1, parent->customToneCurve2, parent->beforeToneCurveBW, parent->afterToneCurveBW,rrm, ggm, bbm, parent->bwAutoR, parent->bwAutoG, parent->bwAutoB); @@ -203,7 +239,7 @@ void Crop::update (int todo) { bool cclutili=parent->cclutili; LUTu dummy; - parent->ipf.chromiLuminanceCurve (1,labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy); + parent->ipf.chromiLuminanceCurve (this, 1,labnCrop, labnCrop, parent->chroma_acurve, parent->chroma_bcurve, parent->satcurve, parent->lhskcurve, parent->clcurve, parent->lumacurve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy); parent->ipf.vibrance (labnCrop); if((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) parent->ipf.EPDToneMap(labnCrop,5,1); //parent->ipf.EPDToneMap(labnCrop, 5, 1); //Go with much fewer than normal iterates for fast redisplay. @@ -328,15 +364,17 @@ void Crop::freeAll () { if (settings->verbose) printf ("freeallcrop starts %d\n", (int)cropAllocated); if (cropAllocated) { - if (origCrop ) { delete origCrop; origCrop=NULL; } - if (transCrop) { delete transCrop; transCrop=NULL; } - if (laboCrop ) { delete laboCrop; laboCrop=NULL; } - if (labnCrop ) { delete labnCrop; labnCrop=NULL; } - if (cropImg ) { delete cropImg; cropImg=NULL; } - if (cieCrop ) { delete cieCrop; cieCrop=NULL; } - if (cbuf_real) { delete [] cbuf_real; cbuf_real=NULL; } - if (cbuffer ) { delete [] cbuffer; cbuffer=NULL; } - if (cshmap ) { delete cshmap; cshmap=NULL; } + if (origCrop ) { delete origCrop; origCrop=NULL; } + if (transCrop) { delete transCrop; transCrop=NULL; } + if (laboCrop ) { delete laboCrop; laboCrop=NULL; } + if (labnCrop ) { delete labnCrop; labnCrop=NULL; } + if (cropImg ) { delete cropImg; cropImg=NULL; } + if (cieCrop ) { delete cieCrop; cieCrop=NULL; } + if (cbuf_real) { delete [] cbuf_real; cbuf_real=NULL; } + if (cbuffer ) { delete [] cbuffer; cbuffer=NULL; } + if (cshmap ) { delete cshmap; cshmap=NULL; } + + EditBuffer::flush(); } cropAllocated = false; } @@ -409,25 +447,42 @@ if (settings->verbose) printf ("setcropsizes before lock\n"); if (cw!=cropw || ch!=croph || orW!=trafw || orH!=trafh) { - freeAll (); - cropw = cw; croph = ch; trafw = orW; trafh = orH; - origCrop = new Imagefloat (trafw, trafh); - //transCrop will be allocated later, if necessary + if (!origCrop) + origCrop = new Imagefloat; + origCrop->allocate(trafw, trafh); // Resizing the buffer (optimization) + + // if transCrop doesn't exist yet, it'll be created where necessary + if (transCrop) transCrop->allocate(cropw, croph); + + if (laboCrop) delete laboCrop; // laboCrop can't be resized laboCrop = new LabImage (cropw, croph); + + if (labnCrop) delete labnCrop; // labnCrop can't be resized labnCrop = new LabImage (cropw, croph); - cropImg = new Image8 (cropw, croph); - //cieCrop is only used in Crop::update, it will be allocated on first use and deleted if not used anymore + + if (!cropImg) + cropImg = new Image8; + cropImg->allocate(cropw, croph); // Resizing the buffer (optimization) + + //cieCrop is only used in Crop::update, it is destroyed now but will be allocated on first use + if (cieCrop) { delete cieCrop; cieCrop=NULL; } + + if (cbuffer ) delete [] cbuffer; + if (cbuf_real) delete [] cbuf_real; + if (cshmap ) delete cshmap; cbuffer = new float*[croph]; cbuf_real= new float[(croph+2)*cropw]; 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 "editbuffer.h" + +namespace rtengine { + +EditBuffer::EditBuffer(::EditDataProvider *dataProvider) : + objectMap(NULL), objectMap2(NULL), objectMode(OM_255), dataProvider(dataProvider), + imgFloatBuffer(NULL), LabBuffer(NULL), singlePlaneBuffer() {} + +EditBuffer::~EditBuffer() { + flush(); + #ifndef NDEBUG + imgFloatBuffer = (Imagefloat*)(0xbaadf00d); + #endif + #ifndef NDEBUG + LabBuffer = (LabImage*)(0xbaadf00d); + #endif +} + +void EditBuffer::createBuffer(int width, int height) { + resize (width, height); +} + +void EditBuffer::flush() { + if (imgFloatBuffer) { + delete imgFloatBuffer; + imgFloatBuffer = NULL; + } + if (LabBuffer) { + delete LabBuffer; + LabBuffer = NULL; + } + singlePlaneBuffer.flushData(); +} + +/* Upgrade or downgrade the objectModeType; we're assuming that objectMap has been allocated */ +void EditBuffer::setObjectMode(ObjectMode newType) { + switch (newType) { + case (OM_255): + if (objectMap2) { + objectMap2->unreference(); + } + objectMode = OM_255; + break; + case (OM_65535): + if (!objectMap2) { + objectMap2 = Cairo::ImageSurface::create(Cairo::FORMAT_A8, objectMap->get_width(), objectMap->get_height()); + } + objectMode = OM_65535; + break; + } +} + +EditUniqueID EditBuffer::getEditID() { + if (dataProvider && dataProvider->getCurrSubscriber()) { + return dataProvider->getCurrSubscriber()->getEditID(); + } + else return EUID_None; +} + +// Resize buffers if they already exist +void EditBuffer::resize(int newWidth, int newHeight) { + EditSubscriber* subscriber = NULL; + if (dataProvider && (subscriber = dataProvider->getCurrSubscriber())) { + if (subscriber->getEditingType() == ET_OBJECTS) { + if (objectMap && (objectMap->get_width() != newWidth || objectMap->get_height() != newHeight)) + objectMap->unreference(); + + if (!objectMap) { + objectMap = Cairo::ImageSurface::create(Cairo::FORMAT_A8, newWidth, newHeight); + } + if (objectMode==OM_65535) { + if (objectMap2) { + if (objectMap2->get_width() != newWidth || objectMap2->get_height() != newHeight) { + objectMap2->unreference(); + } + } + if (!objectMap2) { + objectMap2 = Cairo::ImageSurface::create(Cairo::FORMAT_A8, newWidth, newHeight); + } + } + // OM_255 -> deleting objectMap2, if any + else if (objectMap2) + objectMap2->unreference(); + + // Should never happen! + if (imgFloatBuffer) { + delete imgFloatBuffer; + imgFloatBuffer = NULL; + } + if (LabBuffer) { + delete LabBuffer; + LabBuffer = NULL; + } + if (singlePlaneBuffer.data) { + singlePlaneBuffer.allocate(0,0); + } + } + + if (subscriber->getEditingType() == ET_PIPETTE) { + if (subscriber->getEditBufferType() == BT_IMAGEFLOAT) { + if (!imgFloatBuffer) + imgFloatBuffer = new Imagefloat(newWidth, newHeight); + else + imgFloatBuffer->allocate(newWidth, newHeight); + } + else if (imgFloatBuffer) { + delete imgFloatBuffer; + imgFloatBuffer = NULL; + } + + if (subscriber->getEditBufferType() == BT_LABIMAGE) { + if (LabBuffer && (LabBuffer->W != newWidth && LabBuffer->H != newHeight)) { + delete LabBuffer; + LabBuffer = NULL; + } + if (!LabBuffer) + LabBuffer = new LabImage(newWidth, newHeight); + } + else if (LabBuffer) { + delete LabBuffer; + LabBuffer = NULL; + } + + if (subscriber->getEditBufferType() == BT_SINGLEPLANE_FLOAT) { + singlePlaneBuffer.allocate(newWidth, newHeight); + } + else if (singlePlaneBuffer.data) + singlePlaneBuffer.allocate(0,0); + + // Should never happen! + if (objectMap ) objectMap->unreference(); + if (objectMap2) objectMap2->unreference(); + } + } +} + +bool EditBuffer::bufferCreated() { + if (dataProvider && dataProvider->getCurrSubscriber()) { + switch (dataProvider->getCurrSubscriber()->getEditBufferType()) { + case (BT_IMAGEFLOAT): + return imgFloatBuffer != NULL; + case (BT_LABIMAGE): + return LabBuffer != NULL; + case (BT_SINGLEPLANE_FLOAT): + return singlePlaneBuffer.data != NULL; + } + } + return false; +} + +unsigned short EditBuffer::getObjectID(const Coord& location) { + unsigned short id = 0; + + if (objectMap) + id = (unsigned short)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )); + + if (objectMap2) + id |= (unsigned short)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )) << 8; + + return id; +} + +void EditBuffer::getPipetteData(float* v, int x, int y, int squareSize) { + if (dataProvider && dataProvider->getCurrSubscriber()) { + switch (dataProvider->getCurrSubscriber()->getEditBufferType()) { + case (BT_IMAGEFLOAT): + if (imgFloatBuffer) + imgFloatBuffer->getPipetteData(v[0], v[1], v[2], x, y, squareSize, 0); + else + v[0] = v[1] = v[2] = -1.f; + break; + case (BT_LABIMAGE): + if (LabBuffer) + LabBuffer->getPipetteData(v[0], v[1], v[2], x, y, squareSize); + else + v[0] = v[1] = v[2] = -1.f; + break; + case (BT_SINGLEPLANE_FLOAT): + if (singlePlaneBuffer.data != NULL) { + singlePlaneBuffer.getPipetteData(v[0], x, y, squareSize, 0); + v[1] = v[2] = -1.f; + } + else + v[0] = v[1] = v[2] = -1.f; + } + } +} + +} diff --git a/rtengine/editbuffer.h b/rtengine/editbuffer.h new file mode 100644 index 000000000..17847586e --- /dev/null +++ b/rtengine/editbuffer.h @@ -0,0 +1,80 @@ +/* + * 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 _EDITBUFFER_H_ +#define _EDITBUFFER_H_ + +#include "../rtgui/edit.h" +#include "array2D.h" +#include "iimage.h" + +namespace rtengine { + +/// @brief Structure that contains information about and pointers to the Edit buffer +class EditBuffer { +private: + + // Used to draw the objects where the color correspond to the object's ID, in order to find the correct object when hovering + Cairo::RefPtr objectMap; + // If more than 254 objects has to be handled, objectMap2 contains the "upper part" of the 16 bit int value. objectMap2 will be NULL otherwise. + Cairo::RefPtr objectMap2; + ObjectMode objectMode; + +protected: + + // To avoid duplicated information, we points to a EditDataProvider that contains the current EditSubscriber + // instead of pointing to the EditSubscriber directly + ::EditDataProvider* dataProvider; + + // TODO: Unfortunately, buffer can be of several type, each one representing a floating point image. Maybe we could unify everything one day!? + // Only one of the following pointers will be allocated at a time, if any; "one chunk" allocation + Imagefloat* imgFloatBuffer; + LabImage* LabBuffer; + PlanarWhateverData singlePlaneBuffer; + + void createBuffer(int width, int height); + void resize(int newWidth, int newHeight); + void flush(); + +public: + EditBuffer(::EditDataProvider *dataProvider); + ~EditBuffer(); + + + void setObjectMode(ObjectMode newType); + ::EditDataProvider* getDataProvider() { return dataProvider; } + EditUniqueID getEditID(); + Imagefloat* getImgFloatBuffer() { return imgFloatBuffer; } + LabImage* getLabBuffer() { return LabBuffer; } + PlanarWhateverData* getSinglePlaneBuffer() { return &singlePlaneBuffer; } + ObjectMode getObjectMode() { return objectMode; } + + Cairo::RefPtr &getObjectMap () { return objectMap; } + Cairo::RefPtr &getObjectMap2() { return objectMap2; } + + // return true if the buffer has been allocated + bool bufferCreated(); + + unsigned short getObjectID(const Coord& location); + // get the pipette values + void getPipetteData(float* v, int x, int y, int squareSize); +}; + +} + +#endif diff --git a/rtengine/iimage.h b/rtengine/iimage.h index 0349c6b89..6f9ec9707 100644 --- a/rtengine/iimage.h +++ b/rtengine/iimage.h @@ -149,7 +149,311 @@ namespace rtengine { }; template - class PlanarImageData : virtual public ImageDatas { + class PlanarWhateverData : virtual public ImageDatas { + + private: + AlignedBuffer abData; + + int rowstride; // Plan size, in bytes (all padding bytes included) + + public: + T* data; + PlanarPtr v; // v stands for "value", whatever it represent + + PlanarWhateverData() : rowstride(0), data (NULL) {} + PlanarWhateverData(int w, int h) : rowstride(0), data (NULL) { + allocate(w, h); + } + + // Send back the row stride. WARNING: unit = byte, not element! + int getRowStride () { return rowstride; } + + void swap(PlanarWhateverData &other) { + abData.swap(other.abData); + v.swap(other.v); + T* tmpData = other.data; + other.data = data; + data = tmpData; + int tmpWidth = other.width; + other.width = width; + width = tmpWidth; + int tmpHeight = other.height; + other.height = height; + height = tmpHeight; + } + + // use as pointer to data + //operator void*() { return data; }; + + /* If any of the required allocation fails, "width" and "height" are set to -1, and all remaining buffer are freed + * Can be safely used to reallocate an existing image */ + void allocate (int W, int H) { + + if (W==width && H==height) + return; + + width=W; + height=H; + + if (sizeof(T) > 1) { + // 128 bits memory alignment for >8bits data + rowstride = ( width*sizeof(T)+15 )/16*16; + } + else { + // No memory alignment for 8bits data + rowstride = width*sizeof(T); + } + + // find the padding length to ensure a 128 bits alignment for each row + size_t size = rowstride * height; + if (!width) { + size = 0; + rowstride = 0; + } + + if (size && abData.resize(size, 1) + && v.resize(height) ) + { + data = abData.data; + } + else { + // asking for a new size of 0 is safe and will free memory, if any! + abData.resize(0); + data = NULL; + v.resize(0); + width = height = -1; + return; + } + + char *start = (char*)(data); + + for (int i=0; i *dest) { + assert (dest!=NULL); + // Make sure that the size is the same, reallocate if necessary + dest->allocate(width, height); + if (dest->width == -1) { + return; + } + for (int i=0; iv(i), v(i), width*sizeof(T)); + } + } + + void rotate (int deg) { + + if (deg==90) { + PlanarWhateverData rotatedImg(height, width); // New image, rotated + + for (int ny=0; ny rotatedImg(height, width); // New image, rotated + + for (int nx=0; nx32 && height>50; + #pragma omp parallel for schedule(static) if(bigImage) + #endif + for (int i=0; i + 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) { + for (int i=0; iv(i,j)); + } + } + } + else if (interp == TI_Bilinear) { + for (int i=0; i=height) sy = height-1; + float dy = float(i)*float(height)/float(nh) - float(sy); + int ny = sy+1; + if (ny>=height) ny = sy; + for (int j=0; j=width) sx = width; + float dx = float(j)*float(width)/float(nw) - float(sx); + int nx = sx+1; + if (nx>=width) nx = sx; + convertTo(v(sy,sx)*(1.f-dx)*(1.f-dy) + v(sy,nx)*dx*(1.f-dy) + v(ny,sx)*(1.f-dx)*dy + v(ny,nx)*dx*dy, imgPtr->v(i,j)); + } + } + } + else { + // This case should never occur! + for (int i=0; i32 && height>50; + #pragma omp parallel for schedule(static) if(bigImage) + #endif + for (int i=0; i32 && height>50; + #pragma omp parallel for schedule(static) if(bigImage) + #endif + for (int i=0; i please creates specialization if necessary + unsigned long int n = 0; + int halfSquare = squareSize/2; + transformPixel (posX, posY, tran, x, y); + for (int iy=y-halfSquare; iy=0 && iy>=0 && ixv(iy, ix)); + ++n; + } + } + } + value = n ? T(accumulator/float(n)) : T(0); + } + + void readData (FILE *f) { + for (int i=0; i + class PlanarRGBData : virtual public ImageDatas { private: AlignedBuffer abData; @@ -163,8 +467,8 @@ namespace rtengine { PlanarPtr g; PlanarPtr b; - PlanarImageData() : rowstride(0), planestride(0), data (NULL) {} - PlanarImageData(int w, int h) : rowstride(0), planestride(0), data (NULL) { + PlanarRGBData() : rowstride(0), planestride(0), data (NULL) {} + PlanarRGBData(int w, int h) : rowstride(0), planestride(0), data (NULL) { allocate(w, h); } @@ -173,7 +477,7 @@ namespace rtengine { // Send back the plane stride. WARNING: unit = byte, not element! int getPlaneStride () { return planestride; } - void swap(PlanarImageData &other) { + void swap(PlanarRGBData &other) { abData.swap(other.abData); r.swap(other.r); g.swap(other.g); @@ -215,6 +519,10 @@ namespace rtengine { // find the padding length to ensure a 128 bits alignment for each row size_t size = rowstride * 3*height; + if (!width) { + size = 0; + rowstride = 0; + } if (size && abData.resize(size, 1) && r.resize(height) @@ -226,6 +534,7 @@ namespace rtengine { else { // asking for a new size of 0 is safe and will free memory, if any! abData.resize(0); + data = NULL; r.resize(0); g.resize(0); b.resize(0); @@ -245,11 +554,15 @@ namespace rtengine { } } - /** Copy the data to another PlanarImageData */ - void copyData(PlanarImageData *dest) { + /** Copy the data to another PlanarRGBData */ + void copyData(PlanarRGBData *dest) { assert (dest!=NULL); // Make sure that the size is the same, reallocate if necessary dest->allocate(width, height); + if (dest->width == -1) { + printf("ERROR: PlanarRGBData::copyData >>> allocation failed!\n"); + return; + } for (int i=0; ir(i), r(i), width*sizeof(T)); memcpy (dest->g(i), g(i), width*sizeof(T)); @@ -260,7 +573,7 @@ namespace rtengine { void rotate (int deg) { if (deg==90) { - PlanarImageData rotatedImg(height, width); // New image, rotated + PlanarRGBData rotatedImg(height, width); // New image, rotated for (int ny=0; ny rotatedImg(height, width); // New image, rotated + PlanarRGBData rotatedImg(height, width); // New image, rotated for (int nx=0; nx please creates specialization if necessary + float accumulatorG = 0.f; // " + float accumulatorB = 0.f; // " + unsigned long int n = 0; + int halfSquare = squareSize/2; + transformPixel (posX, posY, tran, x, y); + for (int iy=y-halfSquare; iy=0 && iy>=0 && ixr(iy, ix)); + accumulatorG += float(this->g(iy, ix)); + accumulatorB += float(this->b(iy, ix)); + ++n; + } + } + } + valueR = n ? T(accumulatorR/float(n)) : T(0); + valueG = n ? T(accumulatorG/float(n)) : T(0); + valueB = n ? T(accumulatorB/float(n)) : T(0); + } + void readData (FILE *f) { for (int i=0; i - class ChunkyImageData : virtual public ImageDatas { + class ChunkyRGBData : virtual public ImageDatas { private: AlignedBuffer abData; @@ -632,8 +974,8 @@ namespace rtengine { ChunkyPtr g; ChunkyPtr b; - ChunkyImageData() : data (NULL) {} - ChunkyImageData(int w, int h) : data (NULL) { + ChunkyRGBData() : data (NULL) {} + ChunkyRGBData(int w, int h) : data (NULL) { allocate(w, h); } @@ -641,7 +983,7 @@ namespace rtengine { * @return a pointer to the pixel data */ const T* getData () { return data; } - void swap(ChunkyImageData &other) { + void swap(ChunkyRGBData &other) { abData.swap(other.abData); r.swap(other.r); g.swap(other.g); @@ -669,7 +1011,8 @@ namespace rtengine { width=W; height=H; - if (abData.resize(width*height*3)) { + abData.resize(width*height*3); + if (!abData.isEmpty()) { data = abData.data; r.init(data, width); g.init(data+1, width); @@ -684,18 +1027,22 @@ namespace rtengine { } } - /** Copy the data to another ChunkyImageData */ - void copyData(ChunkyImageData *dest) { + /** Copy the data to another ChunkyRGBData */ + void copyData(ChunkyRGBData *dest) { assert (dest!=NULL); // Make sure that the size is the same, reallocate if necessary dest->allocate(width, height); + if (dest->width == -1) { + printf("ERROR: ChunkyRGBData::copyData >>> allocation failed!\n"); + return; + } memcpy (dest->data, data, 3*width*height*sizeof(T)); } void rotate (int deg) { if (deg==90) { - ChunkyImageData rotatedImg(height, width); // New image, rotated + ChunkyRGBData rotatedImg(height, width); // New image, rotated for (int ny=0; ny rotatedImg(height, width); // New image, rotated + ChunkyRGBData rotatedImg(height, width); // New image, rotated for (int nx=0; nx { + /** @brief This class represents an image having a float pixel planar representation. + The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */ + class IImagefloat : public IImage, public PlanarRGBData { public: virtual ~IImagefloat() {} }; - /** This class represents an image having a classical 8 bits/pixel representation */ - class IImage8 : public IImage, public ChunkyImageData { + /** @brief This class represents an image having a classical 8 bits/pixel representation */ + class IImage8 : public IImage, public ChunkyRGBData { public: virtual ~IImage8() {} }; - /** This class represents an image having a 16 bits/pixel planar representation. + /** @brief This class represents an image having a 16 bits/pixel planar representation. The planes are stored as two dimensional arrays. All the rows have a 128 bits alignment. */ - class IImage16 : public IImage, public PlanarImageData { + class IImage16 : public IImage, public PlanarRGBData { public: virtual ~IImage16() {} }; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 267b8b03c..313e1ccac 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -108,14 +108,14 @@ ImProcCoordinator::~ImProcCoordinator () { updaterThreadStart.unlock (); } -DetailedCrop* ImProcCoordinator::createCrop () { +DetailedCrop* ImProcCoordinator::createCrop (::EditDataProvider *editDataProvider) { - return new Crop (this); + return new Crop (this, editDataProvider); } // todo: bitmask containing desired actions, taken from changesSinceLast -// cropCall: calling crop, used to prevent self-updates +// cropCall: calling crop, used to prevent self-updates ...doesn't seem to be used void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { MyMutex::MyLock processingLock(mProcessing); @@ -304,7 +304,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { if ((todo & M_RGBCURVE) || (todo & M_CROP)) { if (hListener) oprevi->calcCroppedHistogram(params, scale, histCropped); - // complexCurve also calculated pre-curves histogram dependend on crop + // complexCurve also calculated pre-curves histogram depending on crop ipf.g = imgsrc->getGamma(); ipf.iGamma = true; CurveFactory::complexCurve (params.toneCurve.expcomp, params.toneCurve.black/65535.0, @@ -325,8 +325,8 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { double bbm=33.; // if it's just crop we just need the histogram, no image updates - if ( todo!=MINUPDATE ) { - ipf.rgbProc (oprevi, oprevl, hltonecurve, shtonecurve, tonecurve, shmap, params.toneCurve.saturation, + if ( todo & M_RGBCURVE ) { + ipf.rgbProc (oprevi, oprevl, NULL, hltonecurve, shtonecurve, tonecurve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2,beforeToneCurveBW, afterToneCurveBW, rrm, ggm, bbm, bwAutoR, bwAutoG, bwAutoB, params.toneCurve.expcomp, params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh); if(params.blackwhite.enabled && params.blackwhite.autoc && abwListener) { if (settings->verbose) @@ -337,7 +337,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { } // compute L channel histogram - int x1, y1, x2, y2, pos, poscc; + int x1, y1, x2, y2, pos; params.crop.mapToResized(pW, pH, scale, x1, x2, y1, y2); lhist16.clear(); lhist16Cropped.clear(); lhist16Clad.clear(); lhist16CLlad.clear();lhist16LLClad.clear(); @@ -380,7 +380,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) { progress ("Applying Color Boost...",100*readyphase/numofphases); - ipf.chromiLuminanceCurve (pW,nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili,cclutili,clcutili, histCCurve, histCLurve, histLLCurve, histLCurve); + ipf.chromiLuminanceCurve (NULL, pW,nprevl, nprevl, chroma_acurve, chroma_bcurve, satcurve, lhskcurve, clcurve, lumacurve, utili, autili, butili, ccutili,cclutili,clcutili, histCCurve, histCLurve, histLLCurve, histLCurve); ipf.vibrance(nprevl); if((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled)) ipf.EPDToneMap(nprevl,5,1); // for all treatments Defringe, Sharpening, Contrast detail , Microcontrast they are activated if "CIECAM" function are disabled diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h index 2fbf63009..65eca08c1 100644 --- a/rtengine/improccoordinator.h +++ b/rtengine/improccoordinator.h @@ -198,7 +198,7 @@ class ImProcCoordinator : public StagedImageProcessor { int getPreviewWidth () { return pW; } int getPreviewHeight () { return pH; } - DetailedCrop* createCrop (); + DetailedCrop* createCrop (::EditDataProvider *editDataProvider); bool getAutoWB (double& temp, double& green, double equal); void getCamWB (double& temp, double& green); @@ -212,7 +212,7 @@ class ImProcCoordinator : public StagedImageProcessor { void setAutoExpListener (AutoExpListener* ael) {aeListener = ael; } void setHistogramListener(HistogramListener *h) {hListener = h; } void setAutoCamListener (AutoCamListener* acl) {acListener = acl; } - void setAutoBWListener (AutoBWListener* abw) {abwListener = abw; } + void setAutoBWListener (AutoBWListener* abw) {abwListener = abw; } void saveInputICCReference (const Glib::ustring& fname); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index d005ab52a..7a7c2b2c6 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -1,4 +1,3 @@ -/* /* * This file is part of RawTherapee. * @@ -1911,20 +1910,39 @@ if((params->colorappearance.tonecie && (params->edgePreservingDecompositionUI.en } } //end CIECAM -void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, +void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *editBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const ToneCurve & customToneCurve1,const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1,const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob ) { - rgbProc (working, lab, hltonecurve, shtonecurve, tonecurve, shmap, sat, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2,rrm, ggm, bbm, autor, autog, autob, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh); + rgbProc (working, lab, editBuffer, hltonecurve, shtonecurve, tonecurve, shmap, sat, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2,rrm, ggm, bbm, autor, autog, autob, params->toneCurve.expcomp, params->toneCurve.hlcompr, params->toneCurve.hlcomprthresh); } // Process RGB image and convert to LAB space -void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, +void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *editBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1,const ToneCurve & customToneCurvebw2,double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, double expcomp, int hlcompr, int hlcomprthresh) { LUTf iGammaLUTf; Imagefloat *tmpImage; + // NOTE: We're getting all 3 pointers here, but this function may not need them all, so one could optimize this + Imagefloat* editImgFloat = NULL; + LabImage* editLab = NULL; + PlanarWhateverData* editWhatever = NULL; + EditUniqueID editID = editBuffer ? editBuffer->getEditID() : EUID_None; + if (editID != EUID_None) { + switch (editBuffer->getDataProvider()->getCurrSubscriber()->getEditBufferType()) { + case (BT_IMAGEFLOAT): + editImgFloat = editBuffer->getImgFloatBuffer(); + break; + case (BT_LABIMAGE): + editLab = editBuffer->getLabBuffer(); + break; + case (BT_SINGLEPLANE_FLOAT): + editWhatever = editBuffer->getSinglePlaneBuffer(); + break; + } + } + int h_th, s_th; if (shmap) { h_th = shmap->max_f - params->sh.htonalwidth * (shmap->max_f - shmap->avg) / 100; @@ -2007,6 +2025,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone // extracting datas from 'params' to avoid cache flush (to be confirmed) ToneCurveParams::eTCModeId curveMode = params->toneCurve.curveMode; ToneCurveParams::eTCModeId curveMode2 = params->toneCurve.curveMode2; + bool highlight = params->toneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated bool hasToneCurve1 = bool(customToneCurve1); bool hasToneCurve2 = bool(customToneCurve2); BlackWhiteParams::eTCModeId beforeCurveMode = params->blackwhite.beforeCurveMode; @@ -2089,8 +2108,12 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone #pragma omp parallel if (multiThread) #endif { - char *buffer = (char *) malloc(3*sizeof(float)*TS*TS + 20*64 + 63); - char *data; + char *buffer; + char *editIFloatBuffer = NULL; + char *editWhateverBuffer = NULL; + + buffer = (char *) malloc(3*sizeof(float)*TS*TS + 20*64 + 63); + char *data; data = (char*)( ( uintptr_t(buffer) + uintptr_t(63)) / 64 * 64); float *rtemp = (float(*))data; @@ -2098,8 +2121,26 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone float *btemp = (float (*)) ((char*)gtemp + sizeof(float)*TS*TS + 8*64); int istart; int jstart; - int tW; - int tH; + int tW; + int tH; + + // Allocating buffer for the EditBuffer + float *editIFloatTmpR, *editIFloatTmpG, *editIFloatTmpB, *editWhateverTmp; + char *editIFBuffer = NULL; + if (editImgFloat) { + editIFloatBuffer = (char *) malloc(3*sizeof(float)*TS*TS + 20*64 + 63); + data = (char*)( ( uintptr_t(editIFloatBuffer) + uintptr_t(63)) / 64 * 64); + + editIFloatTmpR = (float(*))data; + editIFloatTmpG = (float (*)) ((char*)editIFloatTmpR + sizeof(float)*TS*TS + 4*64); + editIFloatTmpB = (float (*)) ((char*)editIFloatTmpG + sizeof(float)*TS*TS + 8*64); + } + if (editWhatever) { + editWhateverBuffer = (char *) malloc(sizeof(float)*TS*TS + 20*64 + 63); + data = (char*)( ( uintptr_t(editWhateverBuffer) + uintptr_t(63)) / 64 * 64); + + editWhateverTmp = (float(*))data; + } #pragma omp for schedule(dynamic) collapse(2) nowait for(int ii=0;iiheight;ii+=TS) @@ -2214,72 +2255,91 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone } } - if (hasToneCurve1) { + if (editID == EUID_ToneCurve1) { // filling the pipette buffer + for (int i=istart,ti=0; i(customToneCurve1); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); - } + const StandardToneCurve& userToneCurve = static_cast(customToneCurve1); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } + } } else if (curveMode==ToneCurveParams::TC_MODE_FILMLIKE){ // Adobe like for (int i=istart,ti=0; i(customToneCurve1); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); - } + const AdobeToneCurve& userToneCurve = static_cast(customToneCurve1); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } + } } else if (curveMode==ToneCurveParams::TC_MODE_SATANDVALBLENDING){ // apply the curve on the saturation and value channels for (int i=istart,ti=0; i(customToneCurve1); - rtemp[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]); - gtemp[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]); - btemp[ti*TS+tj] = CLIP(btemp[ti*TS+tj]); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); - } + const SatAndValueBlendingToneCurve& userToneCurve = static_cast(customToneCurve1); + rtemp[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]); + gtemp[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]); + btemp[ti*TS+tj] = CLIP(btemp[ti*TS+tj]); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } + } } else if (curveMode==ToneCurveParams::TC_MODE_WEIGHTEDSTD){ // apply the curve to the rgb channels, weighted const WeightedStdToneCurve& userToneCurve = static_cast(customToneCurve1); for (int i=istart,ti=0; i(rtemp[ti*TS+tj]); - gtemp[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]); - btemp[ti*TS+tj] = CLIP(btemp[ti*TS+tj]); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); + rtemp[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]); + gtemp[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]); + btemp[ti*TS+tj] = CLIP(btemp[ti*TS+tj]); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } } } } + if (editID == EUID_ToneCurve2) { // filling the pipette buffer + for (int i=istart,ti=0; i(customToneCurve2); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); - } + const StandardToneCurve& userToneCurve = static_cast(customToneCurve2); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } } + } else if (curveMode2==ToneCurveParams::TC_MODE_FILMLIKE){ // Adobe like for (int i=istart,ti=0; i(customToneCurve2); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); - } + const AdobeToneCurve& userToneCurve = static_cast(customToneCurve2); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } } + } else if (curveMode2==ToneCurveParams::TC_MODE_SATANDVALBLENDING){ // apply the curve on the saturation and value channels for (int i=istart,ti=0; i(customToneCurve2); - userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); - } + const SatAndValueBlendingToneCurve& userToneCurve = static_cast(customToneCurve2); + userToneCurve.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } + } } else if (curveMode2==ToneCurveParams::TC_MODE_WEIGHTEDSTD){ // apply the curve to the rgb channels, weighted const WeightedStdToneCurve& userToneCurve = static_cast(customToneCurve2); @@ -2302,6 +2362,28 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone } } + if (editID == EUID_RGB_R) { + for (int i=istart,ti=0; irgbCurves.lumamode){ // normal RGB mode @@ -2320,8 +2402,6 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone for (int i=istart,ti=0; itoneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated - float r1,g1,b1, r2,g2,b2, L_1,L_2, Lfactor,a_1,b_1,x_,y_,z_,R,G,B ; float y,fy, yy,fyy,x,z,fx,fz; @@ -2391,7 +2471,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone b_1=327.68f*Chpro*sincosval.x; } //end of gamut control - //calculate RGB with L_2 and old value of a and b + //calculate RGB with L_2 and old value of a and b Color::Lab2XYZ(L_2, a_1, b_1, x_, y_, z_) ; Color::xyz2rgb(x_,y_,z_,R,G,B,wip); @@ -2403,6 +2483,16 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone } } + if (editID == EUID_HSV_H || editID == EUID_HSV_S || editID == EUID_HSV_V) { + for (int i=istart,ti=0; ilab + Color::rgbxyz(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj], X, Y, Z, wp); + //convert Lab + Color::XYZ2Lab(X, Y, Z, L, aa, bb); + //end rgb=>lab + float HH=xatan2f(bb,aa);// HH hue in -3.141 +3.141 + + editWhateverTmp[ti*TS+tj] = float(Color::huelab_to_huehsv2(HH)); + } + } + } //black and white if(blackwhite){ @@ -2483,7 +2599,6 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone rtemp[ti*TS+tj] = CLIP(rtemp[ti*TS+tj]); gtemp[ti*TS+tj] = CLIP(gtemp[ti*TS+tj]); btemp[ti*TS+tj] = CLIP(btemp[ti*TS+tj]); - userToneCurvebw.Apply(rtemp[ti*TS+tj], gtemp[ti*TS+tj], btemp[ti*TS+tj]); } } @@ -2536,8 +2651,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone else if (algm==1) {//Luminance mixer in Lab mode to avoid artifacts for (int i=istart,ti=0; itoneCurve.hrenabled;//Get the value if "highlight reconstruction" is activated - //rgb=>lab + //rgb=>lab float r = rtemp[ti*TS+tj]; float g = gtemp[ti*TS+tj]; float b = btemp[ti*TS+tj]; @@ -2552,9 +2666,10 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone float HH=xatan2f(bb,aa);// HH hue in -3.141 +3.141 float l_r;//Luminance Lab in 0..1 l_r = L/32768.f; + if (bwlCurveEnabled) { - double hr; - float valparam = float((bwlCurve->getVal((hr=Color::huelab_to_huehsv2(HH)))-0.5f) * 2.0f);//get l_r=f(H) + double hr = Color::huelab_to_huehsv2(HH); + float valparam = float((bwlCurve->getVal(hr)-0.5f) * 2.0f);//get l_r=f(H) float kcc=(CC/70.f);//take Chroma into account...70 "middle" of chromaticity (arbitrary and simple), one can imagine other algorithme //reduct action for low chroma and increase action for high chroma valparam *= kcc; @@ -2598,6 +2713,17 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone // ready, fill lab for (int i=istart,ti=0; ir(i,j) = editIFloatTmpR[ti*TS+tj]; + editImgFloat->g(i,j) = editIFloatTmpG[ti*TS+tj]; + editImgFloat->b(i,j) = editIFloatTmpB[ti*TS+tj]; + } + else if (editWhatever) { + editWhatever->v(i,j) = editWhateverTmp[ti*TS+tj]; + } + float r = rtemp[ti*TS+tj]; float g = gtemp[ti*TS+tj]; float b = btemp[ti*TS+tj]; @@ -2633,9 +2759,19 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone } } } else { // black & white - // Auto channel mixer needs whole image, so we now copy to tmoImage and close the tiled processing + // Auto channel mixer needs whole image, so we now copy to tmpImage and close the tiled processing for (int i=istart,ti=0; ir(i,j) = editIFloatTmpR[ti*TS+tj]; + editImgFloat->g(i,j) = editIFloatTmpG[ti*TS+tj]; + editImgFloat->b(i,j) = editIFloatTmpB[ti*TS+tj]; + } + else if (editWhatever) { + editWhatever->v(i,j) = editWhateverTmp[ti*TS+tj]; + } + tmpImage->r(i,j) = rtemp[ti*TS+tj]; tmpImage->g(i,j) = gtemp[ti*TS+tj]; tmpImage->b(i,j) = btemp[ti*TS+tj]; @@ -2644,6 +2780,8 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone } } free(buffer); + if (editIFloatBuffer) free (editIFloatBuffer); + if (editWhateverBuffer) free (editWhateverBuffer); } //black and white @@ -2748,7 +2886,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltone float in[3], val[3]; #ifdef _OPENMP -#pragma omp patallel for schedule(dynamic, 5) +#pragma omp parallel for schedule(dynamic, 5) #endif for (int i=0; iv(i,j) = CLIP(tmpImage->r(i,j)/65535.f); // assuming that r=g=b + } + } + } + if (hasToneCurvebw2) { if (afterCurveMode==BlackWhiteParams::TC_MODE_STD_BW){ // Standard @@ -2872,66 +3021,108 @@ void ImProcFunctions::luminanceCurve (LabImage* lold, LabImage* lnew, LUTf & cur -void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* lnew, LUTf & acurve, LUTf & bcurve, LUTf & satcurve,LUTf & lhskcurve, LUTf & clcurve, LUTf & curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histCLurve, LUTu &histLLCurve, LUTu &histLCurve) { +void ImProcFunctions::chromiLuminanceCurve (EditBuffer *editBuffer, int pW, LabImage* lold, LabImage* lnew, LUTf & acurve, LUTf & bcurve, LUTf & satcurve,LUTf & lhskcurve, LUTf & clcurve, LUTf & curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histCLurve, LUTu &histLLCurve, LUTu &histLCurve) { int W = lold->W; int H = lold->H; // lhskcurve.dump("lh_curve"); //init Flatcurve for C=f(H) + + // NOTE: We're getting all 3 pointers here, but this function may not need them all, so one could optimize this + Imagefloat* editImgFloat = NULL; + LabImage* editLab = NULL; + PlanarWhateverData* editWhatever = NULL; + EditUniqueID editID = EUID_None; + if (editBuffer) { + editID = editBuffer->getEditID(); + if (editID != EUID_None) { + switch (editBuffer->getDataProvider()->getCurrSubscriber()->getEditBufferType()) { + case (BT_IMAGEFLOAT): + editImgFloat = editBuffer->getImgFloatBuffer(); + break; + case (BT_LABIMAGE): + editLab = editBuffer->getLabBuffer(); + break; + case (BT_SINGLEPLANE_FLOAT): + editWhatever = editBuffer->getSinglePlaneBuffer(); + break; + } + } + } + FlatCurve* chCurve = NULL;// curve C=f(H) bool chutili = false; if (params->labCurve.chromaticity > -100) { chCurve = new FlatCurve(params->labCurve.chcurve); - if (chCurve && !chCurve->isIdentity()) { - chutili=true; + if (!chCurve || chCurve->isIdentity()) { + if (chCurve) { + delete chCurve; + chCurve = NULL; + } }//do not use "Munsell" if Chcurve not used + else + chutili=true; } FlatCurve* lhCurve = NULL;//curve L=f(H) bool lhutili = false; if (params->labCurve.chromaticity > -100) { lhCurve = new FlatCurve(params->labCurve.lhcurve); - if (lhCurve && !lhCurve->isIdentity()) { + if (!lhCurve || lhCurve->isIdentity()) { + if (lhCurve) { + delete lhCurve; + lhCurve = NULL; + } + }//do not use "Munsell" if Chcurve not used + else lhutili=true; - } } FlatCurve* hhCurve = NULL;//curve H=f(H) bool hhutili = false; if (params->labCurve.chromaticity > -100) { hhCurve = new FlatCurve(params->labCurve.hhcurve); - if (hhCurve && !hhCurve->isIdentity()) { - hhutili=true; - } + if (!hhCurve || hhCurve->isIdentity()) { + if (hhCurve) { + delete hhCurve; + hhCurve = NULL; + } + }//do not use "Munsell" if Chcurve not used + else + hhutili = true; } - - LUTf dCcurve(65536,0); - LUTf dLcurve(65536,0); - - LUTu hist16Clad(65536); - LUTu hist16CLlad(65536); - LUTu hist16LLClad(65536); - + + LUTf dCcurve; + LUTf dLcurve; + + LUTu hist16Clad; + LUTu hist16CLlad; + LUTu hist16LLClad; + bool chrop=false; float val; //preparate for histograms CIECAM if(pW!=1){//only with improccoordinator + dCcurve(65536,0); + dLcurve(65536,0); + hist16Clad(65536); + hist16CLlad(65536); + hist16LLClad(65536); chrop = true; for (int i=0; i<48000; i++) { //# 32768*1.414 approximation maxi for chroma - val = (double)i / 47999.0; - dCcurve[i] = CLIPD(val); - } + val = (double)i / 47999.0; + dCcurve[i] = CLIPD(val); + } for (int i=0; i<65535; i++) { // a - val = (double)i / 65534.0; - dLcurve[i] = CLIPD(val); - - } - + val = (double)i / 65534.0; + dLcurve[i] = CLIPD(val); + } + hist16Clad.clear(); hist16CLlad.clear(); hist16LLClad.clear(); - + } #ifdef _DEBUG - MyTime t1e,t2e, t3e, t4e; + MyTime t1e,t2e; t1e.set(); // init variables to display Munsell corrections MunsellDebugInfo* MunsDebugInfo = new MunsellDebugInfo(); @@ -2957,7 +3148,6 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln int chromaticity = params->labCurve.chromaticity; bool bwToning = params->labCurve.chromaticity==-100 /*|| params->blackwhite.method=="Ch" */ || params->blackwhite.enabled; //if(chromaticity==-100) chromaticity==-99; - //if(bwToning) printf("OK bwto\n"); else printf("pas de bw\n"); bool LCredsk = params->labCurve.lcredsk; bool ccut = ccutili; bool clut = clcutili; @@ -3049,61 +3239,66 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln if (lhutili) { // L=f(H) float l_r;//Luminance Lab in 0..1 l_r = Lprov1/100.f; - { - double lr; - float khue=1.9f;//in reserve in case of! - float valparam = float((lhCurve->getVal(lr=Color::huelab_to_huehsv2(HH))-0.5f));//get l_r=f(H) - float valparamneg; - valparamneg=valparam; - float kcc=(CC/amountchroma);//take Chroma into account...40 "middle low" of chromaticity (arbitrary and simple), one can imagine other algorithme - //reduct action for low chroma and increase action for high chroma - valparam *= 2.f*kcc; - valparamneg*= kcc;//slightly different for negative - if(valparam > 0.f) { l_r = (1.f-valparam)*l_r+ valparam*(1.f-SQR(((SQR(1.f-min(l_r,1.0f))))));} - else {l_r *= (1.f+khue*valparamneg);}//for negative - } - - Lprov1=l_r*100.f; - - Chprov2 = sqrt(SQR(atmp/327.68f)+SQR(btmp/327.68f)); - //Gamut control especialy fot negative values slightly different of gamutlchonly - do { - inRGB=true; - float aprov1=Chprov2*sincosval.y; - float bprov1=Chprov2*sincosval.x; - - float fy = (0.00862069f *Lprov1 )+ 0.137932f; - float fx = (0.002f * aprov1) + fy; - float fz = fy - (0.005f * bprov1); + { + double lr; + float khue=1.9f;//in reserve in case of! + float valparam = float((lhCurve->getVal(lr=Color::huelab_to_huehsv2(HH))-0.5f));//get l_r=f(H) + float valparamneg; + valparamneg=valparam; + float kcc=(CC/amountchroma);//take Chroma into account...40 "middle low" of chromaticity (arbitrary and simple), one can imagine other algorithme + //reduce action for low chroma and increase action for high chroma + valparam *= 2.f*kcc; + valparamneg*= kcc;//slightly different for negative + if(valparam > 0.f) + l_r = (1.f-valparam)*l_r+ valparam*(1.f-SQR(((SQR(1.f-min(l_r,1.0f)))))); + else + //for negative + l_r *= (1.f+khue*valparamneg); + } - float x_ = 65535.0f * Color::f2xyz(fx)*Color::D50x; - float z_ = 65535.0f * Color::f2xyz(fz)*Color::D50z; - float y_=(Lprov1>Color::epskap) ? 65535.0*fy*fy*fy : 65535.0*Lprov1/Color::kappa; - float R,G,B; - Color::xyz2rgb(x_,y_,z_,R,G,B,wip); - if (R<0.0f || G<0.0f || B<0.0f) { - if(Lprov1 < 0.1f) Lprov1=0.1f; - Chprov2*=0.95f; - inRGB=false; - } - else - if (!highlight && (R>ClipLevel || G>ClipLevel || B>ClipLevel)) { + Lprov1=l_r*100.f; + + Chprov2 = sqrt(SQR(atmp/327.68f)+SQR(btmp/327.68f)); + //Gamut control especialy fot negative values slightly different of gamutlchonly + do { + inRGB=true; + float aprov1=Chprov2*sincosval.y; + float bprov1=Chprov2*sincosval.x; + + float fy = (0.00862069f *Lprov1 )+ 0.137932f; + float fx = (0.002f * aprov1) + fy; + float fz = fy - (0.005f * bprov1); + + float x_ = 65535.0f * Color::f2xyz(fx)*Color::D50x; + float z_ = 65535.0f * Color::f2xyz(fz)*Color::D50z; + float y_=(Lprov1>Color::epskap) ? 65535.0*fy*fy*fy : 65535.0*Lprov1/Color::kappa; + float R,G,B; + Color::xyz2rgb(x_,y_,z_,R,G,B,wip); + if (R<0.0f || G<0.0f || B<0.0f) { + if(Lprov1 < 0.1f) Lprov1=0.1f; + Chprov2*=0.95f; + inRGB=false; + } + else if (!highlight && (R>ClipLevel || G>ClipLevel || B>ClipLevel)) { if (Lprov1 > 99.999f) Lprov1 = 99.98f; Chprov2 *= 0.95f; inRGB = false; - } - } - while (!inRGB) ; - -// float2 sincosval = xsincosf(HH); - atmp=327.68f*Chprov2*sincosval.y; - btmp=327.68f*Chprov2*sincosval.x; + } + } + while (!inRGB) ; +// float2 sincosval = xsincosf(HH); + atmp=327.68f*Chprov2*sincosval.y; + btmp=327.68f*Chprov2*sincosval.x; } + + if (editID == EUID_Lab_LCurve) + editWhatever->v(i,j) = LIM01(Lprov1/100.f); + // calculate C=f(H) if (chutili) { - double hr; - float chparam = float((chCurve->getVal((hr=Color::huelab_to_huehsv2(HH)))-0.5f) * 2.0f);//get C=f(H) + double hr = Color::huelab_to_huehsv2(HH); + float chparam = float((chCurve->getVal(hr)-0.5f) * 2.0f);//get C=f(H) chromaChfactor=1.0f+chparam; } @@ -3112,23 +3307,22 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln if (hhutili) { // H=f(H) //hue Lab in -PI +PI - double hr; - float valparam = float((hhCurve->getVal(hr=Color::huelab_to_huehsv2(HH))-0.5f) * 1.7f) +HH;//get H=f(H) 1.7 optimisation ! - HH= valparam; + double hr; + float valparam = float((hhCurve->getVal(hr=Color::huelab_to_huehsv2(HH))-0.5f) * 1.7f) +HH;//get H=f(H) 1.7 optimisation ! + HH = valparam; } - //simulate very approximative gamut f(L) : with pyramid transition - float dred=55.0f;//C red value limit - if (Lprov1<25.0f) dred = 40.0f; - else if(Lprov1<30.0f) dred = 3.0f*Lprov1 -35.0f; - else if(Lprov1<70.0f) dred = 55.0f; - else if(Lprov1<75.0f) dred = -3.0f*Lprov1 +265.0f; - else dred = 40.0f; - // end pyramid - if(params->dirpyrDenoise.enabled && chromaticity ==0) chromaticity = 0.5f; + //simulate very approximative gamut f(L) : with pyramid transition + float dred=55.0f;//C red value limit + if (Lprov1<25.0f) dred = 40.0f; + else if(Lprov1<30.0f) dred = 3.0f*Lprov1 -35.0f; + else if(Lprov1<70.0f) dred = 55.0f; + else if(Lprov1<75.0f) dred = -3.0f*Lprov1 +265.0f; + else dred = 40.0f; + // end pyramid + if(params->dirpyrDenoise.enabled && chromaticity ==0) chromaticity = 0.5f; - if(!bwToning){ - float chromahist; + if(!bwToning){ float factorskin, factorsat, factor, factorskinext, interm; float scale = 100.0f/100.1f;//reduction in normal zone float scaleext=1.0f;//reduction in transition zone @@ -3144,9 +3338,11 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln deltaHH=protect_redh;//transition hue float chromapro = (chromaticity + 100.0f)/100.0f; if(chromapro>0.0) Color::scalered ( rstprotection, chromapro, 0.0, HH, deltaHH, scale, scaleext);//1.0 - if(chromapro>1.0) {interm=(chromapro-1.0f)*100.0f; + if(chromapro>1.0) { + interm=(chromapro-1.0f)*100.0f; factorskin= 1.0f+(interm*scale)/100.0f; - factorskinext=1.0f+(interm*scaleext)/100.0f;} + factorskinext=1.0f+(interm*scaleext)/100.0f; + } else { //factorskin= chromapro*scale; //factorskinext= chromapro*scaleext; @@ -3171,92 +3367,90 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln atmp *= factor; btmp *= factor; - } - - if (!bwToning && clut) { // begin C=f(L) - float factorskin,factorsat,factor,factorskinext,interm; - float chroma = sqrt(SQR(atmp)+SQR(btmp)+0.001f); - // float chromaCfactor=(clcurve[Lprov1*327.68f])/(Lprov1*327.68f);//apply C=f(L) - float chromaCfactor=(clcurve[Lprov2*327.68f])/(Lprov2*327.68f);//apply C=f(L) - float curf=0.7f;//empirical coeff because curve is more progressive - float scale = 100.0f/100.1f;//reduction in normal zone for curve C - float scaleext=1.0f;//reduction in transition zone for curve C - float protect_redcur,protect_redhcur;//perhaps the same value than protect_red and protect_redh - float deltaHH;//HH value transition for C curve - protect_redcur=curf*float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160==> curf =because curve is more progressive - if(protect_redcur < 20.0f) protect_redcur=20.0; // avoid too low value - if(protect_redcur > 180.0f) protect_redcur=180.0; // avoid too high value - protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0 ==> curf =because curve is more progressive - if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values - if(protect_redhcur>1.0f) protect_redhcur=1.0f;//avoid too big values + if (clut) { // begin C=f(L) + float factorskin,factorsat,factor,factorskinext,interm; + float chroma = sqrt(SQR(atmp)+SQR(btmp)+0.001f); + // float chromaCfactor=(clcurve[Lprov1*327.68f])/(Lprov1*327.68f);//apply C=f(L) + float chromaCfactor=(clcurve[Lprov2*327.68f])/(Lprov2*327.68f);//apply C=f(L) + float curf=0.7f;//empirical coeff because curve is more progressive + float scale = 100.0f/100.1f;//reduction in normal zone for curve C + float scaleext=1.0f;//reduction in transition zone for curve C + float protect_redcur,protect_redhcur;//perhaps the same value than protect_red and protect_redh + float deltaHH;//HH value transition for C curve + protect_redcur=curf*float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160==> curf =because curve is more progressive + if(protect_redcur < 20.0f) protect_redcur=20.0; // avoid too low value + if(protect_redcur > 180.0f) protect_redcur=180.0; // avoid too high value + protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0 ==> curf =because curve is more progressive + if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values + if(protect_redhcur>1.0f) protect_redhcur=1.0f;//avoid too big values - deltaHH=protect_redhcur;//transition hue - if(chromaCfactor>0.0) Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext);//1.0 - if(chromaCfactor>1.0) { - interm=(chromaCfactor-1.0f)*100.0f; - factorskin= 1.0f+(interm*scale)/100.0f; - factorskinext=1.0f+(interm*scaleext)/100.0f; - } - else { - factorskin= chromaCfactor; // +(1.0f-chromaCfactor)*scale; - factorskinext= chromaCfactor ; //+(1.0f-chromaCfactor)*scaleext; - } + deltaHH=protect_redhcur;//transition hue + if(chromaCfactor>0.0) Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext);//1.0 + if(chromaCfactor>1.0) { + interm=(chromaCfactor-1.0f)*100.0f; + factorskin= 1.0f+(interm*scale)/100.0f; + factorskinext=1.0f+(interm*scaleext)/100.0f; + } + else { + factorskin= chromaCfactor; // +(1.0f-chromaCfactor)*scale; + factorskinext= chromaCfactor ; //+(1.0f-chromaCfactor)*scaleext; + } - factorsat=chromaCfactor; - factor=factorsat; - Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); - atmp *= factor; - btmp *= factor; - } - // end C=f(L) - - // I have placed C=f(C) after all C treatments to assure maximum amplitude of "C" - if (!bwToning && ccut) { - float factorskin,factorsat,factor,factorskinext,interm; - float chroma = sqrt(SQR(atmp)+SQR(btmp)+0.001f); - float chromaCfactor=(satcurve[chroma*adjustr])/(chroma*adjustr);//apply C=f(C) - float curf=0.7f;//empirical coeff because curve is more progressive - float scale = 100.0f/100.1f;//reduction in normal zone for curve CC - float scaleext=1.0f;//reduction in transition zone for curve CC - float protect_redcur,protect_redhcur;//perhaps the same value than protect_red and protect_redh - float deltaHH;//HH value transition for CC curve - protect_redcur=curf*float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160==> curf =because curve is more progressive - if(protect_redcur < 20.0f) protect_redcur=20.0; // avoid too low value - if(protect_redcur > 180.0f) protect_redcur=180.0; // avoid too high value - protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0 ==> curf =because curve is more progressive - if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values - if(protect_redhcur>1.0f) protect_redhcur=1.0f;//avoid too big values + factorsat=chromaCfactor; + factor=factorsat; + Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); + atmp *= factor; + btmp *= factor; + } + // end C=f(L) - deltaHH=protect_redhcur;//transition hue - if(chromaCfactor>0.0) Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext);//1.0 - if(chromaCfactor>1.0) { - interm=(chromaCfactor-1.0f)*100.0f; - factorskin= 1.0f+(interm*scale)/100.0f; - factorskinext=1.0f+(interm*scaleext)/100.0f; - } - else { - //factorskin= chromaCfactor*scale; - //factorskinext=chromaCfactor*scaleext; - factorskin= chromaCfactor; // +(1.0f-chromaCfactor)*scale; - factorskinext= chromaCfactor ; //+(1.0f-chromaCfactor)*scaleext; + // I have placed C=f(C) after all C treatments to assure maximum amplitude of "C" + if (ccut) { + float factorskin,factorsat,factor,factorskinext,interm; + float chroma = sqrt(SQR(atmp)+SQR(btmp)+0.001f); + float chromaCfactor=(satcurve[chroma*adjustr])/(chroma*adjustr);//apply C=f(C) + float curf=0.7f;//empirical coeff because curve is more progressive + float scale = 100.0f/100.1f;//reduction in normal zone for curve CC + float scaleext=1.0f;//reduction in transition zone for curve CC + float protect_redcur,protect_redhcur;//perhaps the same value than protect_red and protect_redh + float deltaHH;//HH value transition for CC curve + protect_redcur=curf*float(protectRed);//default=60 chroma: one can put more or less if necessary...in 'option' 40...160==> curf =because curve is more progressive + if(protect_redcur < 20.0f) protect_redcur=20.0; // avoid too low value + if(protect_redcur > 180.0f) protect_redcur=180.0; // avoid too high value + protect_redhcur=curf*float(protectRedH);//default=0.4 rad : one can put more or less if necessary...in 'option' 0.2 ..1.0 ==> curf =because curve is more progressive + if(protect_redhcur<0.1f) protect_redhcur=0.1f;//avoid divide by 0 and negatives values + if(protect_redhcur>1.0f) protect_redhcur=1.0f;//avoid too big values - } + deltaHH=protect_redhcur;//transition hue + if(chromaCfactor>0.0) Color::scalered ( rstprotection, chromaCfactor, 0.0, HH, deltaHH, scale, scaleext);//1.0 + if(chromaCfactor>1.0) { + interm=(chromaCfactor-1.0f)*100.0f; + factorskin= 1.0f+(interm*scale)/100.0f; + factorskinext=1.0f+(interm*scaleext)/100.0f; + } + else { + //factorskin= chromaCfactor*scale; + //factorskinext=chromaCfactor*scaleext; + factorskin= chromaCfactor; // +(1.0f-chromaCfactor)*scale; + factorskinext= chromaCfactor ; //+(1.0f-chromaCfactor)*scaleext; - factorsat=chromaCfactor; - factor=factorsat; - Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); - atmp *= factor; - btmp *= factor; + } + + factorsat=chromaCfactor; + factor=factorsat; + Color::transitred ( HH, Chprov1, dred, factorskin, protect_redcur, factorskinext, deltaHH, factorsat, factor); + atmp *= factor; + btmp *= factor; + } } // end chroma C=f(C) - - //update histogram C - if(pW!=1){//only with improccoordinator - posp=CLIP((int)sqrt((atmp*atmp + btmp*btmp))); - hist16Clad[posp]++; - hist16CLlad[posp]++; - } + //update histogram C + if(pW!=1){//only with improccoordinator + posp=CLIP((int)sqrt((atmp*atmp + btmp*btmp))); + hist16Clad[posp]++; + hist16CLlad[posp]++; + } if (!bwToning) { //apply curve L=f(C) for skin and rd...but also for extended color ==> near green and blue (see 'curf') @@ -3272,11 +3466,11 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln float skbeg=-0.05f;//begin hue skin float skend=1.60f;//end hue skin - const float chrmin=50.0f;//to avoid artefact, because L curve is not a real curve for luminance + const float chrmin=50.0f;//to avoid artifact, because L curve is not a real curve for luminance float aa,bb; float zz=0.0f; float yy=0.0f; - if(Chprov1 < chrmin) yy=(Chprov1/chrmin)*(Chprov1/chrmin)*xx;else yy=xx;//avoid artefact for low C + if(Chprov1 < chrmin) yy=(Chprov1/chrmin)*(Chprov1/chrmin)*xx;else yy=xx;//avoid artifact for low C if(!LCredsk) {skbeg=-3.1415; skend=3.14159; deltaHH=0.001f;} if(HH>skbeg && HH < skend ) zz=yy; else if(HH>skbeg-deltaHH && HH<=skbeg) {aa=yy/deltaHH;bb=-aa*(skbeg-deltaHH); zz=aa*HH+bb;}//transition @@ -3372,7 +3566,9 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln } } } + } // end of parallelization + //update histogram C with data chromaticity and not with CC curve if(pW!=1){//only with improccoordinator for (int i=0; i<=48000; i++) {//32768*1.414 + ... @@ -3380,7 +3576,7 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln float hval = dCcurve[i]; int hi = (int)(255.0*CLIPD(hval)); // histCCurve[hi] += hist16Clad[i] ; - histCLurve[hi] += hist16CLlad[i] ; + histCLurve[hi] += hist16CLlad[i] ; } } //update histogram L with data luminance @@ -3389,7 +3585,7 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln float hlval = dLcurve[i]; int hli = (int)(255.0*CLIPD(hlval)); histLLCurve[hli] += hist16LLClad[i] ; - histLCurve[hli] += hist16LLClad[i] ; + histLCurve[hli] += hist16LLClad[i] ; } } @@ -3398,8 +3594,8 @@ void ImProcFunctions::chromiLuminanceCurve (int pW, LabImage* lold, LabImage* ln #ifdef _DEBUG if (settings->verbose) { - t3e.set(); - printf("Color::AllMunsellLch (correction performed in %d usec):\n", t3e.etime(t1e)); + t2e.set(); + printf("Color::AllMunsellLch (correction performed in %d usec):\n", t2e.etime(t1e)); printf(" Munsell chrominance: MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad dep=%i\n", MunsDebugInfo->maxdhue[0], MunsDebugInfo->maxdhue[1], MunsDebugInfo->maxdhue[2], MunsDebugInfo->maxdhue[3], MunsDebugInfo->depass); printf(" Munsell luminance : MaxBP=%1.2frad MaxRY=%1.2frad MaxGY=%1.2frad MaxRP=%1.2frad dep=%i\n", MunsDebugInfo->maxdhuelum[0], MunsDebugInfo->maxdhuelum[1], MunsDebugInfo->maxdhuelum[2], MunsDebugInfo->maxdhuelum[3], MunsDebugInfo->depassLum); } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index e496275e0..8196defde 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -33,6 +33,7 @@ #include "curves.h" #include #include "cplx_wavelet_dec.h" +#include "editbuffer.h" namespace rtengine { @@ -194,10 +195,10 @@ class ImProcFunctions { bool needsPCVignetting (); void firstAnalysis (Imagefloat* working, const ProcParams* params, LUTu & vhist16, double gamma); - void rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, + void rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *editBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1,const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob); - void rgbProc (Imagefloat* working, LabImage* lab, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, + void rgbProc (Imagefloat* working, LabImage* lab, EditBuffer *editBuffer, LUTf & hltonecurve, LUTf & shtonecurve, LUTf & tonecurve, SHMap* shmap, int sat, LUTf & rCurve, LUTf & gCurve, LUTf & bCurve, const ToneCurve & customToneCurve1, const ToneCurve & customToneCurve2, const ToneCurve & customToneCurvebw1,const ToneCurve & customToneCurvebw2, double &rrm, double &ggm, double &bbm, float &autor, float &autog, float &autob, double expcomp, int hlcompr, int hlcomprthresh); @@ -208,7 +209,7 @@ class ImProcFunctions { void ciecam_02 (CieImage* ncie, double adap, int begh, int endh, int pW, int pwb, LabImage* lab, const ProcParams* params, const ColorAppearance & customColCurve1, const ColorAppearance & customColCurve, const ColorAppearance & customColCurve3, LUTu &histLCAM, LUTu &histCCAM, LUTf & CAMBrightCurveJ, LUTf & CAMBrightCurveQ, float &mean, int Iterates, int scale, float** buffer, bool execsharp, double &d); - void chromiLuminanceCurve (int pW, LabImage* lold, LabImage* lnew, LUTf &acurve, LUTf &bcurve, LUTf & satcurve,LUTf & satclcurve, LUTf &clcurve, LUTf &curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histCLurve, LUTu &histLCurve, LUTu &histLurve); + void chromiLuminanceCurve (EditBuffer *editBuffer, int pW, LabImage* lold, LabImage* lnew, LUTf &acurve, LUTf &bcurve, LUTf & satcurve,LUTf & satclcurve, LUTf &clcurve, LUTf &curve, bool utili, bool autili, bool butili, bool ccutili, bool cclutili, bool clcutili, LUTu &histCCurve, LUTu &histCLurve, LUTu &histLCurve, LUTu &histLurve); void vibrance (LabImage* lab);//Jacques' vibrance void colorCurve (LabImage* lold, LabImage* lnew); void sharpening (LabImage* lab, float** buffer); diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index b42760782..c6efa21de 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -107,9 +107,9 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) { int H = lab->H; unsigned char * data = image->data; -#ifdef _OPENMP -#pragma omp parallel for schedule(static) firstprivate(lab, data, W, H) if (multiThread) -#endif +//#ifdef _OPENMP +//#pragma omp parallel for schedule(static) firstprivate(lab, data, W, H) if (multiThread) +//#endif for (int i=0; iL[i]; float* ra = lab->a[i]; diff --git a/rtengine/labimage.cc b/rtengine/labimage.cc index 743582984..90e8e3da9 100644 --- a/rtengine/labimage.cc +++ b/rtengine/labimage.cc @@ -32,7 +32,28 @@ LabImage::~LabImage () { } void LabImage::CopyFrom(LabImage *Img){ - memcpy(data, Img->data, W*H*3*sizeof(float)); + memcpy(data, Img->data, W*H*3*sizeof(float)); +} + +void LabImage::getPipetteData (float &v1, float &v2, float &v3, int posX, int posY, int squareSize) { + float accumulator_L = 0.f; + float accumulator_a = 0.f; + float accumulator_b = 0.f; + unsigned long int n = 0; + int halfSquare = squareSize/2; + for (int iy=posY-halfSquare; iy=0 && iy>=0 && ix inline const _Tp LIM01(const _Tp& a) { - return std::max(_Tp(1),std::min(a,_Tp(0))); + return std::max(_Tp(0),std::min(a,_Tp(1))); } template diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index a973703a3..a10f4bac0 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -39,6 +39,7 @@ * */ + class EditDataProvider; namespace rtengine { @@ -329,8 +330,10 @@ namespace rtengine { * @return the height of the preview image */ virtual int getPreviewHeight () =0; - /** Creates and returns a Crop instance that acts as a window on the image */ - virtual DetailedCrop* createCrop () =0; + /** Creates and returns a Crop instance that acts as a window on the image + * @param editDataProvider pointer to the EditDataProvider that communicates with the EditSubscriber + * @return a pointer to the Crop object that handles the image data trough its own pipeline */ + virtual DetailedCrop* createCrop (::EditDataProvider *editDataProvider) =0; virtual bool getAutoWB (double& temp, double& green, double equal) =0; virtual void getCamWB (double& temp, double& green) =0; diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 2af226f54..ac03a9952 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -763,7 +763,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei double rrm, ggm, bbm; float autor, autog, autob; autor = autog = autob = -9000.f; // This will ask to compute the "auto" values for the B&W tool - ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2,rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh); + ipf.rgbProc (baseImg, labView, NULL, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2,rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh); if (shmap) delete shmap; @@ -774,7 +774,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei for (int j=0; jL[i][j])))]++; hist16C[CLIP((int)sqrt(labView->a[i][j]*labView->a[i][j] + labView->b[i][j]*labView->b[i][j]))]++; - } + } // luminance processing // ipf.EPDToneMap(labView,0,6); @@ -785,17 +785,17 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei bool cclutili=false; bool clcutili=false; - CurveFactory::complexLCurve (params.labCurve.brightness, params.labCurve.contrast, params.labCurve.lcurve, + CurveFactory::complexLCurve (params.labCurve.brightness, params.labCurve.contrast, params.labCurve.lcurve, hist16, hist16, curve, dummy, 16, utili); CurveFactory::curveCL(clcutili, params.labCurve.clcurve, clcurve, hist16C, dummy, 16); CurveFactory::complexsgnCurve (autili, butili, ccutili, cclutili,params.labCurve.chromaticity, params.labCurve.rstprotection, params.labCurve.acurve, params.labCurve.bcurve,params.labCurve.cccurve,params.labCurve.lccurve, curve1, curve2, satcurve,lhskcurve, - hist16C, hist16C, dummy, dummy, + hist16C, hist16C, dummy, dummy, 16); //ipf.luminanceCurve (labView, labView, curve); - ipf.chromiLuminanceCurve (1,labView, labView, curve1, curve2, satcurve,lhskcurve, clcurve, curve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy); + ipf.chromiLuminanceCurve (NULL, 1,labView, labView, curve1, curve2, satcurve,lhskcurve, clcurve, curve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy); ipf.vibrance(labView); int begh = 0, endh = labView->H; diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index fe6faf484..b56ea840e 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -217,7 +217,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p double rrm, ggm, bbm; float autor, autog, autob; autor = -9000.f; // This will ask to compute the "auto" values for the B&W tool (have to be inferior to -5000) - ipf.rgbProc (baseImg, labView, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2,customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh); + ipf.rgbProc (baseImg, labView, NULL, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, customToneCurve1, customToneCurve2,customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh); if (settings->verbose) printf("Output image / Auto B&W coefs: R=%.2f G=%.2f B=%.2f\n", autor, autog, autob); @@ -277,7 +277,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p hist16C, hist16C, dummy,dummy, 1); - ipf.chromiLuminanceCurve (1,labView, labView, curve1, curve2, satcurve,lhskcurve,clcurve, lumacurve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy); + ipf.chromiLuminanceCurve (NULL, 1,labView, labView, curve1, curve2, satcurve,lhskcurve,clcurve, lumacurve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy); if((params.colorappearance.enabled && !params.colorappearance.tonecie) || (!params.colorappearance.enabled))ipf.EPDToneMap(labView,5,1); diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 862ccab98..c88754039 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -4,7 +4,7 @@ set (BASESOURCEFILES exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc ilabel.cc thumbbrowserbase.cc adjuster.cc filebrowserentry.cc filebrowser.cc filethumbnailbuttonset.cc cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc - clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc + clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc coloredbar.cc edit.cc coarsepanel.cc cacorrection.cc chmixer.cc blackwhite.cc resize.cc icmpanel.cc crop.cc shadowshighlights.cc impulsedenoise.cc dirpyrdenoise.cc epd.cc diff --git a/rtgui/blackwhite.cc b/rtgui/blackwhite.cc index f9bcab58a..34c25835f 100644 --- a/rtgui/blackwhite.cc +++ b/rtgui/blackwhite.cc @@ -22,6 +22,7 @@ #include #include #include "guiutils.h" +#include "edit.h" using namespace rtengine; using namespace rtengine::procparams; @@ -81,6 +82,7 @@ BlackWhite::BlackWhite (): Gtk::VBox(), FoldableToolPanel(this) { luminanceCEG = new CurveEditorGroup (options.lastBWCurvesDir, M("TP_BWMIX_CHANNEL")); luminanceCEG->setCurveListener (this); luminanceCurve = static_cast(luminanceCEG->addCurve(CT_Flat, M("TP_BWMIX_VAL"))); + luminanceCurve->setEditID(EUID_BlackWhiteLuminance, BT_SINGLEPLANE_FLOAT); luminanceCurve->setBottomBarBgGradient(bottomMilestones); luminanceCurve->setCurveColorProvider(this, 3); luminanceCurve->setTooltip(M("TP_BWMIX_CURVEEDITOR_LH_TOOLTIP")); @@ -323,6 +325,7 @@ BlackWhite::BlackWhite (): Gtk::VBox(), FoldableToolPanel(this) { beforeCurveCEG->setCurveListener (this); beforeCurve = static_cast(beforeCurveCEG->addCurve(CT_Diagonal, "", beforeCurveMode)); + beforeCurve->setEditID(EUID_BlackWhiteBeforeCurve, BT_IMAGEFLOAT); beforeCurve->setBottomBarBgGradient(bottomMilestonesbw); beforeCurve->setLeftBarBgGradient(bottomMilestonesbw); beforeCurve->setTooltip(M("TP_BWMIX_CURVEEDITOR_BEFORE_TOOLTIP")); @@ -346,6 +349,7 @@ BlackWhite::BlackWhite (): Gtk::VBox(), FoldableToolPanel(this) { // afterCurve = static_cast(afterCurveCEG->addCurve(CT_Diagonal, "", afterCurveMode)); afterCurve = static_cast(afterCurveCEG->addCurve(CT_Diagonal, "")); + afterCurve->setEditID(EUID_BlackWhiteAfterCurve, BT_SINGLEPLANE_FLOAT); afterCurve->setBottomBarBgGradient(bottomMilestonesbw); afterCurve->setLeftBarBgGradient(bottomMilestonesbw); afterCurve->setTooltip(M("TP_BWMIX_CURVEEDITOR_AFTER_TOOLTIP")); @@ -1108,6 +1112,11 @@ void BlackWhite::autoOpenCurve () { beforeCurve->openIfNonlinear(); afterCurve->openIfNonlinear(); } +void BlackWhite::setEditProvider (EditDataProvider *provider) { + luminanceCurve->setEditProvider(provider); + beforeCurve->setEditProvider(provider); + afterCurve->setEditProvider(provider); +} void BlackWhite::setAdjusterBehavior (bool bwadd, bool bwgadd) { diff --git a/rtgui/blackwhite.h b/rtgui/blackwhite.h index 804a5f569..de2fcd245 100644 --- a/rtgui/blackwhite.h +++ b/rtgui/blackwhite.h @@ -104,6 +104,7 @@ class BlackWhite : public Gtk::VBox, public AdjusterListener, public FoldableToo void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); void setBatchMode (bool batchMode); void autoOpenCurve (); + void setEditProvider (EditDataProvider *provider); void autoch_toggled (); void neutral_pressed (); diff --git a/rtgui/crop.cc b/rtgui/crop.cc index f7f5530c1..efc5aece0 100644 --- a/rtgui/crop.cc +++ b/rtgui/crop.cc @@ -23,7 +23,6 @@ using namespace rtengine; using namespace rtengine::procparams; -extern Glib::ustring argv0; extern Options options; class RefreshSpinHelper { @@ -433,6 +432,10 @@ void Crop::trim (ProcParams* pp, int ow, int oh) { } } +bool Crop::inImageArea (int x, int y) { + return x>=0 && x=0 && y #include "guiutils.h" +#include "cropwindow.h" +#include "../rtengine/dcrop.h" #include "../rtengine/refreshmap.h" using namespace rtengine; -CropHandler::CropHandler () +CropHandler::CropHandler () : zoom(10), cx(0), cy(0), cw(0), ch(0), cropX(0), cropY(0), cropW(0), cropH(0), enabled(false), cropimg(NULL), cropimgtrue(NULL), ipc(NULL), crop(NULL), listener(NULL) { @@ -53,7 +55,11 @@ CropHandler::~CropHandler () { delete chi; cimg.unlock (); } - + +void CropHandler::setEditSubscriber (EditSubscriber* newSubscriber) { + (static_cast(crop))->setEditSubscriber(newSubscriber); +} + void CropHandler::newImage (StagedImageProcessor* ipc_) { ipc = ipc_; @@ -61,9 +67,13 @@ void CropHandler::newImage (StagedImageProcessor* ipc_) { cy = 0; if (!ipc) - return; - - crop = ipc->createCrop (); + return; + + EditDataProvider *editDataProvider = NULL; + CropWindow *cropWin = listener ? static_cast(listener) : NULL; + if (cropWin) + editDataProvider = cropWin->getImageArea(); + crop = ipc->createCrop (editDataProvider); ipc->setSizeListener (this); crop->setListener (enabled ? this : NULL); initial = true; diff --git a/rtgui/crophandler.h b/rtgui/crophandler.h index cd0c7406d..395e3034d 100644 --- a/rtgui/crophandler.h +++ b/rtgui/crophandler.h @@ -21,11 +21,13 @@ #include "../rtengine/rtengine.h" #include "threadutils.h" +#include "edit.h" #include class CropHandlerListener { public: + virtual ~CropHandlerListener() {} virtual void cropImageUpdated () {} virtual void cropWindowChanged () {} virtual void initialImageArrived () {} @@ -78,6 +80,7 @@ class CropHandler : public rtengine::DetailedCropListener, public rtengine::Size ~CropHandler (); void setCropHandlerListener (CropHandlerListener* l) { listener = l; } + void setEditSubscriber (EditSubscriber* newSubscriber); void newImage (rtengine::StagedImageProcessor* ipc_); void setZoom (int z, int centerx=-1, int centery=-1); @@ -92,6 +95,8 @@ class CropHandler : public rtengine::DetailedCropListener, public rtengine::Size void setEnabled (bool e); bool getEnabled (); + rtengine::DetailedCrop* getCrop() { return crop; } + // DetailedCropListener interface void setDetailedCrop (rtengine::IImage8* im, rtengine::IImage8* imworking,rtengine::procparams::ColorManagementParams cmp, rtengine::procparams::CropParams cp, int cx, int cy, int cw, int ch, int skip); diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index e4fe21db8..69b2135ad 100755 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -27,6 +27,7 @@ #include "cursormanager.h" #include "../rtengine/safegtk.h" #include "../rtengine/rt_math.h" +#include "../rtengine/dcrop.h" using namespace rtengine; @@ -104,20 +105,14 @@ CropWindow::CropWindow (ImageArea* parent, rtengine::StagedImageProcessor* ipc_, titleHeight = bsh; minWidth = bsw + iw + 2*sideBorderWidth; - - cropHandler.newImage (ipc_); - cropHandler.setEnabled (true); cropHandler.setCropHandlerListener (this); - + cropHandler.newImage (ipc_); + cropHandler.setEnabled (true); state = SNormal; } -CropWindow::~CropWindow () { - -} - void CropWindow::setPosition (int x, int y) { if (y<0) @@ -203,7 +198,6 @@ void CropWindow::setSize (int w, int h, bool norefresh) { imgAreaW = width; imgAreaH = height; } - if (!norefresh) cropHandler.setWSize (imgAreaW, imgAreaH); @@ -227,7 +221,20 @@ bool CropWindow::isInside (int x, int y) { return x>=xpos && x=ypos && ygetCurrSubscriber(); + if (state==SNormal && subscriber && subscriber->getEditingType()==ET_PIPETTE) { + printf("Values\n"); + iarea->pipetteVal[0] = iarea->pipetteVal[1] = iarea->pipetteVal[2] = -1.f; + if (subscriber->mouseOver(0)) { + printf("Redraw\n"); + iarea->redraw(); + } + } +} + void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { iarea->grabFocus (this); @@ -236,7 +243,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { if (fitZoom) { state = SNormal; zoomVersion = exposeVersion; - translateCoord (x, y, action_x, action_y); + screenCoordToImage (x, y, action_x, action_y); changeZoom (ZOOM11INDEX, true, action_x, action_y); fitZoom = false; } @@ -335,6 +342,13 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { action_y = 0; press_x = x; press_y = y; + + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); + + if (button==1 && editSubscriber && cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && ( (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK)) || (editSubscriber->getEditingType() == ET_OBJECTS && iarea->object)) ) { + editSubscriber->button1Pressed(bstate); + state=SEditDrag; + } } else if (iarea->getToolMode () == TMStraighten) { state = SRotateSelecting; @@ -345,12 +359,12 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { rot_deg = 0; } else if (iarea->getToolMode () == TMSpotWB) { - translateCoord (x, y, action_x, action_y); + screenCoordToImage (x, y, action_x, action_y); iarea->spotWBSelected (action_x, action_y); } else if (iarea->getToolMode () == TMCropSelect && cropgl) { state = SCropSelecting; - translateCoord (x, y, press_x, press_y); + screenCoordToImage (x, y, press_x, press_y); cropHandler.cropParams.enabled = true; cropHandler.cropParams.x = press_x; cropHandler.cropParams.y = press_y; @@ -359,12 +373,15 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { } } if (button==3) { + iarea->pipetteVal[0] = iarea->pipetteVal[1] = iarea->pipetteVal[2] = -1.f; + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); + if (editSubscriber && editSubscriber->getEditingType()==ET_PIPETTE) + editSubscriber->mouseOver(0); state = SNormal; iarea->setToolHand (); - if (pmhlistener) { + if (pmhlistener) { pmhlistener->toggleFreeze(); - } - + } } iarea->redraw (); updateCursor (x, y); @@ -372,6 +389,8 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) { + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); + if (state==SCropWinResize) { setSize (press_x + x - action_x, press_y + y - action_y); state = SNormal; @@ -395,6 +414,15 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) { observedCropWin->remoteMoveReady (); state = SNormal; } + else if (state==SEditDrag) { + editSubscriber->button1Released(); + iarea->object = 0; + iarea->deltaImage.set(0, 0); + iarea->deltaScreen.set(0, 0); + iarea->deltaPrevImage.set(0, 0); + iarea->deltaPrevScreen.set(0, 0); + state = SNormal; + } if (cropgl && (state==SCropSelecting || state==SResizeH1 || state==SResizeH2 || state==SResizeW1 || state==SResizeW2 || state==SResizeTL || state==SResizeTR || state==SResizeBL || state==SResizeBR || state==SCropMove)) { cropgl->cropManipReady (); @@ -412,7 +440,9 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) { updateCursor (x, y); } -void CropWindow::pointerMoved (int x, int y) { +void CropWindow::pointerMoved (int bstate, int x, int y) { + + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); if (state==SCropWinMove) { setPosition (press_x + x - action_x, press_y + y - action_y); @@ -429,7 +459,7 @@ void CropWindow::pointerMoved (int x, int y) { double factor = options.panAccelFactor == 1 ? 1.0 : options.panAccelFactor * zoomSteps[cropZoom].zoom; // never move the preview slower than the cursor if (factor < 1.0) - factor = 1.0; + factor = 1.0; action_x = (press_x - x) / zoomSteps[cropZoom].zoom * factor; action_y = (press_y - y) / zoomSteps[cropZoom].zoom * factor; for (std::list::iterator i=listeners.begin(); i!=listeners.end(); i++) @@ -509,7 +539,7 @@ void CropWindow::pointerMoved (int x, int y) { iarea->redraw (); } else if (state==SCropSelecting && cropgl) { - translateCoord (x, y, action_x, action_y); + screenCoordToImage (x, y, action_x, action_y); int cx1 = press_x, cy1 = press_y; int cx2 = action_x, cy2 = action_y; cropgl->cropResized (cx1, cy1, cx2, cy2); @@ -535,6 +565,51 @@ void CropWindow::pointerMoved (int x, int y) { observedCropWin->remoteMove ((x - press_x)/zoomSteps[cropZoom].zoom, (y - press_y)/zoomSteps[cropZoom].zoom); iarea->redraw (); } + else if (editSubscriber) { + rtengine::Crop* crop = static_cast(cropHandler.getCrop()); + if (state==SNormal) { + Coord imgPos; + action_x = x; + action_y = y; + screenCoordToImage (x, y, imgPos.x, imgPos.y); + + iarea->posImage.set(imgPos.x, imgPos.y); + iarea->posScreen.set(x, y); + + if (editSubscriber->getEditingType()==ET_PIPETTE) { + Coord cropPos; + screenCoordToCropBuffer(x, y, cropPos.x, cropPos.y); + iarea->object = onArea (CropImage, x, y) ? 1 : 0; + //iarea->object = cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) ? 1 : 0; + if (iarea->object) { + crop->getPipetteData(iarea->pipetteVal, cropPos.x, cropPos.y, iarea->getPipetteRectSize()); + //printf("PipetteData: %.3f %.3f %.3f\n", iarea->pipetteVal[0], iarea->pipetteVal[1], iarea->pipetteVal[2]); + } + else { + iarea->pipetteVal[0] = iarea->pipetteVal[1] = iarea->pipetteVal[2] = -1.f; + } + } + + if (editSubscriber->mouseOver(bstate)) + iarea->redraw (); + } + else if (state==SEditDrag) { + Coord oldPosImage = iarea->posImage; + Coord oldPosScreen = iarea->posScreen; + screenCoordToImage (x, y, action_x, action_y); + + Coord currPos(action_x, action_y); + iarea->deltaImage = currPos - iarea->posImage; + iarea->deltaPrevImage = currPos - oldPosImage; + + currPos.set(x, y); + iarea->deltaScreen = currPos - iarea->posScreen; + iarea->deltaPrevScreen = currPos - oldPosScreen; + + if (editSubscriber->drag(bstate)) + iarea->redraw (); + } + } updateCursor (x, y); bool oRA = onArea (CropResize, x, y); @@ -545,45 +620,43 @@ void CropWindow::pointerMoved (int x, int y) { if (decorated) buttonSet.motionNotify (x, y); - - if (pmlistener) { - int mx, my; - translateCoord (x, y, mx, my); - if (!onArea (CropImage, x, y) || !cropHandler.cropPixbuf) { + + if (pmlistener) { + int mx, my; + screenCoordToImage (x, y, mx, my); + if (!onArea (CropImage, x, y) || !cropHandler.cropPixbuf) { cropHandler.getFullImageSize(mx,my); - pmlistener->pointerMoved (false, cropHandler.colorParams.working, mx, my, -1, -1, -1); + pmlistener->pointerMoved (false, cropHandler.colorParams.working, mx, my, -1, -1, -1); if (pmhlistener) pmhlistener->pointerMoved (false, cropHandler.colorParams.working, mx, my, -1, -1, -1); - } - else { - /*MyMutex::MyLock lock(cropHandler.cimg); + } + else { + /*MyMutex::MyLock lock(cropHandler.cimg); - int vx = x - xpos - imgX; - int vy = y - ypos - imgY; - guint8* pix = cropHandler.cropPixbuf->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3; - if (vx < cropHandler.cropPixbuf->get_width() && vy < cropHandler.cropPixbuf->get_height()) - pmlistener->pointerMoved (true, mx, my, pix[0], pix[1], pix[2]); - - */ + int vx = x - xpos - imgX; + int vy = y - ypos - imgY; + guint8* pix = cropHandler.cropPixbuf->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3; + if (vx < cropHandler.cropPixbuf->get_width() && vy < cropHandler.cropPixbuf->get_height()) + pmlistener->pointerMoved (true, mx, my, pix[0], pix[1], pix[2]); - cropHandler.cimg.lock (); - int vx = x - xpos - imgX; - int vy = y - ypos - imgY; -// guint8* pix = cropHandler.cropPixbuf->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3; -// if (vx < cropHandler.cropPixbuf->get_width() && vy < cropHandler.cropPixbuf->get_height()) -// pmlistener->pointerMoved (true, mx, my, pix[0], pix[1], pix[2]); - int imwidth = cropHandler.cropPixbuf->get_width(); - int imheight = cropHandler.cropPixbuf->get_height(); - guint8* pix = cropHandler.cropPixbuftrue->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3; - if (vx < imwidth && vy < imheight) { - pmlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); - if (pmhlistener) { - pmhlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); - } - } - cropHandler.cimg.unlock (); - - } - } + */ + + cropHandler.cimg.lock (); + int vx = x - xpos - imgX; + int vy = y - ypos - imgY; +// guint8* pix = cropHandler.cropPixbuf->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3; +// if (vx < cropHandler.cropPixbuf->get_width() && vy < cropHandler.cropPixbuf->get_height()) +// pmlistener->pointerMoved (true, mx, my, pix[0], pix[1], pix[2]); + int imwidth = cropHandler.cropPixbuf->get_width(); + int imheight = cropHandler.cropPixbuf->get_height(); + guint8* pix = cropHandler.cropPixbuftrue->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3; + if (vx < imwidth && vy < imheight) { + pmlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); + if (pmhlistener) + pmhlistener->pointerMoved (true, cropHandler.colorParams.working, mx, my, pix[0], pix[1], pix[2]); + } + cropHandler.cimg.unlock (); + } + } } bool CropWindow::onArea (CursorArea a, int x, int y) { @@ -602,7 +675,7 @@ bool CropWindow::onArea (CursorArea a, int x, int y) { (x>=xpos+imgAreaX && y>=ypos+imgAreaY && x=xpos+imgX && y>=ypos+imgY && x=cropHandler.cropParams.y-CROPRESIZEBORDER && y1<=cropHandler.cropParams.y+CROPRESIZEBORDER && @@ -611,7 +684,7 @@ bool CropWindow::onArea (CursorArea a, int x, int y) { x1<=cropHandler.cropParams.x+CROPRESIZEBORDER && x>=xpos+imgX; case CropTopRight: - translateCoord (x, y, x1, y1); + screenCoordToImage (x, y, x1, y1); return cropHandler.cropParams.enabled && y1>=cropHandler.cropParams.y-CROPRESIZEBORDER && y1<=cropHandler.cropParams.y+CROPRESIZEBORDER && @@ -620,7 +693,7 @@ bool CropWindow::onArea (CursorArea a, int x, int y) { x1<=cropHandler.cropParams.x+cropHandler.cropParams.w-1+CROPRESIZEBORDER && x=cropHandler.cropParams.y+cropHandler.cropParams.h-1-CROPRESIZEBORDER && y1<=cropHandler.cropParams.y+cropHandler.cropParams.h-1+CROPRESIZEBORDER && @@ -629,7 +702,7 @@ bool CropWindow::onArea (CursorArea a, int x, int y) { x1<=cropHandler.cropParams.x+CROPRESIZEBORDER && x>=xpos+imgX; case CropBottomRight: - translateCoord (x, y, x1, y1); + screenCoordToImage (x, y, x1, y1); return cropHandler.cropParams.enabled && y1>=cropHandler.cropParams.y+cropHandler.cropParams.h-1-CROPRESIZEBORDER && y1<=cropHandler.cropParams.y+cropHandler.cropParams.h-1+CROPRESIZEBORDER && @@ -638,7 +711,7 @@ bool CropWindow::onArea (CursorArea a, int x, int y) { x1<=cropHandler.cropParams.x+cropHandler.cropParams.w-1+CROPRESIZEBORDER && xcropHandler.cropParams.x+CROPRESIZEBORDER && x1=ypos+imgY; case CropBottom: - translateCoord (x, y, x1, y1); + screenCoordToImage (x, y, x1, y1); return cropHandler.cropParams.enabled && x1>cropHandler.cropParams.x+CROPRESIZEBORDER && x1cropHandler.cropParams.y+CROPRESIZEBORDER && y1=xpos+imgX; case CropRight: - translateCoord (x, y, x1, y1); + screenCoordToImage (x, y, x1, y1); return cropHandler.cropParams.enabled && y1>cropHandler.cropParams.y+CROPRESIZEBORDER && y1cropHandler.cropParams.y && y1getCurrSubscriber(); ToolMode tm = iarea->getToolMode (); - + if (state==SNormal) { if (onArea (CropWinButtons, x, y)) cursorManager.setCursor (iarea->get_window(), CSArrow); @@ -711,7 +785,7 @@ void CropWindow::updateCursor (int x, int y) { cursorManager.setCursor (iarea->get_window(), CSResizeHeight); else if (tm==TMHand && (onArea (CropLeft, x, y) || onArea (CropRight, x, y))) cursorManager.setCursor (iarea->get_window(), CSResizeWidth); - else if (onArea (CropImage, x, y)) { + else if (onArea (CropImage, x, y)) { if (tm==TMHand) { if (onArea (CropObserved, x, y)) cursorManager.setCursor (iarea->get_window(), CSMove); @@ -725,6 +799,9 @@ void CropWindow::updateCursor (int x, int y) { else if (tm==TMStraighten) cursorManager.setCursor (iarea->get_window(), CSStraighten); } + else if (tm==TMHand && editSubscriber) { + cursorManager.setCursor (iarea->get_window(), iarea->getCursor( static_cast(cropHandler.getCrop())->getObjectID(iarea->posImage ))); + } else cursorManager.setCursor (iarea->get_window(), CSArrow); } @@ -809,14 +886,14 @@ void CropWindow::expose (Cairo::RefPtr cr) { exposeVersion++; // PERFORMANCE BOTTLENECK STARTS HERE //t3.set (); - bool showcs = iarea->indClippedPanel->showClippedShadows(); - bool showch = iarea->indClippedPanel->showClippedHighlights(); - bool showR = iarea->previewModePanel->showR(); // will show clipping if R channel is clipped - bool showG = iarea->previewModePanel->showG(); // will show clipping if G channel is clipped - bool showB = iarea->previewModePanel->showB(); // will show clipping if B channel is clipped - bool showL = iarea->previewModePanel->showL(); // will show clipping if L value is clipped - bool showFocusMask = iarea->previewModePanel->showFocusMask(); - bool showclippedAny = (!showR && !showG && !showB && !showL); // will show clipping if any of RGB chanels is clipped + bool showcs = iarea->indClippedPanel->showClippedShadows(); + bool showch = iarea->indClippedPanel->showClippedHighlights(); + bool showR = iarea->previewModePanel->showR(); // will show clipping if R channel is clipped + bool showG = iarea->previewModePanel->showG(); // will show clipping if G channel is clipped + bool showB = iarea->previewModePanel->showB(); // will show clipping if B channel is clipped + bool showL = iarea->previewModePanel->showL(); // will show clipping if L value is clipped + bool showFocusMask = iarea->previewModePanel->showFocusMask(); + bool showclippedAny = (!showR && !showG && !showB && !showL); // will show clipping if any of RGB chanels is clipped // While the Right-side ALT is pressed, auto-enable highlight and shadow clipping indicators // TODO: Add linux/MacOS specific functions for alternative @@ -826,9 +903,9 @@ void CropWindow::expose (Cairo::RefPtr cr) { } #endif - if (showcs || showch || showR || showG || showB || showL || showFocusMask) { - Glib::RefPtr tmp = cropHandler.cropPixbuf->copy (); - guint8* pix = tmp->get_pixels(); + if (showcs || showch || showR || showG || showB || showL || showFocusMask) { + Glib::RefPtr tmp = cropHandler.cropPixbuf->copy (); + guint8* pix = tmp->get_pixels(); guint8* pixWrkSpace = cropHandler.cropPixbuftrue->get_pixels(); int pixRowStride = tmp->get_rowstride (); @@ -847,12 +924,11 @@ void CropWindow::expose (Cairo::RefPtr cr) { int delta; bool changedHL; bool changedSH; for (int j=0; jget_width(); j++) { - - + if (showFocusMask){ // modulate preview to display focus mask //************* - // Copyright (c) 2011 Michael Ezra michael@michaelezra.com + // Copyright (c) 2011 Michael Ezra michael@michaelezra.com // determine if pixel is in the sharp area of the image using // standard deviation analysis on two different scales int blur_radius, blur_radius2; @@ -878,23 +954,23 @@ void CropWindow::expose (Cairo::RefPtr cr) { if (j>blur_radius && jget_width()-blur_radius && i>blur_radius && iget_height()-blur_radius){ //stay within image area // calculate average in +-blur_radius pixels area around the current pixel - // speed up: calculate sum of squares in the same loops + // speed up: calculate sum of squares in the same loops - sum_L = 0; sum_L2=0; - sumsq_L = 0; sumsq_L2 = 0; + sum_L = 0; sum_L2=0; + sumsq_L = 0; sumsq_L2 = 0; for (int kh=-blur_radius; kh<=blur_radius;kh++){ for (int k=-blur_radius; k<=blur_radius;k++){ //1 pixel step is equivalent to 3-bytes step - currIndex = currWS+3*k + kh*pixWSRowStride; - curL = 0.299*(currIndex)[0]+0.587*(currIndex)[1]+0.114*(currIndex)[2]; - sum_L += curL; - sumsq_L += SQR(curL); + currIndex = currWS+3*k + kh*pixWSRowStride; + curL = 0.299*(currIndex)[0]+0.587*(currIndex)[1]+0.114*(currIndex)[2]; + sum_L += curL; + sumsq_L += SQR(curL); - // Band2 @ blur_radius2 - if (kh>=-blur_radius2 && kh<=blur_radius2 && k>=-blur_radius2 && k<=blur_radius2){ - sum_L2 += curL; - sumsq_L2 += SQR(curL); - } + // Band2 @ blur_radius2 + if (kh>=-blur_radius2 && kh<=blur_radius2 && k>=-blur_radius2 && k<=blur_radius2){ + sum_L2 += curL; + sumsq_L2 += SQR(curL); + } } } //************* @@ -915,9 +991,9 @@ void CropWindow::expose (Cairo::RefPtr cr) { /* if (stdDev_L2>focus_thresh2 && (stdDev_L cr) { && stdDev_L2 > stdDev_L //this is the key to select fine detail within lower contrast on larger scale && stdDev_L > focus_thresh/10 //options.highlightThreshold ){ - curr[0]=0; - curr[1]=255; - curr[2]=0; + curr[0]=0; + curr[1]=255; + curr[2]=0; } } } else { // !showFocusMask - + // we must compare clippings in working space, since the cropPixbuf is in sRGB, with mon profile - + changedHL=false; changedSH=false; @@ -950,10 +1026,10 @@ void CropWindow::expose (Cairo::RefPtr cr) { if (showch) { delta=0; changedHL=false; - if (currWS[0]>=options.highlightThreshold && (showR || showclippedAny)) { delta += 255-currWS[0]; changedHL=true; } - if (currWS[1]>=options.highlightThreshold && (showG || showclippedAny)) { delta += 255-currWS[1]; changedHL=true; } - if (currWS[2]>=options.highlightThreshold && (showB || showclippedAny)) { delta += 255-currWS[2]; changedHL=true; } - if (currWS_L>= options.highlightThreshold && showL) { delta += 255-currWS_L ; changedHL=true; } + if (currWS[0]>=options.highlightThreshold && (showR || showclippedAny)) { delta += 255-currWS[0]; changedHL=true; } + if (currWS[1]>=options.highlightThreshold && (showG || showclippedAny)) { delta += 255-currWS[1]; changedHL=true; } + if (currWS[2]>=options.highlightThreshold && (showB || showclippedAny)) { delta += 255-currWS[2]; changedHL=true; } + if (currWS_L>= options.highlightThreshold && showL) { delta += 255-currWS_L ; changedHL=true; } if (changedHL) { delta *= HighlightFac; @@ -969,7 +1045,7 @@ void CropWindow::expose (Cairo::RefPtr cr) { if (currWS[2]<=options.shadowThreshold && (showB || showclippedAny)) { delta += currWS[2]; changedSH=true; } if (currWS_L <=options.shadowThreshold && showL) { delta += currWS_L ; changedSH=true; } - if (changedSH) { + if (changedSH) { if (showclippedAny) { delta = 255 - (delta * ShawdowFac); curr[0]=curr[1]=curr[2]=delta; // indicate clipped shadows in gray @@ -998,24 +1074,24 @@ void CropWindow::expose (Cairo::RefPtr cr) { /* - if (showch && (currWS[0]>=options.highlightThreshold || currWS[1]>=options.highlightThreshold || currWS[2]>=options.highlightThreshold)) - curr[0] = curr[1] = curr[2] = 0; - else if (showcs && (currWS[0]<=options.shadowThreshold || currWS[1]<=options.shadowThreshold || currWS[2]<=options.shadowThreshold)) - curr[0] = curr[1] = curr[2] = 255; - //if (showch && ((0.299*curr[0]+0.587*curr[1]+0.114*curr[2])>=options.highlightThreshold)) - // curr[0] = curr[1] = curr[2] = 0; - //else if (showcs && ((0.299*curr[0]+0.587*curr[1]+0.114*curr[2])<=options.shadowThreshold)) - // curr[0] = curr[1] = curr[2] = 255; + if (showch && (currWS[0]>=options.highlightThreshold || currWS[1]>=options.highlightThreshold || currWS[2]>=options.highlightThreshold)) + curr[0] = curr[1] = curr[2] = 0; + else if (showcs && (currWS[0]<=options.shadowThreshold || currWS[1]<=options.shadowThreshold || currWS[2]<=options.shadowThreshold)) + curr[0] = curr[1] = curr[2] = 255; + //if (showch && ((0.299*curr[0]+0.587*curr[1]+0.114*curr[2])>=options.highlightThreshold)) + // curr[0] = curr[1] = curr[2] = 0; + //else if (showcs && ((0.299*curr[0]+0.587*curr[1]+0.114*curr[2])<=options.shadowThreshold)) + // curr[0] = curr[1] = curr[2] = 255; */ curr+=3; currWS+=3; - } + } } //printf("zoomSteps[cropZoom].zoom=%d\n",zoomSteps[cropZoom].zoom); - iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), tmp, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); - } - else - iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), cropHandler.cropPixbuf, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); + iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), tmp, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); + } + else + iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), cropHandler.cropPixbuf, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0); //t4.set (); // END OF BOTTLENECK if (cropHandler.cropParams.enabled) { @@ -1025,9 +1101,53 @@ void CropWindow::expose (Cairo::RefPtr cr) { } if (observedCropWin) drawObservedFrame (cr); + + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); + rtengine::Crop* crop = static_cast(cropHandler.getCrop()); + if (editSubscriber && crop->bufferCreated()) { + + // drawing Subscriber's visible geometry + + const std::vector visibleGeom = editSubscriber->getVisibleGeometry(); + cr->set_antialias(Cairo::ANTIALIAS_DEFAULT); // ANTIALIAS_SUBPIXEL ? + cr->set_line_cap(Cairo::LINE_CAP_SQUARE); + cr->set_line_join(Cairo::LINE_JOIN_ROUND); + // drawing outer lines + for (unsigned short currObject=0; currObject < visibleGeom.size(); ++currObject) { + visibleGeom.at(currObject)->drawOuterGeometry(cr, crop, *this); + } + // drawing inner lines + for (unsigned short currObject=0; currObject < visibleGeom.size(); ++currObject) { + visibleGeom.at(currObject)->drawInnerGeometry(cr, crop, *this); + } + + // drawing to the "mouse over" channel + + if (editSubscriber->getEditingType() == ET_OBJECTS) { + const std::vector mouseOverGeom = editSubscriber->getMouseOverGeometry(); + if (mouseOverGeom.size()) { + Cairo::RefPtr crMO = Cairo::Context::create(crop->getObjectMap()); + crMO->set_antialias(Cairo::ANTIALIAS_NONE); + crMO->set_line_cap(Cairo::LINE_CAP_SQUARE); + crMO->set_line_join(Cairo::LINE_JOIN_ROUND); + + Cairo::RefPtr crMO2; + if (crop->getObjectMode() > OM_255) { + crMO2 = Cairo::Context::create(crop->getObjectMap2()); + crMO2->set_antialias(Cairo::ANTIALIAS_NONE); + crMO2->set_line_cap(Cairo::LINE_CAP_SQUARE); + crMO2->set_line_join(Cairo::LINE_JOIN_ROUND); + } + for (unsigned short currObject=0; currObject < mouseOverGeom.size(); ++currObject) { + visibleGeom.at(currObject)->drawToMOChannel(crMO, crMO2, currObject, crop, *this); + } + } + + } + } } else { - // cropHandler.cropPixbuf is null + // cropHandler.cropPixbuf is null int cropX, cropY; cropHandler.getPosition (cropX, cropY); Glib::RefPtr rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom); @@ -1036,8 +1156,8 @@ void CropWindow::expose (Cairo::RefPtr cr) { if (cropHandler.cropParams.enabled) { drawCrop (cr, x+imgAreaX+(imgAreaW-rough->get_width())/2, y+imgAreaY+(imgAreaH-rough->get_height())/2, rough->get_width(), rough->get_height(), cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams); } - if (observedCropWin) - drawObservedFrame (cr, rough->get_width(), rough->get_height()); + if (observedCropWin) + drawObservedFrame (cr, rough->get_width(), rough->get_height()); } } } @@ -1060,17 +1180,22 @@ void CropWindow::expose (Cairo::RefPtr cr) { } if (state==SRotateSelecting) drawStraightenGuide (cr); - if (state==SNormal && iarea->getToolMode () == TMSpotWB) - drawSpotWBRectangle (cr); + if (state==SNormal) { + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); + if (iarea->getToolMode () == TMHand && editSubscriber && editSubscriber->getEditingType()==ET_PIPETTE) + drawUnscaledSpotRectangle (cr, iarea->getPipetteRectSize ()); + else if (iarea->getToolMode () == TMSpotWB) + drawScaledSpotRectangle (cr, iarea->getSpotWBRectSize ()); + } //t2.set (); // printf ("etime --> %d, %d\n", t2.etime (t1), t4.etime (t3)); } -// calculate the center of the zommed in/out preview given a cursor position +// calculate the center of the zoomed in/out preview given a cursor position void CropWindow::findCenter (int deltaZoom, int& x, int& y) { int cursorX, cursorY; - translateCoord(x, y, cursorX, cursorY); + screenCoordToImage(x, y, cursorX, cursorY); int cropX, cropY, cropW, cropH, skip; cropHandler.getWindow (cropX, cropY, cropW, cropH, skip); @@ -1108,7 +1233,7 @@ void CropWindow::zoomIn (bool toCursor, int cursorX, int cursorY) { } zoomVersion = exposeVersion; } else if (zoomVersion != exposeVersion) { - translateCoord(xpos+imgX+imgW/2, ypos+imgY+imgH/2, x, y); + screenCoordToImage(xpos+imgX+imgW/2, ypos+imgY+imgH/2, x, y); if (cropHandler.cropParams.enabled) { // add some gravity towards crop center int x1 = cropHandler.cropParams.x + cropHandler.cropParams.w / 2; @@ -1254,7 +1379,20 @@ void CropWindow::changeZoom (int zoom, bool notify, int centerx, int centery) { iarea->redraw (); } -void CropWindow::translateCoord (int phyx, int phyy, int& imgx, int& imgy) { +void CropWindow::screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy) { + + rtengine::Crop* crop = static_cast(cropHandler.getCrop()); + cropx = phyx - xpos - imgX; + cropy = phyy - ypos - imgY; + if (zoomSteps[cropZoom].zoom > 1.) { + cropx = int(double(cropx)/zoomSteps[cropZoom].zoom); + cropy = int(double(cropy)/zoomSteps[cropZoom].zoom); + } + cropx += crop->getLeftBorder(); + cropy += crop->getUpperBorder(); +} + +void CropWindow::screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy) { int cropX, cropY; cropHandler.getPosition (cropX, cropY); @@ -1262,6 +1400,38 @@ void CropWindow::translateCoord (int phyx, int phyy, int& imgx, int& imgy) { imgy = cropY + (phyy - ypos - imgY)/zoomSteps[cropZoom].zoom; } +void CropWindow::imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy) { + + int cropX, cropY; + cropHandler.getPosition (cropX, cropY); + phyx = (imgx - cropX)*zoomSteps[cropZoom].zoom + xpos + imgX; + phyy = (imgy - cropY)*zoomSteps[cropZoom].zoom + ypos + imgY; +} + +int CropWindow::scaleValueToImage (int value) { + return int(double(value)/zoomSteps[cropZoom].zoom); +} + +float CropWindow::scaleValueToImage (float value) { + return float(double(value)/zoomSteps[cropZoom].zoom); +} + +double CropWindow::scaleValueToImage (double value) { + return value/zoomSteps[cropZoom].zoom; +} + +int CropWindow::scaleValueToScreen (int value) { + return int(double(value)*zoomSteps[cropZoom].zoom); +} + +float CropWindow::scaleValueToScreen (float value) { + return float(double(value)*zoomSteps[cropZoom].zoom); +} + +double CropWindow::scaleValueToScreen (double value) { + return value*zoomSteps[cropZoom].zoom; +} + void CropWindow::drawDecoration (Cairo::RefPtr cr) { int x = xpos, y = ypos; @@ -1394,13 +1564,12 @@ void CropWindow::drawStraightenGuide (Cairo::RefPtr cr) { } } -void CropWindow::drawSpotWBRectangle (Cairo::RefPtr cr) { +void CropWindow::drawScaledSpotRectangle (Cairo::RefPtr cr, int rectSize) { - int rectsize = iarea->getSpotWBRectSize (); - int x1 = action_x/zoomSteps[cropZoom].zoom - rectsize; - int y1 = action_y/zoomSteps[cropZoom].zoom - rectsize; - int y2 = action_y/zoomSteps[cropZoom].zoom + rectsize; - int x2 = action_x/zoomSteps[cropZoom].zoom + rectsize; + int x1 = action_x/zoomSteps[cropZoom].zoom - rectSize; + int y1 = action_y/zoomSteps[cropZoom].zoom - rectSize; + int y2 = action_y/zoomSteps[cropZoom].zoom + rectSize; + int x2 = action_x/zoomSteps[cropZoom].zoom + rectSize; cr->set_line_width (1.0); cr->rectangle (xpos+imgX-0.5, ypos+imgY-0.5, imgW, imgH); @@ -1416,6 +1585,27 @@ void CropWindow::drawSpotWBRectangle (Cairo::RefPtr cr) { cr->reset_clip (); } +void CropWindow::drawUnscaledSpotRectangle (Cairo::RefPtr cr, int rectSize) { + + int x1 = action_x - rectSize; + int y1 = action_y - rectSize; + int y2 = action_y + rectSize; + int x2 = action_x + rectSize; + + cr->set_line_width (1.0); + cr->rectangle (xpos+imgX-0.5, ypos+imgY-0.5, imgW, imgH); + cr->clip (); + + cr->set_source_rgb (1.0, 1.0, 1.0); + cr->rectangle (x1-1.5, y1-1.5, x2-x1+2, y2-y1+2); + cr->stroke (); + cr->set_source_rgb (0.0, 0.0, 0.0); + cr->rectangle (x1-0.5, y1-0.5, x2-x1, y2-y1); + cr->stroke (); + + cr->reset_clip (); +} + void CropWindow::getObservedFrameArea (int& x, int& y, int& w, int& h, int rw, int rh) { int cropX, cropY, cropW, cropH; @@ -1499,3 +1689,7 @@ void CropWindow::delCropWindowListener (CropWindowListener* l) { else i++; } + +EditDataProvider* CropWindow::getImageArea() { + return iarea; +} diff --git a/rtgui/cropwindow.h b/rtgui/cropwindow.h index efb7adfea..ae0691eb4 100755 --- a/rtgui/cropwindow.h +++ b/rtgui/cropwindow.h @@ -33,6 +33,7 @@ class CropWindow; class CropWindowListener { public: + virtual ~CropWindowListener() {} virtual void cropPositionChanged (CropWindow*) {} virtual void cropWindowSizeChanged (CropWindow*) {} virtual void cropZoomChanged (CropWindow*) {} @@ -40,7 +41,7 @@ class CropWindowListener { }; class ImageArea; -class CropWindow : public LWButtonListener, public CropHandlerListener { +class CropWindow : public LWButtonListener, public CropHandlerListener, public EditCoordSystem { // state management ImgEditState state; // current state of user (see enum State) @@ -84,30 +85,41 @@ class CropWindow : public LWButtonListener, public CropHandlerListener { CropWindow* observedCropWin; - bool onArea (CursorArea a, int x, int y); - void updateCursor (int x, int y); - void drawDecoration (Cairo::RefPtr cr); - void drawStraightenGuide (Cairo::RefPtr cr); - void drawSpotWBRectangle (Cairo::RefPtr cr); - void drawObservedFrame (Cairo::RefPtr cr, int rw=0, int rh=0); - void translateCoord (int phyx, int phyy, int& imgx, int& imgy); - void changeZoom (int zoom, bool notify=true, int centerx=-1, int centery=-1); - void getObservedFrameArea(int& x, int& y, int& w, int& h, int rw=0, int rh=0); + bool onArea (CursorArea a, int x, int y); + void updateCursor (int x, int y); + void drawDecoration (Cairo::RefPtr cr); + void drawStraightenGuide (Cairo::RefPtr cr); + void drawScaledSpotRectangle (Cairo::RefPtr cr, int rectSize); + void drawUnscaledSpotRectangle (Cairo::RefPtr cr, int rectSize); + void drawObservedFrame (Cairo::RefPtr cr, int rw=0, int rh=0); + void changeZoom (int zoom, bool notify=true, int centerx=-1, int centery=-1); + void getObservedFrameArea (int& x, int& y, int& w, int& h, int rw=0, int rh=0); public: CropHandler cropHandler; CropWindow (ImageArea* parent, rtengine::StagedImageProcessor* ipc_, bool isLowUpdatePriority_); - virtual ~CropWindow (); void setDecorated (bool decorated) { this->decorated = decorated; } void setFitZoomEnabled (bool fze) { fitZoomEnabled = fze; } void setObservedCropWin (CropWindow* cw) { observedCropWin = cw; } + void screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy); + void screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy); + void imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy); + int scaleValueToImage (int value); + float scaleValueToImage (float value); + double scaleValueToImage (double value); + int scaleValueToScreen (int value); + float scaleValueToScreen (float value); + double scaleValueToScreen (double value); + void setPosition (int x, int y); void getPosition (int& x, int& y); void setSize (int w, int h, bool norefresh=false); void getSize (int& w, int& h); + void leaveNotify (GdkEventCrossing* event); + // zoomlistener interface void zoomIn (bool toCursor=false, int cursorX=-1, int cursorY=-1); void zoomOut (bool toCursor=false, int cursorX=-1, int cursorY=-1); @@ -124,7 +136,7 @@ class CropWindow : public LWButtonListener, public CropHandlerListener { void buttonPress (int button, int num, int state, int x, int y); void buttonRelease (int button, int num, int state, int x, int y); - void pointerMoved (int x, int y); + void pointerMoved (int bstate, int x, int y); void expose (Cairo::RefPtr cr); @@ -154,6 +166,8 @@ class CropWindow : public LWButtonListener, public CropHandlerListener { void remoteMove (int deltaX, int deltaY); void remoteMoveReady (); + + EditDataProvider* getImageArea(); }; #endif diff --git a/rtgui/cursormanager.h b/rtgui/cursormanager.h index 4c0d1f446..19b024155 100644 --- a/rtgui/cursormanager.h +++ b/rtgui/cursormanager.h @@ -21,7 +21,12 @@ #include -enum CursorShape {CSArrow, CSOpenHand, CSClosedHand, CSMove, CSMoveLeft, CSMoveRight, CSResizeWidth, CSResizeHeight, CSResizeDiagonal, CSResizeTopLeft, CSResizeTopRight, CSResizeBottomLeft, CSResizeBottomRight, CSSpotWB, CSCropSelect, CSStraighten, CSPlus, CSWait, CSEmpty}; +enum CursorShape { + CSArrow, CSOpenHand, CSClosedHand, CSMove, CSMoveLeft, + CSMoveRight, CSResizeWidth, CSResizeHeight, CSResizeDiagonal, + CSResizeTopLeft, CSResizeTopRight, CSResizeBottomLeft, CSResizeBottomRight, + CSSpotWB, CSCropSelect, CSStraighten, CSPlus, CSWait, CSEmpty +}; class CursorManager { diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 7d8b9cf11..a6f045c50 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -132,6 +132,7 @@ std::vector FlatCurveEditor::getCurve () { CurveEditor::CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) { bgHistValid = false; + remoteDrag = false; selected = DCT_Linear; bottomBarCP = NULL; leftBarCP = NULL; @@ -288,3 +289,52 @@ sigc::signal CurveEditor::signal_curvepoint_click() { sigc::signal CurveEditor::signal_curvepoint_release() { return sig_curvepoint_release; } + +void CurveEditor::switchOffEditMode () { + if (EditSubscriber::getEditID() != EUID_None) { + // switching off the toggle button + if (group->displayedCurve == this) { + subGroup->editModeSwitchedOff(); + } + } + EditSubscriber::switchOffEditMode(); // disconnect +} + +bool CurveEditor::mouseOver(int modifierKey) { + EditDataProvider* provider = getEditProvider(); + subGroup->pipetteMouseOver(provider, modifierKey); + subGroup->refresh(this); + return true; // return true will ask the preview to be redrawn, for the cursor +} + +bool CurveEditor::button1Pressed(int modifierKey) { + EditDataProvider* provider = getEditProvider(); + if (provider->object) { + subGroup->pipetteButton1Pressed(provider, modifierKey); + remoteDrag = true; + } + subGroup->refresh(this); + return true; +} + +bool CurveEditor::button1Released() { + EditDataProvider* provider = getEditProvider(); + subGroup->pipetteButton1Released(provider); + remoteDrag = false; + subGroup->refresh(this); + return true; +} + +bool CurveEditor::drag(int modifierKey) { + EditDataProvider* provider = getEditProvider(); + subGroup->pipetteDrag(provider, modifierKey); + subGroup->refresh(this); + return false; +} + +CursorShape CurveEditor::getCursor(int objectID) { + printf("CurveEditor::getCursor\n"); + if (remoteDrag) + return CSResizeHeight; + return CSOpenHand; +} diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index 129d5b83b..877bed0e7 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -22,6 +22,7 @@ #include "popuptogglebutton.h" #include "../rtengine/LUT.h" #include "coloredbar.h" +#include "edit.h" class CurveEditorGroup; class CurveEditorSubGroup; @@ -36,7 +37,7 @@ class CurveEditorSubGroup; * This class is an interface between RT and the curve editor group ; it handles the methods * related to a specific curve. It is created by CurveEditorGroup::addCurve */ -class CurveEditor { +class CurveEditor : public EditSubscriber { friend class CurveEditorGroup; friend class CurveEditorSubGroup; @@ -56,6 +57,8 @@ class CurveEditor { LUTu histogram; // histogram values bool bgHistValid; + bool remoteDrag; + int selected; CurveEditorGroup* group; @@ -116,6 +119,15 @@ class CurveEditor { sigc::signal signal_curvegraph_leave(); sigc::signal signal_curvepoint_click(); sigc::signal signal_curvepoint_release(); + + void switchOffEditMode (); + bool mouseOver(int modifierKey); + bool button1Pressed(int modifierKey); + bool button1Released(); + bool drag(int modifierKey); + CursorShape getCursor(int objectID); + + }; diff --git a/rtgui/curveeditorgroup.cc b/rtgui/curveeditorgroup.cc index da795b26f..3e56f8b58 100644 --- a/rtgui/curveeditorgroup.cc +++ b/rtgui/curveeditorgroup.cc @@ -27,12 +27,9 @@ #include "../rtengine/safegtk.h" #include "rtimage.h" -CurveEditorGroup::CurveEditorGroup (Glib::ustring& curveDir, Glib::ustring groupLabel) : curveDir(curveDir), cl(NULL) { - curveEditors.clear(); - displayedCurve = 0; - numberOfPackedCurve = 0; - flatSubGroup = 0; - diagonalSubGroup = 0; +CurveEditorGroup::CurveEditorGroup (Glib::ustring& curveDir, Glib::ustring groupLabel) : curveDir(curveDir), curve_reset(NULL), + displayedCurve(0), flatSubGroup(0), diagonalSubGroup(0), cl(NULL), numberOfPackedCurve(0) +{ // We set the label to the one provided as parameter, even if it's an empty string curveGroupLabel = Gtk::manage (new Gtk::Label (groupLabel+":", Gtk::ALIGN_LEFT)); @@ -330,6 +327,21 @@ CurveEditorSubGroup::~CurveEditorSubGroup() { if (bottomBar) delete bottomBar; } +void CurveEditorSubGroup::updateEditButton(CurveEditor* curve, Gtk::ToggleButton *button, sigc::connection &connection) { + if (curve->getEditID() == EUID_None) { + button->hide(); + } + else { + button->show(); + bool prevstate = connection.block(true); + if (curve->isCurrentSubscriber()) + button->set_active(true); + else + button->set_active(false); + if (!prevstate) connection.block(false); + } +} + Glib::ustring CurveEditorSubGroup::outputFile () { Gtk::FileChooserDialog dialog(M("CURVEEDITOR_SAVEDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_SAVE); diff --git a/rtgui/curveeditorgroup.h b/rtgui/curveeditorgroup.h index a14f2d28f..182a0ef7a 100644 --- a/rtgui/curveeditorgroup.h +++ b/rtgui/curveeditorgroup.h @@ -113,9 +113,16 @@ public: virtual ~CurveEditorSubGroup(); int getValUnchanged() { return valUnchanged; } int getValLinear() { return valLinear; } + void updateEditButton(CurveEditor* curve, Gtk::ToggleButton *button, sigc::connection &connection); virtual void updateBackgroundHistogram (CurveEditor* ce) {} virtual void switchGUI() = 0; virtual void refresh(CurveEditor *curveToRefresh) = 0; + virtual void editModeSwitchedOff() = 0; + + virtual void pipetteMouseOver(EditDataProvider *provider, int modifierKey) =0; + virtual void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) =0; + virtual void pipetteButton1Released(EditDataProvider *provider) =0; + virtual void pipetteDrag(EditDataProvider *provider, int modifierKey) =0; protected: diff --git a/rtgui/diagonalcurveeditorsubgroup.cc b/rtgui/diagonalcurveeditorsubgroup.cc index 12a822a1c..a2c1bc99f 100644 --- a/rtgui/diagonalcurveeditorsubgroup.cc +++ b/rtgui/diagonalcurveeditorsubgroup.cc @@ -59,11 +59,14 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, saveCustom->add (*Gtk::manage (new RTImage ("gtk-save-large.png"))); loadCustom = Gtk::manage (new Gtk::Button ()); loadCustom->add (*Gtk::manage (new RTImage ("gtk-open.png"))); - + editCustom = Gtk::manage (new Gtk::ToggleButton()); + editCustom->add (*Gtk::manage (new RTImage ("editmodehand.png"))); + editCustom->hide(); custombbox->pack_end (*pasteCustom, Gtk::PACK_SHRINK, 0); custombbox->pack_end (*copyCustom, Gtk::PACK_SHRINK, 0); custombbox->pack_end (*saveCustom, Gtk::PACK_SHRINK, 0); custombbox->pack_end (*loadCustom, Gtk::PACK_SHRINK, 0); + custombbox->pack_start(*editCustom, Gtk::PACK_SHRINK, 0); customCurveBox->pack_end (*custombbox, Gtk::PACK_SHRINK, 0); customCurveBox->show_all (); @@ -72,7 +75,8 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, loadCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); copyCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) ); pasteCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) ); - + editCustomConn = editCustom->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editCustom) ); + saveCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); loadCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); copyCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPCOPY")); @@ -100,11 +104,14 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, saveNURBS->add (*Gtk::manage (new RTImage ("gtk-save-large.png"))); loadNURBS = Gtk::manage (new Gtk::Button ()); loadNURBS->add (*Gtk::manage (new RTImage ("gtk-open.png"))); - + editNURBS = Gtk::manage (new Gtk::ToggleButton()); + editNURBS->add (*Gtk::manage (new RTImage ("editmodehand.png"))); + editNURBS->hide(); NURBSbbox->pack_end (*pasteNURBS, Gtk::PACK_SHRINK, 0); NURBSbbox->pack_end (*copyNURBS, Gtk::PACK_SHRINK, 0); NURBSbbox->pack_end (*saveNURBS, Gtk::PACK_SHRINK, 0); NURBSbbox->pack_end (*loadNURBS, Gtk::PACK_SHRINK, 0); + NURBSbbox->pack_start(*editNURBS, Gtk::PACK_SHRINK, 0); NURBSCurveBox->pack_end (*NURBSbbox, Gtk::PACK_SHRINK, 0); NURBSCurveBox->show_all (); @@ -113,6 +120,7 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, loadNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); pasteNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) ); copyNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) ); + editNURBSConn = editNURBS->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editNURBS) ); saveNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); loadNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); @@ -136,8 +144,8 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, paramCurveBox->pack_start (*paramCurve, Gtk::PACK_EXPAND_WIDGET, 0); paramCurveBox->pack_start (*shcSelector, Gtk::PACK_EXPAND_WIDGET, 0); - Gtk::HBox* Parambbox = Gtk::manage (new Gtk::HBox ()); - Parambbox->set_spacing(4); + Gtk::HBox* parambbox = Gtk::manage (new Gtk::HBox ()); + parambbox->set_spacing(4); pasteParam = Gtk::manage (new Gtk::Button ()); pasteParam->add (*Gtk::manage (new RTImage ("edit-paste.png"))); copyParam = Gtk::manage (new Gtk::Button ()); @@ -146,23 +154,27 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, saveParam->add (*Gtk::manage (new RTImage ("gtk-save-large.png"))); loadParam = Gtk::manage (new Gtk::Button ()); loadParam->add (*Gtk::manage (new RTImage ("gtk-open.png"))); - - Parambbox->pack_end (*pasteParam, Gtk::PACK_SHRINK, 0); - Parambbox->pack_end (*copyParam, Gtk::PACK_SHRINK, 0); - Parambbox->pack_end (*saveParam, Gtk::PACK_SHRINK, 0); - Parambbox->pack_end (*loadParam, Gtk::PACK_SHRINK, 0); + editParam = Gtk::manage (new Gtk::ToggleButton()); + editParam->add (*Gtk::manage (new RTImage ("editmodehand.png"))); + editParam->hide(); + parambbox->pack_end (*pasteParam, Gtk::PACK_SHRINK, 0); + parambbox->pack_end (*copyParam, Gtk::PACK_SHRINK, 0); + parambbox->pack_end (*saveParam, Gtk::PACK_SHRINK, 0); + parambbox->pack_end (*loadParam, Gtk::PACK_SHRINK, 0); + parambbox->pack_start(*editParam, Gtk::PACK_SHRINK, 0); saveParam->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) ); loadParam->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); pasteParam->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::pastePressed) ); copyParam->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::copyPressed) ); + editCustomConn = editParam->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::editToggled), editParam) ); saveParam->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); loadParam->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); pasteParam->set_tooltip_text (M("CURVEEDITOR_TOOLTIPPASTE")); copyParam->set_tooltip_text (M("CURVEEDITOR_TOOLTIPCOPY")); - paramCurveBox->pack_end (*Parambbox, Gtk::PACK_EXPAND_WIDGET, 0); + paramCurveBox->pack_end (*parambbox, Gtk::PACK_EXPAND_WIDGET, 0); highlights = Gtk::manage (new Adjuster (M("CURVEEDITOR_HIGHLIGHTS"), -100, 100, 1, 0)); lights = Gtk::manage (new Adjuster (M("CURVEEDITOR_LIGHTS"), -100, 100, 1, 0)); @@ -235,6 +247,98 @@ DiagonalCurveEditor* DiagonalCurveEditorSubGroup::addCurve(Glib::ustring curveLa return newCE; } +/* + * Switch off the edit button + */ +void DiagonalCurveEditorSubGroup::editModeSwitchedOff () { + // toggling off all edit buttons, even if only one is toggle on + bool prevState; + prevState = editCustomConn.block(true); + editCustom->set_active(false); + if (!prevState) editCustomConn.block(false); + prevState = editNURBSConn.block(true); + editNURBS->set_active(false); + if (!prevState) editNURBSConn.block(false); + prevState = editParamConn.block(true); + editParam->set_active(false); + if (!prevState) editParamConn.block(false); +} + +void DiagonalCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, int modifierKey) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((DiagonalCurveType)(curveEditor->curveType->getSelected())) { + case (DCT_Spline): + customCurve->pipetteMouseOver(provider, modifierKey); + customCurve->setDirty(true); + break; + case (DCT_Parametric): + paramCurve->pipetteMouseOver(provider, modifierKey); + paramCurve->setDirty(true); + break; + case (DCT_NURBS): + NURBSCurve->pipetteMouseOver(provider, modifierKey); + NURBSCurve->setDirty(true); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + +void DiagonalCurveEditorSubGroup::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((DiagonalCurveType)(curveEditor->curveType->getSelected())) { + case (DCT_Spline): + customCurve->pipetteButton1Pressed(provider, modifierKey); + break; + case (DCT_Parametric): + paramCurve->pipetteButton1Pressed(provider, modifierKey); + break; + case (DCT_NURBS): + NURBSCurve->pipetteButton1Pressed(provider, modifierKey); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + +void DiagonalCurveEditorSubGroup::pipetteButton1Released(EditDataProvider *provider) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((DiagonalCurveType)(curveEditor->curveType->getSelected())) { + case (DCT_Spline): + customCurve->pipetteButton1Released(provider); + break; + case (DCT_Parametric): + paramCurve->pipetteButton1Released(provider); + break; + case (DCT_NURBS): + NURBSCurve->pipetteButton1Released(provider); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + +void DiagonalCurveEditorSubGroup::pipetteDrag(EditDataProvider *provider, int modifierKey) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((DiagonalCurveType)(curveEditor->curveType->getSelected())) { + case (DCT_Spline): + customCurve->pipetteDrag(provider, modifierKey); + break; + case (DCT_Parametric): + paramCurve->pipetteDrag(provider, modifierKey); + break; + case (DCT_NURBS): + NURBSCurve->pipetteDrag(provider, modifierKey); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + /* * Force the resize of the curve editor, if the displayed one is the requested one */ @@ -330,6 +434,7 @@ void DiagonalCurveEditorSubGroup::switchGUI() { customCurve->setColorProvider(dCurve->getCurveColorProvider(), dCurve->getCurveCallerId()); customCurve->setColoredBar(leftBar, bottomBar); customCurve->forceResize(); + updateEditButton(dCurve, editCustom, editCustomConn); parent->pack_start (*customCurveBox); customCurveBox->check_resize(); break; @@ -360,6 +465,7 @@ void DiagonalCurveEditorSubGroup::switchGUI() { shcSelector->setMargins( (leftBar ? MyCurve::getBarWidth()+CBAR_MARGIN : RADIUS), RADIUS ); paramCurve->setColoredBar(leftBar, NULL); paramCurve->forceResize(); + updateEditButton(dCurve, editParam, editParamConn); parent->pack_start (*paramCurveBox); break; } @@ -368,6 +474,7 @@ void DiagonalCurveEditorSubGroup::switchGUI() { NURBSCurve->setColorProvider(dCurve->getCurveColorProvider(), dCurve->getCurveCallerId()); NURBSCurve->setColoredBar(leftBar, bottomBar); NURBSCurve->forceResize(); + updateEditButton(dCurve, editNURBS, editNURBSConn); parent->pack_start (*NURBSCurveBox); NURBSCurveBox->check_resize(); break; @@ -543,6 +650,58 @@ void DiagonalCurveEditorSubGroup::pastePressed () { return; } +void DiagonalCurveEditorSubGroup::editToggled (Gtk::ToggleButton *button) { + DiagonalCurveEditor* dCurve = static_cast(parent->displayedCurve); + if (!dCurve) + // should never happen! + return; + + if (button->get_active()) { + dCurve->subscribe(); + if (button == editCustom) + customCurve->notifyListener (); + else if (button == editNURBS) + NURBSCurve->notifyListener (); + else if (button == editParam) + paramCurve->notifyListener (); + + /* + if (button != editCustom) { + editCustomConn.block(true); + editCustom->set_active(true); + editCustomConn.block(false); + } + else { + // will throw the event of curveChanged, which will now build the edit's buffer + customCurve->notifyListener (); + } + if (button != editNURBS) { + editNURBSConn.block(true); + editNURBS->set_active(true); + editNURBSConn.block(false); + } + else { + NURBSCurve->notifyListener (); + } + if (button != editParam) { + editParamConn.block(true); + editParam->set_active(true); + editParamConn.block(false); + } + else { + paramCurve->notifyListener (); + } + */ + } + else { + dCurve->unsubscribe(); + /* + if (button != editCustom ) { editCustomConn.block(true); editCustom->set_active(false); editCustomConn.block(false); } + if (button != editNURBS ) { editNURBSConn.block(true); editNURBS->set_active(false); editNURBSConn.block(false); } + if (button != editParam ) { editParamConn.block(true); editParam->set_active(false); editParamConn.block(false); } + */ + } +} /* * Store the curves of the currently displayed type from the widgets to the CurveEditor object diff --git a/rtgui/diagonalcurveeditorsubgroup.h b/rtgui/diagonalcurveeditorsubgroup.h index 71a46e959..4c3acda31 100644 --- a/rtgui/diagonalcurveeditorsubgroup.h +++ b/rtgui/diagonalcurveeditorsubgroup.h @@ -43,18 +43,24 @@ protected: Adjuster* darks; Adjuster* shadows; - Gtk::Button* saveCustom; - Gtk::Button* loadCustom; - Gtk::Button* copyCustom; - Gtk::Button* pasteCustom; - Gtk::Button* saveNURBS; - Gtk::Button* loadNURBS; - Gtk::Button* copyNURBS; - Gtk::Button* pasteNURBS; - Gtk::Button* saveParam; - Gtk::Button* loadParam; - Gtk::Button* copyParam; - Gtk::Button* pasteParam; + Gtk::Button* saveCustom; + Gtk::Button* loadCustom; + Gtk::Button* copyCustom; + Gtk::Button* pasteCustom; + Gtk::ToggleButton* editCustom; + sigc::connection editCustomConn; + Gtk::Button* saveNURBS; + Gtk::Button* loadNURBS; + Gtk::Button* copyNURBS; + Gtk::Button* pasteNURBS; + Gtk::ToggleButton* editNURBS; + sigc::connection editNURBSConn; + Gtk::Button* saveParam; + Gtk::Button* loadParam; + Gtk::Button* copyParam; + Gtk::Button* pasteParam; + Gtk::ToggleButton* editParam; + sigc::connection editParamConn; int activeParamControl; @@ -66,6 +72,11 @@ public: virtual void updateBackgroundHistogram (CurveEditor* ce); void switchGUI(); void refresh(CurveEditor *curveToRefresh); + void editModeSwitchedOff (); + void pipetteMouseOver(EditDataProvider *provider, int modifierKey); + void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); + void pipetteButton1Released(EditDataProvider *provider); + void pipetteDrag(EditDataProvider *provider, int modifierKey); protected: void storeCurveValues (CurveEditor* ce, const std::vector& p); @@ -75,6 +86,7 @@ protected: void loadPressed (); void copyPressed (); void pastePressed (); + void editToggled (Gtk::ToggleButton *button); bool curveReset (int cType, double iValue); void removeEditor (); const std::vector getCurveFromGUI (int type); diff --git a/rtgui/edit.cc b/rtgui/edit.cc new file mode 100644 index 000000000..2cfe321d4 --- /dev/null +++ b/rtgui/edit.cc @@ -0,0 +1,433 @@ +/* + * 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 "edit.h" +#include "../rtengine/editbuffer.h" + +RGBColor Geometry::getInnerLineColor () { + RGBColor color; + if (flags & AUTO_COLOR) { + if (state == NORMAL) { color.setColor (1., 1., 1.); } // White + else if (state == PRELIGHT) { color.setColor (1., 1., 0.); } // Orange + else if (state == DRAGGED) { color.setColor (1., 0., 0.); } // Red + } + else { color = innerLineColor; } + return color; +} + +RGBColor Geometry::getOuterLineColor () { + RGBColor color; + if (flags & AUTO_COLOR) { + /* + if (state == NORMAL) { color.setColor (0., 0., 0.); } // Black + else if (state == PRELIGHT) { color.setColor (0., 0., 0.); } // Black + else if (state == DRAGGED) { color.setColor (1., 0., 0.); } // Black + */ + color.setColor (0., 0., 0.); // Black + } + else { color = outerLineColor; } + return color; +} + +void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if (flags & ACTIVE) { + cr->set_line_width( getOuterLineWidth() ); + Coord center_ = center; + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToImage(double(radius)) : double(radius); + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(center.x, center.y, center_.x, center_.y); + } + else if (datum == CLICKED_POINT) { + center_ += editBuffer->getDataProvider()->posScreen; + } + else if (datum == CURSOR) { + center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + } + cr->set_source_rgb (outerLineColor.getR(), outerLineColor.getG(), outerLineColor.getB()); + cr->arc(center_.x, center_.y, radius_, 0., 2.*M_PI); + } +} + +void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if (flags & ACTIVE) { + Coord center_ = center; + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToImage(double(radius)) : double(radius); + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(center.x, center.y, center_.x, center_.y); + } + else if (datum == CLICKED_POINT) { + center_ += editBuffer->getDataProvider()->posScreen; + } + else if (datum == CURSOR) { + center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + } + cr->set_source_rgb (innerLineColor.getR(), innerLineColor.getG(), innerLineColor.getB()); + if (filled) { + cr->arc(center_.x, center_.y, radius_, 0., 2.*M_PI); + cr->set_line_width( innerLineWidth ); + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } + else + cr->fill(); + } + else if (innerLineWidth > 0.) { + cr->arc(center_.x, center_.y, radius_, 0., 2.*M_PI); + cr->stroke(); + } + } +} + +void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if (flags & ACTIVE) { + cr->set_line_width( getMouseOverLineWidth() ); + Coord center_ = center; + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToImage(double(radius)) : double(radius); + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(center.x, center.y, center_.x, center_.y); + } + else if (datum == CLICKED_POINT) { + center_ += editBuffer->getDataProvider()->posScreen; + } + else if (datum == CURSOR) { + center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + } + + // drawing the lower byte's value + unsigned short a = (id+1) & 0xFF; + cr->set_source_rgba (0.,0., 0., double(a)/255.); + cr->arc(center_.x, center_.y, radius_, 0, 2.*M_PI); + if (filled) { + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } + else + cr->fill(); + } + else + cr->stroke(); + + // drawing the higher byte's value + if (editBuffer->getObjectMode() == OM_65535) { + a = (id+1)>>8; + cr2->set_source_rgba (0.,0., 0., double(a)/255.); + cr2->arc(center_.x, center_.y, radius_, 0, 2.*M_PI); + if (filled) { + if (innerLineWidth > 0.) { + cr2->fill_preserve(); + cr2->stroke(); + } + else + cr2->fill(); + } + else + cr2->stroke(); + } + } +} + +void Line::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if (flags & ACTIVE) { + cr->set_line_width( getOuterLineWidth() ); + Coord begin_ = begin; + Coord end_ = end; + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(begin.x, begin.y, begin_.x, begin_.y); + coordSystem.imageCoordToScreen(end.x, end.y, end_.x, end_.y); + } + else if (datum == CLICKED_POINT) { + begin_ += editBuffer->getDataProvider()->posScreen; + end_ += editBuffer->getDataProvider()->posScreen; + } + else if (datum == CURSOR) { + begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + } + cr->set_source_rgb (outerLineColor.getR(), outerLineColor.getG(), outerLineColor.getB()); + cr->move_to(begin_.x, begin_.y); + cr->line_to(end_.x, end_.y); + cr->stroke(); + } +} + +void Line::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if ((flags & ACTIVE) && innerLineWidth > 0.) { + Coord begin_ = begin; + Coord end_ = end; + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(begin.x, begin.y, begin_.x, begin_.y); + coordSystem.imageCoordToScreen(end.x, end.y, end_.x, end_.y); + } + else if (datum == CLICKED_POINT) { + begin_ += editBuffer->getDataProvider()->posScreen; + end_ += editBuffer->getDataProvider()->posScreen; + } + else if (datum == CURSOR) { + begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + } + cr->set_line_width(innerLineWidth); + cr->set_source_rgb (innerLineColor.getR(), innerLineColor.getG(), innerLineColor.getB()); + cr->move_to(begin_.x, begin_.y); + cr->line_to(end_.x, end_.y); + cr->stroke(); + } +} + +void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if (flags & ACTIVE) { + cr->set_line_width( getMouseOverLineWidth() ); + Coord begin_ = begin; + Coord end_ = end; + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(begin.x, begin.y, begin_.x, begin_.y); + coordSystem.imageCoordToScreen(end.x, end.y, end_.x, end_.y); + } + else if (datum == CLICKED_POINT) { + begin_ += editBuffer->getDataProvider()->posScreen; + end_ += editBuffer->getDataProvider()->posScreen; + } + else if (datum == CURSOR) { + begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + } + + // drawing the lower byte's value + unsigned short a = (id+1) & 0xFF; + cr->set_source_rgba (0.,0., 0., double(a)/255.); + cr->move_to(begin_.x, begin_.y); + cr->line_to(end_.x, end_.y); + cr->stroke(); + + // drawing the higher byte's value + if (editBuffer->getObjectMode() == OM_65535) { + a = (id+1)>>8; + cr2->set_source_rgba (0.,0., 0., double(a)/255.); + cr2->move_to(begin_.x, begin_.y); + cr2->line_to(end_.x, end_.y); + cr2->stroke(); + } + } +} + +void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if ((flags & ACTIVE) && points.size()>1) { + cr->set_source_rgb (outerLineColor.getR(), outerLineColor.getG(), outerLineColor.getB()); + + Coord currPos; + for (unsigned int i=0; iset_line_width( getOuterLineWidth() ); + currPos = points.at(i); + + if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + else if (datum == CLICKED_POINT) currPos += editBuffer->getDataProvider()->posScreen; + else if (datum == CURSOR) currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + + if (!i) cr->move_to(currPos.x, currPos.y); + else cr->line_to(currPos.x, currPos.y); + } + if (filled) { + cr->fill_preserve(); + cr->stroke(); + } + else + cr->fill(); + } +} + +void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if ((flags & ACTIVE) && points.size()>1) { + cr->set_source_rgb (innerLineColor.getR(), innerLineColor.getG(), innerLineColor.getB()); + + if (filled) { + Coord currPos; + for (unsigned int i=0; iset_line_width( getOuterLineWidth() ); + currPos = points.at(i); + + if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + else if (datum == CLICKED_POINT) currPos += editBuffer->getDataProvider()->posScreen; + else if (datum == CURSOR) currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + + if (!i) cr->move_to(currPos.x, currPos.y); + else cr->line_to(currPos.x, currPos.y); + } + + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } + else + cr->fill(); + } + else if (innerLineWidth > 0.) { + Coord currPos; + for (unsigned int i=0; iset_line_width( getOuterLineWidth() ); + currPos = points.at(i); + + if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + else if (datum == CLICKED_POINT) currPos += editBuffer->getDataProvider()->posScreen; + else if (datum == CURSOR) currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + + if (!i) cr->move_to(currPos.x, currPos.y); + else cr->line_to(currPos.x, currPos.y); + } + cr->fill(); + } + } +} + +void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { + if ((flags & ACTIVE) && points.size()>1) { + Coord currPos; + + // drawing the lower byte's value + unsigned short a = (id+1) & 0xFF; + cr->set_source_rgba (0.,0., 0., double(a)/255.); + for (unsigned int i=0; iset_line_width( getOuterLineWidth() ); + currPos = points.at(i); + + if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + else if (datum == CLICKED_POINT) currPos += editBuffer->getDataProvider()->posScreen; + else if (datum == CURSOR) currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + + if (!i) cr->move_to(currPos.x, currPos.y); + else cr->line_to(currPos.x, currPos.y); + } + if (filled) { + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } + else + cr->fill(); + } + else + cr->stroke(); + + // drawing the higher byte's value + if (editBuffer->getObjectMode() == OM_65535) { + a = (id+1)>>8; + cr2->set_source_rgba (0.,0., 0., double(a)/255.); + for (unsigned int i=0; iset_line_width( getOuterLineWidth() ); + currPos = points.at(i); + + if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + else if (datum == CLICKED_POINT) currPos += editBuffer->getDataProvider()->posScreen; + else if (datum == CURSOR) currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + + if (!i) cr2->move_to(currPos.x, currPos.y); + else cr2->line_to(currPos.x, currPos.y); + } + if (filled) { + if (innerLineWidth > 0.) { + cr2->fill_preserve(); + cr2->stroke(); + } + else + cr2->fill(); + } + else + cr2->stroke(); + } + } +} + +EditSubscriber::EditSubscriber () : ID(EUID_None), editingType(ET_PIPETTE), bufferType(BT_SINGLEPLANE_FLOAT), provider(NULL) {} + +void EditSubscriber::setEditProvider(EditDataProvider *provider) { + this->provider = provider; +} + +void EditSubscriber::setEditID(EditUniqueID ID, BufferType buffType) { + this->ID = ID; + bufferType = buffType; +} + +bool EditSubscriber::isCurrentSubscriber() { + //if (provider && provider->getCurrSubscriber()) + // return provider->getCurrSubscriber()->getEditID() == ID; + + if (provider) + return provider->getCurrSubscriber() == this; + + return false; +} + +void EditSubscriber::subscribe() { + if (provider) + provider->subscribe(this); +} + +void EditSubscriber::unsubscribe() { + if (provider) + provider->unsubscribe(); +} + +void EditSubscriber::switchOffEditMode() { + unsubscribe(); +} + +EditUniqueID EditSubscriber::getEditID() { + return ID; +} + +EditType EditSubscriber::getEditingType() { + return editingType; +} + +BufferType EditSubscriber::getEditBufferType() { + return bufferType; +} + + +//-------------------------------------------------------------------------------------------------- + + +EditDataProvider::EditDataProvider() : currSubscriber(NULL), object(0), posScreen(-1,-1), posImage(-1,-1), + deltaScreen(0,0), deltaImage(0,0), deltaPrevScreen(0,0), deltaPrevImage(0,0) { + pipetteVal[0] = pipetteVal[1] = pipetteVal[2] = 0.f; +} + +void EditDataProvider::subscribe(EditSubscriber *subscriber) { + currSubscriber = subscriber; +} + +void EditDataProvider::unsubscribe() { + currSubscriber = NULL; +} + +void EditDataProvider::switchOffEditMode() { + if (currSubscriber) + currSubscriber->switchOffEditMode (); +} + +CursorShape EditDataProvider::getCursor(int objectID) { + return CSOpenHand; +} + +EditSubscriber* EditDataProvider::getCurrSubscriber() { + return currSubscriber; +} + diff --git a/rtgui/edit.h b/rtgui/edit.h new file mode 100644 index 000000000..5a2acf643 --- /dev/null +++ b/rtgui/edit.h @@ -0,0 +1,336 @@ +/* + * 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 _EDIT_H_ +#define _EDIT_H_ + +#include +#include "../rtengine/imagefloat.h" +#include "editid.h" +#include "cursormanager.h" + +class EditDataProvider; + +namespace rtengine { +class EditBuffer; +} + +/** @file + * + * The Edit mechanism is designed to let tools (subscribers) communicate with the preview area (provider). + * Subscribers will be tools that need to create some graphics in the preview area, to let the user interact + * with it in a more user friendly way. + * + * Do not confuse with a _local_ editing, which is another topic implemented in another class. The Edit feature + * is also not supported in batch editing from the File Browser. + * + * Each Edit tool must have a unique ID, that will identify them, and which let the ImProcCoordinator or other + * mechanism act as appropriated. They are all defined in rtgui/editid.h. + * + * ## How does it works? + * + * When a tool is being constructed, unique IDs are affected to the EditSubscribers. Then the EditorPanel class + * will ask all ToolPanel to register the 'after' preview ImageArea object as data provider. The Subscribers + * have now to provide a toggle button to click on to start the Edit listening. When toggling on, the Subscriber + * register itself to the DataProvider, then an event is thrown through the standard ToolPanelListener::panelChanged + * method to update the preview with new graphics to be displayed, and eventually buffers to create and populate. + * + * When the Edit process stops, the Subscriber is removed from the DataProvider, so buffer can be freed up. + * A new ToolPanelListener::panelChanged event is also thrown to update the preview again, without the tool's + * graphical objects. The Edit button is also toggled off (by the user or programmatically). + * + * It means that each Edit buttons toggled on will start an update of the preview which might or might not create + * a new History entry, depending on the event. + * + */ + +// 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 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; + } +}; + +/** @brief Coordinate system where the widgets will be drawn + * + * The EditCoordSystem is used to define a screen and an image coordinate system. + */ +class EditCoordSystem { +public: + virtual ~EditCoordSystem() {} + + /// Convert the widget's DrawingArea (i.e. preview area) coords to the edit buffer coords + virtual void screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy) =0; + /// Convert the widget's DrawingArea (i.e. preview area) coords to the full image coords + virtual void screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy) =0; + /// Convert the image coords to the widget's DrawingArea (i.e. preview area) coords + virtual void imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy) =0; + /// Convert a size value from the preview's scale to the image's scale + virtual int scaleValueToImage (int value) =0; + /// Convert a size value from the preview's scale to the image's scale + virtual float scaleValueToImage (float value) =0; + /// Convert a size value from the preview's scale to the image's scale + virtual double scaleValueToImage (double value) =0; + /// Convert a size value from the image's scale to the preview's scale + virtual int scaleValueToScreen (int value) =0; + /// Convert a size value from the image's scale to the preview's scale + virtual float scaleValueToScreen (float value) =0; + /// Convert a size value from the image's scale to the preview's scale + virtual double scaleValueToScreen (double value) =0; +}; + +class RGBColor { + double r; + double g; + double b; + +public: + RGBColor () : r(0.), g(0.), b(0.) {} + RGBColor (double r, double g, double b) : r(r), g(g), b(b) {} + RGBColor (char r, char g, char b) : r(double(r)/255.), g(double(g)/255.), b(double(b)/255.) {} + + void setColor(double r, double g, double b) { + this->r = r; + this->g = g; + this->b = b; + } + + void setColor(char r, char g, char b) { + r = double(r)/255.; + g = double(g)/255.; + b = double(b)/255.; + } + + double getR() { return r; } + double getG() { return g; } + double getB() { return b; } +}; + +class Geometry { +public: + enum State { + NORMAL, + PRELIGHT, + DRAGGED + }; + enum Datum { + IMAGE, + CLICKED_POINT, + CURSOR + }; + enum Flags { + ACTIVE = 1<<0, // true if the geometry is active and have to be drawn + AUTO_COLOR = 1<<1, // true if the color depend on the state value, not the color field above + }; + +protected: + RGBColor innerLineColor; + RGBColor outerLineColor; + short flags; + +public: + float innerLineWidth; // ...outerLineWidth = innerLineWidth+2 + Datum datum; + State state; // set by the Subscriber + + Geometry () : innerLineColor(), outerLineColor(), flags(ACTIVE|AUTO_COLOR), innerLineWidth(1.f), datum(IMAGE), state(NORMAL) {} + virtual ~Geometry() {} + + void setInnerLineColor (double r, double g, double b) { innerLineColor.setColor(r, g, b); flags &= ~AUTO_COLOR; } + void setInnerLineColor (char r, char g, char b) { innerLineColor.setColor(r, g, b); flags &= ~AUTO_COLOR; } + RGBColor getInnerLineColor (); + void setOuterLineColor (double r, double g, double b) { outerLineColor.setColor(r, g, b); flags &= ~AUTO_COLOR; } + void setOuterLineColor (char r, char g, char b) { outerLineColor.setColor(r, g, b); flags &= ~AUTO_COLOR; } + RGBColor getOuterLineColor (); + double getOuterLineWidth () { return double(innerLineWidth)+2.; } + double getMouseOverLineWidth () { return getOuterLineWidth()+2.; } + void setAutoColor (bool aColor) { if (aColor) flags |= AUTO_COLOR; else flags &= ~AUTO_COLOR; } + void setActive (bool active) { if (active) flags |= ACTIVE; else flags &= ~ACTIVE; } + + virtual void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *parent, EditCoordSystem &coordSystem) =0; + virtual void drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *parent, EditCoordSystem &coordSystem) =0; + virtual void drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *parent, EditCoordSystem &coordSystem) =0; +}; + +class Circle : public Geometry { +public: + Coord center; + int radius; + bool filled; + bool radiusInImageSpace; /// If true, the radius depend on the image scale; if false, it is a fixed 'screen' size + + Circle () : center(100,100), radius(10), filled(false), radiusInImageSpace(false) {} + Circle (Coord ¢er, int radius, bool filled=false) : center(center), radius(radius), filled(filled), radiusInImageSpace(false) {} + Circle (int centerX, int centerY, int radius, bool filled=false) : center(centerX, centerY), radius(radius), filled(filled), radiusInImageSpace(false) {} + + void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); + void drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); + void drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *parent, EditCoordSystem &coordSystem) =0; +}; + +class Line : public Geometry { +public: + Coord begin; + Coord end; + + Line () : begin(10,10), end(100,100) {} + Line (Coord &begin, Coord &end) : begin(begin), end(end) {} + Line (int beginX, int beginY, int endX, int endY) : begin(beginX, beginY), end(endX, endY) {} + + void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); + void drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); + void drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *parent, EditCoordSystem &coordSystem) =0; +}; + +class Polyline : public Geometry { +public: + std::vector points; + bool filled; + + Polyline() : filled(false) {} + + void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); + void drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); + void drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *parent, EditCoordSystem &coordSystem) =0; +}; + +/// @brief Method for client tools needing Edit information +class EditSubscriber { + +public: + +private: + EditUniqueID ID; /// this will be used in improcfun to locate the data that has to be stored in the buffer; it must be unique in RT + EditType editingType; + BufferType bufferType; + EditDataProvider *provider; + +protected: + std::vector visibleGeometry; + std::vector mouseOverGeometry; + +public: + EditSubscriber (); + virtual ~EditSubscriber () {} + + void setEditProvider(EditDataProvider *provider); + EditDataProvider* getEditProvider() { return provider; } + void setEditID(EditUniqueID ID, BufferType buffType); + bool isCurrentSubscriber(); + virtual void subscribe(); + virtual void unsubscribe(); + virtual void switchOffEditMode (); /// Occurs when the user want to stop the editing mode + EditUniqueID getEditID(); + EditType getEditingType(); + BufferType getEditBufferType(); + CursorShape getCursor(int objectID) { return CSOpenHand; } + + /** @brief Triggered when the mouse is moving over an object + This method is also triggered when the cursor is moving over the image in ET_PIPETTE mode + @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...) + @param editBuffer buffer to get the pipette values and the from + @return true if the preview has to be redrawn, false otherwise */ + virtual bool mouseOver(int modifierKey) { return false; } + + /** @brief Triggered when mouse button 1 is pressed, together with the CTRL modifier key if the subscriber is of type ET_PIPETTE + @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...) + @return true if the preview has to be redrawn, false otherwise */ + virtual bool button1Pressed(int modifierKey) { return false; } + + /** @brief Triggered when mouse button 1 is released + @return true if the preview has to be redrawn, false otherwise */ + virtual bool button1Released() { return false; } + + /** @brief Triggered when the user is moving while holding down mouse button 1 + @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...) + @return true if the preview has to be redrawn, false otherwise */ + virtual bool drag(int modifierKey) { return false; } + + /** @brief Get the geometry to be shown to the user */ + const std::vector & getVisibleGeometry() { return visibleGeometry; } + + /** @brief Get the geometry to be drawn in the "mouse over" channel, hidden from the user */ + const std::vector & getMouseOverGeometry() { return mouseOverGeometry; } +}; + +/** @brief Class to handle the furniture of data to the subscribers. + * + * It is admitted that only one Subscriber can ask data at a time. If the Subscriber is of type ET_PIPETTE, it will have to + * trigger the usual event so that the image will be reprocessed to compute the buffer of the current subscriber. + */ +class EditDataProvider { + +private: + EditSubscriber *currSubscriber; + +public: + int object; /// ET_OBJECTS mode: Object detected under the cursor, 0 otherwise; ET_PIPETTE mode: 1 if above the image, 0 otherwise + float pipetteVal[3]; /// Current pipette values; if bufferType==BT_SINGLEPLANE_FLOAT, #2 & #3 will be set to 0 + + Coord posScreen; /// Location of the mouse button press, in preview image space + Coord posImage; /// Location of the mouse button press, in the full image space + Coord deltaScreen; /// Delta relative to posScreen + Coord deltaImage; /// Delta relative to posImage + Coord deltaPrevScreen; /// Delta relative to the previous mouse location, in preview image space + Coord deltaPrevImage; /// Delta relative to the previous mouse location, in the full image space + + EditDataProvider(); + virtual ~EditDataProvider() {} + + virtual void subscribe(EditSubscriber *subscriber); + virtual void unsubscribe(); /// Occurs when the subscriber has been switched off first + virtual void switchOffEditMode (); /// Occurs when the user want to stop the editing mode + virtual CursorShape getCursor(int objectID); + int getPipetteRectSize() { return 8; } // TODO: make a GUI + EditSubscriber* getCurrSubscriber(); +}; + +#endif diff --git a/rtgui/editenums.h b/rtgui/editenums.h index 22631b10f..e92114b58 100644 --- a/rtgui/editenums.h +++ b/rtgui/editenums.h @@ -19,7 +19,9 @@ #ifndef _EDITENUMS_ #define _EDITENUMS_ -enum ImgEditState {SNormal, SCropMove, SHandMove, SResizeW1, SResizeW2, SResizeH1, SResizeH2, SResizeTL, SResizeTR, SResizeBL, SResizeBR, SCropSelecting, SRotateSelecting, SCropWinMove, SCropFrameMove, SCropImgMove, SCropWinResize, SObservedMove}; -enum CursorArea {CropWinButtons, CropToolBar, CropImage, CropBorder, CropTop, CropTopLeft, CropTopRight, CropBottom, CropBottomLeft, CropBottomRight, CropLeft, CropRight, CropInside, CropResize, CropObserved}; +enum ImgEditState {SNormal, SCropMove, SHandMove, SResizeW1, SResizeW2, SResizeH1, SResizeH2, SResizeTL, SResizeTR, SResizeBL, SResizeBR, + SCropSelecting, SRotateSelecting, SCropWinMove, SCropFrameMove, SCropImgMove, SCropWinResize, SObservedMove, SEditDrag}; +enum CursorArea {CropWinButtons, CropToolBar, CropImage, CropBorder, CropTop, CropTopLeft, CropTopRight, CropBottom, CropBottomLeft, + CropBottomRight, CropLeft, CropRight, CropInside, CropResize, CropObserved}; #endif diff --git a/rtgui/editid.h b/rtgui/editid.h new file mode 100644 index 000000000..59f032340 --- /dev/null +++ b/rtgui/editid.h @@ -0,0 +1,61 @@ +/* + * 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 _EDITID_H_ +#define _EDITID_H_ + + +/// @brief List of editing operation +enum EditUniqueID { + EUID_None, /// special value (default) + + EUID_ToneCurve1, + EUID_ToneCurve2, + EUID_Lab_LCurve, + EUID_RGB_R, + EUID_RGB_G, + EUID_RGB_B, + EUID_HSV_H, + EUID_HSV_S, + EUID_HSV_V, + EUID_BlackWhiteLuminance, + EUID_BlackWhiteBeforeCurve, + EUID_BlackWhiteAfterCurve, +}; + +/// @brief Editing mechanisms +// TODO: Look out if it has to be a bitfield to allow both mechanisms at a time +enum EditType { + ET_PIPETTE, /// Will trigger dedicated methods; can have a geometry list to be displayed, but without "mouse over" capabilities + ET_OBJECTS /// The objects are geometrical widgets with "mouse over" capabilities +}; + +/// @brief Buffer type for ET_PIPETTE type editing +enum BufferType { + BT_IMAGEFLOAT, + BT_LABIMAGE, + BT_SINGLEPLANE_FLOAT +}; + +/// @brief Number of object to be handled (for optimization purpose) +enum ObjectMode { + OM_255, /// less or equal than 255 objects + OM_65535 /// less or equal than 65535 objects +}; + +#endif diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index c02bc6edb..08f53aaf2 100755 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -132,6 +132,7 @@ EditorPanel::EditorPanel (FilePanel* filePanel) Gtk::VSeparator* vsepz4 = Gtk::manage (new Gtk::VSeparator ()); iareapanel = new ImageAreaPanel (); + tpc->setEditProvider(iareapanel->imageArea); Gtk::HBox* toolBarPanel = Gtk::manage (new Gtk::HBox ()); toolBarPanel->pack_start (*hidehp, Gtk::PACK_SHRINK, 1); diff --git a/rtgui/flatcurveeditorsubgroup.cc b/rtgui/flatcurveeditorsubgroup.cc index a453b7bdb..93810ae06 100644 --- a/rtgui/flatcurveeditorsubgroup.cc +++ b/rtgui/flatcurveeditorsubgroup.cc @@ -45,7 +45,7 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u CPointsCurve->setType (FCT_MinMaxCPoints); CPointsCurveBox->pack_start (*CPointsCurve, Gtk::PACK_EXPAND_WIDGET, 0); - Gtk::HBox* CPointsbbox = Gtk::manage (new Gtk::HBox ()); + Gtk::HBox*CPointsbbox = Gtk::manage (new Gtk::HBox ()); CPointsbbox->set_spacing(4); pasteCPoints = Gtk::manage (new Gtk::Button ()); @@ -56,11 +56,15 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u saveCPoints->add (*Gtk::manage (new RTImage ("gtk-save-large.png"))); loadCPoints = Gtk::manage (new Gtk::Button ()); loadCPoints->add (*Gtk::manage (new RTImage ("gtk-open.png"))); + editCPoints = Gtk::manage (new Gtk::ToggleButton()); + editCPoints->add (*Gtk::manage (new RTImage ("editmodehand.png"))); + editCPoints->hide(); CPointsbbox->pack_end (*pasteCPoints, Gtk::PACK_SHRINK, 0); CPointsbbox->pack_end (*copyCPoints, Gtk::PACK_SHRINK, 0); CPointsbbox->pack_end (*saveCPoints, Gtk::PACK_SHRINK, 0); CPointsbbox->pack_end (*loadCPoints, Gtk::PACK_SHRINK, 0); + CPointsbbox->pack_start(*editCPoints, Gtk::PACK_SHRINK, 0); CPointsCurveBox->pack_end (*CPointsbbox, Gtk::PACK_SHRINK, 0); CPointsCurveBox->show_all (); @@ -69,6 +73,7 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u loadCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::loadPressed) ); copyCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::copyPressed) ); pasteCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::pastePressed) ); + editCPointsConn = editCPoints->signal_toggled().connect( sigc::bind(sigc::mem_fun(*this, &FlatCurveEditorSubGroup::editToggled), editCPoints) ); saveCPoints->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); loadCPoints->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); @@ -109,6 +114,66 @@ void FlatCurveEditorSubGroup::refresh(CurveEditor *curveToRefresh) { } } +/* + * Switch off the edit button + */ +void FlatCurveEditorSubGroup::editModeSwitchedOff () { + // toggling off all edit buttons, even if only one is toggle on + editCPointsConn.block(true); editCPoints->set_active(false); editCPointsConn.block(false); +} + +void FlatCurveEditorSubGroup::pipetteMouseOver(EditDataProvider *provider, int modifierKey) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((FlatCurveType)(curveEditor->curveType->getSelected())) { + case (FCT_MinMaxCPoints): + CPointsCurve->pipetteMouseOver(provider, modifierKey); + CPointsCurve->setDirty(true); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + +void FlatCurveEditorSubGroup::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((FlatCurveType)(curveEditor->curveType->getSelected())) { + case (FCT_MinMaxCPoints): + CPointsCurve->pipetteButton1Pressed(provider, modifierKey); + CPointsCurve->setDirty(true); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + +void FlatCurveEditorSubGroup::pipetteButton1Released(EditDataProvider *provider) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((FlatCurveType)(curveEditor->curveType->getSelected())) { + case (FCT_MinMaxCPoints): + CPointsCurve->pipetteButton1Released(provider); + CPointsCurve->setDirty(true); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + +void FlatCurveEditorSubGroup::pipetteDrag(EditDataProvider *provider, int modifierKey) { + CurveEditor *curveEditor = static_cast(parent->displayedCurve); + switch((FlatCurveType)(curveEditor->curveType->getSelected())) { + case (FCT_MinMaxCPoints): + CPointsCurve->pipetteDrag(provider, modifierKey); + CPointsCurve->setDirty(true); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } +} + /* * Switch the editor widgets to the currently edited curve */ @@ -182,6 +247,7 @@ void FlatCurveEditorSubGroup::switchGUI() { CPointsCurve->setColorProvider(dCurve->getCurveColorProvider(), dCurve->getCurveCallerId()); CPointsCurve->setColoredBar(leftBar, bottomBar); CPointsCurve->forceResize(); + updateEditButton(dCurve, editCPoints, editCPointsConn); parent->pack_start (*CPointsCurveBox); CPointsCurveBox->check_resize(); break; @@ -291,6 +357,23 @@ void FlatCurveEditorSubGroup::pastePressed () { return; } +void FlatCurveEditorSubGroup::editToggled (Gtk::ToggleButton *button) { + FlatCurveEditor* dCurve = static_cast(parent->displayedCurve); + if (!dCurve) + // should never happen! + return; + + if (button->get_active()) { + dCurve->subscribe(); + CPointsCurve->notifyListener (); + + } + else { + dCurve->unsubscribe(); + } +} + + /* * Store the curves of the currently displayed type from the widgets to the CurveEditor object */ diff --git a/rtgui/flatcurveeditorsubgroup.h b/rtgui/flatcurveeditorsubgroup.h index 95eb90b4b..083a3d420 100644 --- a/rtgui/flatcurveeditorsubgroup.h +++ b/rtgui/flatcurveeditorsubgroup.h @@ -33,10 +33,12 @@ protected: MyFlatCurve* CPointsCurve; - Gtk::Button* saveCPoints; - Gtk::Button* loadCPoints; - Gtk::Button* copyCPoints; - Gtk::Button* pasteCPoints; + Gtk::Button* saveCPoints; + Gtk::Button* loadCPoints; + Gtk::Button* copyCPoints; + Gtk::Button* pasteCPoints; + Gtk::ToggleButton* editCPoints; + sigc::connection editCPointsConn; public: FlatCurveEditorSubGroup(CurveEditorGroup* prt, Glib::ustring& curveDir); @@ -46,6 +48,11 @@ public: //virtual void updateBackgroundHistogram (CurveEditor* ce); void switchGUI(); void refresh(CurveEditor *curveToRefresh); + void editModeSwitchedOff(); + void pipetteMouseOver(EditDataProvider *provider, int modifierKey); + void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); + void pipetteButton1Released(EditDataProvider *provider); + void pipetteDrag(EditDataProvider *provider, int modifierKey); protected: void storeCurveValues (CurveEditor* ce, const std::vector& p); @@ -58,6 +65,7 @@ protected: bool curveReset (int cType, double iValue); void removeEditor (); const std::vector getCurveFromGUI (int type); + void editToggled (Gtk::ToggleButton *button); }; #endif diff --git a/rtgui/hsvequalizer.cc b/rtgui/hsvequalizer.cc index 5af1fae39..c603007ea 100644 --- a/rtgui/hsvequalizer.cc +++ b/rtgui/hsvequalizer.cc @@ -42,16 +42,19 @@ HSVEqualizer::HSVEqualizer () : Gtk::VBox(), FoldableToolPanel(this) { curveEditorG->setCurveListener (this); hshape = static_cast(curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_HUE"))); + hshape->setEditID(EUID_HSV_H, BT_SINGLEPLANE_FLOAT); hshape->setBottomBarBgGradient(bottomMilestones); //hshape->setLeftBarColorProvider(this); Not working yet hshape->setCurveColorProvider(this, 1); sshape = static_cast(curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_SAT"))); + sshape->setEditID(EUID_HSV_S, BT_SINGLEPLANE_FLOAT); sshape->setBottomBarBgGradient(bottomMilestones); //sshape->setLeftBarColorProvider(this); Not working yet sshape->setCurveColorProvider(this, 2); vshape = static_cast(curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_VAL"))); + vshape->setEditID(EUID_HSV_V, BT_SINGLEPLANE_FLOAT); vshape->setBottomBarBgGradient(bottomMilestones); //vshape->setLeftBarColorProvider(this); Not working yet vshape->setCurveColorProvider(this, 3); @@ -88,6 +91,12 @@ void HSVEqualizer::read (const ProcParams* pp, const ParamsEdited* pedited) { enableListener (); } +void HSVEqualizer::setEditProvider (EditDataProvider *provider) { + hshape->setEditProvider(provider); + sshape->setEditProvider(provider); + vshape->setEditProvider(provider); +} + void HSVEqualizer::autoOpenCurve () { // Open up the first curve if selected bool active = hshape->openIfNonlinear(); diff --git a/rtgui/hsvequalizer.h b/rtgui/hsvequalizer.h index 0067672b7..037163f8c 100644 --- a/rtgui/hsvequalizer.h +++ b/rtgui/hsvequalizer.h @@ -46,12 +46,13 @@ public: HSVEqualizer (); virtual ~HSVEqualizer (); - void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); - void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); - void curveChanged (CurveEditor* ce); - //void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); - void setBatchMode (bool batchMode); - void autoOpenCurve (); + void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); + void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); + void curveChanged (CurveEditor* ce); + //void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); + void setBatchMode (bool batchMode); + void setEditProvider (EditDataProvider *provider); + void autoOpenCurve (); virtual void colorForValue (double valX, double valY, int callerId, ColorCaller* caller); //void adjusterChanged (Adjuster* a, double newval); diff --git a/rtgui/imagearea.cc b/rtgui/imagearea.cc index 3e0e5a229..763b0a23f 100644 --- a/rtgui/imagearea.cc +++ b/rtgui/imagearea.cc @@ -204,11 +204,11 @@ bool ImageArea::on_expose_event(GdkEventExpose* event) { bool ImageArea::on_motion_notify_event (GdkEventMotion* event) { if (focusGrabber) - focusGrabber->pointerMoved (event->x, event->y); + focusGrabber->pointerMoved (event->state, event->x, event->y); else { CropWindow* cw = getCropWindow (event->x, event->y); if (cw) - cw->pointerMoved (event->x, event->y); + cw->pointerMoved (event->state, event->x, event->y); } return true; } @@ -256,6 +256,43 @@ bool ImageArea::on_button_release_event (GdkEventButton* event) { return true; } +bool ImageArea::on_leave_notify_event (GdkEventCrossing* event) { + if (focusGrabber) + focusGrabber->leaveNotify (event); + else { + printf("ImageArea::1\n"); + CropWindow* cw = getCropWindow (event->x, event->y); + if (cw) { + printf("ImageArea::appel\n"); + cw->leaveNotify (event); + } + } + return true; +} + +void ImageArea::subscribe(EditSubscriber *subscriber) { + mainCropWindow->cropHandler.setEditSubscriber(subscriber); + for (std::list::iterator i=cropWins.begin(); i!=cropWins.end(); i++) + (*i)->cropHandler.setEditSubscriber(subscriber); + + EditDataProvider::subscribe(subscriber); + + if (listener && listener->getToolBar()) + listener->getToolBar()->startEditMode (); +} + + +void ImageArea::unsubscribe() { + EditDataProvider::unsubscribe(); + // Ask the Crops to free-up edit mode buffers + mainCropWindow->cropHandler.setEditSubscriber(NULL); + for (std::list::iterator i=cropWins.begin(); i!=cropWins.end(); i++) + (*i)->cropHandler.setEditSubscriber(NULL); + setToolHand(); + + if (listener && listener->getToolBar()) + listener->getToolBar()->stopEditMode (); +} void ImageArea::grabFocus (CropWindow* cw) { @@ -469,8 +506,9 @@ ToolMode ImageArea::getToolMode () { void ImageArea::setToolHand () { - if (listener && listener->getToolBar()) - listener->getToolBar()->setTool (TMHand); + if (listener && listener->getToolBar()) { + listener->getToolBar()->setTool (TMHand); + } } int ImageArea::getSpotWBRectSize () { diff --git a/rtgui/imagearea.h b/rtgui/imagearea.h index 97b630e63..43171b123 100644 --- a/rtgui/imagearea.h +++ b/rtgui/imagearea.h @@ -30,9 +30,10 @@ #include "zoompanel.h" #include "indclippedpanel.h" #include "previewmodepanel.h" +#include "edit.h" class ImageAreaPanel; -class ImageArea : public Gtk::DrawingArea, public CropWindowListener { +class ImageArea : public Gtk::DrawingArea, public CropWindowListener, public EditDataProvider { friend class ZoomPanel; @@ -42,9 +43,9 @@ class ImageArea : public Gtk::DrawingArea, public CropWindowListener { Glib::RefPtr ilayout; Glib::RefPtr deglayout; Glib::RefPtr ipixbuf; - bool showClippedH, showClippedS; + bool showClippedH, showClippedS; - ImageAreaPanel* parent; + ImageAreaPanel* parent; std::list cropWins; PreviewHandler* previewHandler; @@ -55,8 +56,8 @@ class ImageArea : public Gtk::DrawingArea, public CropWindowListener { bool dirty; CropWindow* focusGrabber; CropGUIListener* cropgl; - PointerMotionListener* pmlistener; - PointerMotionListener* pmhlistener; + PointerMotionListener* pmlistener; + PointerMotionListener* pmhlistener; ImageAreaToolListener* listener; CropWindow* getCropWindow (int x, int y); @@ -64,21 +65,21 @@ class ImageArea : public Gtk::DrawingArea, public CropWindowListener { public: CropWindow* mainCropWindow; ZoomPanel* zoomPanel; - IndicateClippedPanel* indClippedPanel; - PreviewModePanel* previewModePanel; - ImageArea* iLinkedImageArea; // used to set a reference to the Before image area, which is set when before/after view is enabled + IndicateClippedPanel* indClippedPanel; + PreviewModePanel* previewModePanel; + ImageArea* iLinkedImageArea; // used to set a reference to the Before image area, which is set when before/after view is enabled ImageArea (ImageAreaPanel* p); ~ImageArea (); - + void setImProcCoordinator (rtengine::StagedImageProcessor* ipc_); void setPreviewModePanel(PreviewModePanel* previewModePanel_){previewModePanel = previewModePanel_;}; void setIndicateClippedPanel(IndicateClippedPanel* indClippedPanel_){indClippedPanel = indClippedPanel_;}; - + void getScrollImageSize (int& w, int& h); void getScrollPosition (int& x, int& y); void setScrollPosition (int x, int y); // called by the imageareapanel when the scrollbars have been changed - + // enabling and setting text of info area void setInfoText (Glib::ustring text); void infoEnabled (bool e); @@ -90,6 +91,7 @@ class ImageArea : public Gtk::DrawingArea, public CropWindowListener { bool on_button_press_event (GdkEventButton* event); bool on_button_release_event (GdkEventButton* event); bool on_scroll_event (GdkEventScroll* event); + bool on_leave_notify_event (GdkEventCrossing* event); void on_resized (Gtk::Allocation& req); void styleChanged (const Glib::RefPtr& style); void syncBeforeAfterViews (); @@ -100,7 +102,7 @@ class ImageArea : public Gtk::DrawingArea, public CropWindowListener { void setImageAreaToolListener (ImageAreaToolListener* l) { listener = l; } void setPreviewHandler (PreviewHandler* ph); PreviewHandler* getPreviewHandler () { return previewHandler; } - + void grabFocus (CropWindow* cw); void unGrabFocus (); void addCropWindow (); @@ -112,12 +114,16 @@ class ImageArea : public Gtk::DrawingArea, public CropWindowListener { void spotWBSelected (int x, int y); int getSpotWBRectSize (); void redraw (); - + void zoomFit (); double getZoom (); void setZoom (double zoom); - - // cropwindowlistener interface + + // EditDataProvider interface + void subscribe(EditSubscriber *subscriber); + void unsubscribe(); + + // CropWindowListener interface void cropPositionChanged (CropWindow* cw); void cropWindowSizeChanged (CropWindow* cw); void cropZoomChanged (CropWindow* cw); diff --git a/rtgui/labcurve.cc b/rtgui/labcurve.cc index 3fd6e2f51..38695ce2d 100644 --- a/rtgui/labcurve.cc +++ b/rtgui/labcurve.cc @@ -19,6 +19,7 @@ #include "labcurve.h" #include #include "../rtengine/improcfun.h" +#include "edit.h" using namespace rtengine; using namespace rtengine::procparams; @@ -81,7 +82,8 @@ LCurve::LCurve () : Gtk::VBox(), FoldableToolPanel(this) { lshape = static_cast(curveEditorG->addCurve(CT_Diagonal, "L")); lshape->setTooltip(M("TP_LABCURVE_CURVEEDITOR_LL_TOOLTIP")); - + lshape->setEditID(EUID_Lab_LCurve, BT_SINGLEPLANE_FLOAT); + ashape = static_cast(curveEditorG->addCurve(CT_Diagonal, "a")); ashape->setRangeLabels( M("TP_LABCURVE_CURVEEDITOR_A_RANGE1"), M("TP_LABCURVE_CURVEEDITOR_A_RANGE2"), @@ -275,6 +277,11 @@ void LCurve::autoOpenCurve () { if (!active) clshape->openIfNonlinear(); } +void LCurve::setEditProvider (EditDataProvider *provider) { + lshape->setEditProvider(provider); +} + + void LCurve::write (ProcParams* pp, ParamsEdited* pedited) { pp->labCurve.brightness = brightness->getValue (); diff --git a/rtgui/labcurve.h b/rtgui/labcurve.h index fa24f4eec..590f13e8e 100644 --- a/rtgui/labcurve.h +++ b/rtgui/labcurve.h @@ -64,6 +64,7 @@ class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPan void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); void setBatchMode (bool batchMode); void autoOpenCurve (); + void setEditProvider (EditDataProvider *provider); void setAdjusterBehavior (bool bradd, bool contradd, bool satadd); void trimValues (rtengine::procparams::ProcParams* pp); diff --git a/rtgui/mycurve.cc b/rtgui/mycurve.cc index 8d783be36..6a7f34cec 100644 --- a/rtgui/mycurve.cc +++ b/rtgui/mycurve.cc @@ -21,7 +21,7 @@ #include #include -MyCurve::MyCurve () : listener(NULL) { +MyCurve::MyCurve () : pipetteR(-1.f), pipetteG(-1.f), pipetteB(-1.f), pipetteVal(-1.f), listener(NULL) { cursor_type = CSArrow; graphX = get_allocation().get_width() - RADIUS * 2; diff --git a/rtgui/mycurve.h b/rtgui/mycurve.h index 9eeff7edc..1c87d2c92 100644 --- a/rtgui/mycurve.h +++ b/rtgui/mycurve.h @@ -65,6 +65,9 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { friend class MyCurveIdleHelper; protected: + float pipetteR, pipetteG, pipetteB; /// RGB values from the PipetteDataProvider ; if a channel is set to -1.0f, it is not used + float pipetteVal; /// Effective pipette value, i.e. where to create the point; if a point already exist near this value, it'll be used + CurveListener* listener; ColoredBar *leftBar; ColoredBar *bottomBar; @@ -95,7 +98,7 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { enum ResizeState sized; bool curveIsDirty; - virtual std::vector get_vector (int veclen) = 0; + virtual std::vector get_vector (int veclen) = 0; int getGraphMinSize() { return GRAPH_SIZE + RADIUS + 1; } bool snapCoordinateX(double testedVal, double realVal); bool snapCoordinateY(double testedVal, double realVal); @@ -114,12 +117,17 @@ class MyCurve : public Gtk::DrawingArea, public BackBuffer, public ColorCaller { void forceResize() { sized = RS_Force; } void refresh(); void setCurveDirty () { curveIsDirty = true; } - void styleChanged (const Glib::RefPtr& style); + void styleChanged (const Glib::RefPtr& style); virtual std::vector getPoints () = 0; virtual void setPoints (const std::vector& p) = 0; virtual bool handleEvents (GdkEvent* event) = 0; virtual void reset (double identityValue=0.5) = 0; + virtual void pipetteMouseOver (EditDataProvider *provider, int modifierKey) =0; + virtual void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) =0; + virtual void pipetteButton1Released(EditDataProvider *provider) =0; + virtual void pipetteDrag(EditDataProvider *provider, int modifierKey) =0; + static int getBarWidth() { return options.slimUI ? CBAR_WIDTH_SLIM : CBAR_WIDTH_STD; } }; diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc index 7c5c13b52..2b25462ad 100644 --- a/rtgui/mydiagonalcurve.cc +++ b/rtgui/mydiagonalcurve.cc @@ -228,6 +228,40 @@ void MyDiagonalCurve::draw (int handle) { c = style->get_fg (state); + // draw the pipette values + if (pipetteR > -1.f || pipetteG > -1.f || pipetteB > -1.f) { + cr->set_line_width (0.75); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + int n=0; + if (pipetteR > -1.f) ++n; + if (pipetteG > -1.f) ++n; + if (pipetteB > -1.f) ++n; + + if (n > 1) { + if (pipetteR > -1.f) { + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteR, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + if (pipetteG > -1.f) { + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteG, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + if (pipetteB > -1.f) { + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteB, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + } + if (pipetteVal > -1.f) { + cr->set_source_rgb (1., 0., 0.); + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteVal, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + } + // draw the cage of the NURBS curve if (curve.type==DCT_NURBS) { unsigned int nbPoints; @@ -337,8 +371,6 @@ void MyDiagonalCurve::draw (int handle) { bool MyDiagonalCurve::handleEvents (GdkEvent* event) { CursorShape new_type = cursor_type; - int src, dst; - std::vector::iterator itx, ity; bool retval = false; int num = (int)curve.x.size(); @@ -346,12 +378,12 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { /* graphW and graphH are the size of the graph */ calcDimensions(); - double minDistanceX = double(MIN_DISTANCE) / double(graphW-1); - double minDistanceY = double(MIN_DISTANCE) / double(graphH-1); - if ((graphW < 0) || (graphH < 0)) return false; + double minDistanceX = double(MIN_DISTANCE) / double(graphW-1); + double minDistanceY = double(MIN_DISTANCE) / double(graphH-1); + switch (event->type) { case Gdk::CONFIGURE: { // Happen when the the window is resized @@ -386,11 +418,12 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { snapToElmt = -100; if (curve.type!=DCT_Parametric) { if (event->button.button == 1) { + std::vector::iterator itx, ity; buttonPressed = true; add_modal_grab (); // get the pointer position - getCursorPosition(event); + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); findClosestPoint(); new_type = CSMove; @@ -429,9 +462,11 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { snapToElmt = -100; if (curve.type!=DCT_Parametric) { if (buttonPressed && event->button.button == 1) { + std::vector::iterator itx, ity; + int src, dst; buttonPressed = false; /* get the pointer position */ - getCursorPosition(event); + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); findClosestPoint(); remove_modal_grab (); @@ -499,7 +534,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { snapToElmt = -100; // get the pointer position - getCursorPosition(event); + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); if (grab_point == -1) { // there's no point currently being moved @@ -623,29 +658,283 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { return retval; } -void MyDiagonalCurve::getCursorPosition(GdkEvent* event) { +CursorShape MyDiagonalCurve::motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num) { + CursorShape new_type = type; + + return new_type; +} + +void MyDiagonalCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) { + pipetteR = provider->pipetteVal[0]; + pipetteG = provider->pipetteVal[1]; + pipetteB = provider->pipetteVal[2]; + pipetteVal = 0.f; + int n = 0; + if (pipetteR != -1.f) { + pipetteVal += pipetteR; + ++n; + } + if (pipetteG != -1.f) { + pipetteVal += pipetteG; + ++n; + } + if (pipetteB != -1.f) { + pipetteVal += pipetteB; + ++n; + } + if (n>1) + pipetteVal /= n; + else if (!n) + pipetteVal = -1.f; + + int num = (int)curve.x.size(); + + /* graphW and graphH are the size of the graph */ + calcDimensions(); + + if ((graphW < 0) || (graphH < 0)) + return; + + double minDistanceX = double(MIN_DISTANCE) / double(graphW-1); + double minDistanceY = double(MIN_DISTANCE) / double(graphH-1); + + if (curve.type == DCT_Linear || curve.type == DCT_Spline || curve.type == DCT_NURBS) { + // get the pointer position + int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! + getCursorPosition(Gdk::MOTION_NOTIFY, false, px, graphY, Gdk::ModifierType(modifierKey)); + + if (grab_point == -1) { + // there's no point currently being moved + int previous_lit_point = lit_point; + findClosestPoint(); + if (cursorX<0 || cursorX>graphW || cursorY<0 || cursorY>graphH) { + // the cursor has left the graph area + lit_point = -1; + } + else if (distanceX <= minDistanceX) { + lit_point = closest_point; + } + else { + lit_point = -1; + } + if (lit_point != previous_lit_point) { + setDirty(true); + draw (lit_point); + } + } + } +} + + +void MyDiagonalCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) { + int num = (int)curve.x.size(); + + /* graphW and graphH are the size of the graph */ + calcDimensions(); + + double minDistanceX = double(MIN_DISTANCE) / double(graphW-1); + + if ((graphW < 0) || (graphH < 0)) + return; + + snapToElmt = -100; + if (curve.type!=DCT_Parametric) { + snapToElmt = -100; + if (curve.type!=DCT_Parametric) { + std::vector::iterator itx, ity; + buttonPressed = true; + + // get the pointer position + int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! + getCursorPosition(Gdk::BUTTON_PRESS, false, px, graphY, Gdk::ModifierType(modifierKey)); + findClosestPoint(); + + if (distanceX > minDistanceX) { + rtengine::DiagonalCurve rtCurve(getPoints(), 200); + + /* insert a new control point */ + if (num > 0) { + if (clampedX > curve.x[closest_point]) + ++closest_point; + } + itx = curve.x.begin(); + ity = curve.y.begin(); + for (int i=0; i::iterator itx, ity; + int src, dst; + buttonPressed = false; + /* get the pointer position */ + int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! + getCursorPosition(Gdk::EventType(Gdk::BUTTON_RELEASE), false, graphY, 0, Gdk::ModifierType(0)); + findClosestPoint(); + + int previous_lit_point = lit_point; + /* delete inactive points: */ + itx = curve.x.begin(); + ity = curve.y.begin(); + for (src = dst = 0; src < num; ++src) + if (curve.x[src] >= 0.0) { + curve.x[dst] = curve.x[src]; + curve.y[dst] = curve.y[src]; + ++dst; + ++itx; + ++ity; + } + if (dst < src) { + curve.x.erase (itx, curve.x.end()); + curve.y.erase (ity, curve.y.end()); + if (curve.x.empty()) { + curve.x.push_back (0); + curve.y.push_back (0); + curveIsDirty = true; + setDirty(true); + draw (lit_point); + } + } + if (distanceX <= minDistanceX) { + lit_point = closest_point; + } + else { + lit_point = -1; + } + if (lit_point != previous_lit_point) { + setDirty(true); + draw (lit_point); + } + grab_point = -1; + notifyListener (); + } +} + +void MyDiagonalCurve::pipetteDrag(EditDataProvider *provider, int modifierKey) { + /* graphW and graphH are the size of the graph */ + calcDimensions(); + + double minDistanceX = double(MIN_DISTANCE) / double(graphW-1); + double minDistanceY = double(MIN_DISTANCE) / double(graphH-1); + + if ((graphW < 0) || (graphH < 0)) + return; + + getCursorPosition(Gdk::MOTION_NOTIFY, false, cursorX+graphX, graphY+provider->deltaScreen.y, Gdk::ModifierType(modifierKey)); + + // bounds of the grabbed point + double const bottomBound = 0.; + double const topBound = 1.; + + double bottomDeletionBound = bottomBound - minDistanceY; + double topDeletionBound = topBound + minDistanceY; + + // we memorize the previous position of the point, for optimization purpose + double prevPosX = curve.x[grab_point]; + double prevPosY = curve.y[grab_point]; + + // we memorize the previous position of the point, for optimization purpose + ugpX += deltaX; + ugpY += deltaY; + + // the unclamped grabbed point is brought back in the range + ugpY = CLAMP(ugpY, 0.0, 1.0); + + // snapping point to specific values + if (snapTo && curve.x[grab_point] != -1.) { + if (grab_point > 0 && grab_point < (curve.y.size()-1)) { + double prevX = curve.x[grab_point-1]; + double prevY = curve.y[grab_point-1]; + double nextX = curve.x[grab_point+1]; + double nextY = curve.y[grab_point+1]; + + double ratio = (curve.x[grab_point]-prevX)/(nextX-prevX); + double y = (nextY-prevY) * ratio + prevY; + + if (snapCoordinateY(y, ugpY)) snapToElmt = 1000+grab_point; + } + if (grab_point > 0) { + int prevP = grab_point-1; + if (snapCoordinateY(curve.y[prevP], ugpY)) snapToElmt = prevP; + } + if (grab_point < (curve.y.size()-1)) { + int nextP = grab_point+1; + if (snapCoordinateY(curve.y[nextP], ugpY)) snapToElmt = nextP; + } + if (snapCoordinateY(1.0, ugpY)) snapToElmt = -3; + if (snapCoordinateY(curve.x[grab_point], ugpY)) snapToElmt = -2; + if (snapCoordinateY(0.0, ugpY)) snapToElmt = -1; + + curve.y[grab_point] = snapToValY; + } + else { + // nextPosY is in the bounds + curve.y[grab_point] = CLAMP(ugpY, 0.0, 1.0); + } + + if (curve.x[grab_point] != prevPosX || curve.y[grab_point] != prevPosY) { + // we recalculate the curve only if we have to + curveIsDirty = true; + setDirty(true); + draw (lit_point); + notifyListener (); + } +} + +void MyDiagonalCurve::getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey) { int tx, ty; int prevCursorX, prevCursorY; double incrementX = 1. / double(graphW); double incrementY = 1. / double(graphH); // getting the cursor position - switch (event->type) { + switch (evType) { case (Gdk::MOTION_NOTIFY) : - if (event->motion.is_hint) { + if (isHint) { get_window()->get_pointer (tx, ty, mod_type); } else { - tx = int(event->button.x); - ty = int(event->button.y); - mod_type = (Gdk::ModifierType)event->button.state; + tx = evX; + ty = evY; + mod_type = modifierKey; } break; case (Gdk::BUTTON_PRESS) : case (Gdk::BUTTON_RELEASE) : - tx = int(event->button.x); - ty = int(event->button.y); - mod_type = (Gdk::ModifierType)event->button.state; + tx = evX; + ty = evY; + mod_type = modifierKey; break; default : // The cursor position is not available diff --git a/rtgui/mydiagonalcurve.h b/rtgui/mydiagonalcurve.h index aebd01b08..4aaad9e55 100644 --- a/rtgui/mydiagonalcurve.h +++ b/rtgui/mydiagonalcurve.h @@ -65,8 +65,9 @@ class MyDiagonalCurve : public MyCurve { bool bghistvalid; void draw (int handle); void interpolate (); - void getCursorPosition(GdkEvent* event); + void getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey); void findClosestPoint(); + CursorShape motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num); std::vector get_vector (int veclen); public: @@ -79,6 +80,11 @@ class MyDiagonalCurve : public MyCurve { void setActiveParam (int ac); void reset (double identityValue=0.5); void updateBackgroundHistogram (LUTu & hist); + + void pipetteMouseOver (EditDataProvider *provider, int modifierKey); + void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); + void pipetteButton1Released(EditDataProvider *provider); + void pipetteDrag(EditDataProvider *provider, int modifierKey); }; #endif diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc index 91241a30c..5c2f5b3d4 100644 --- a/rtgui/myflatcurve.cc +++ b/rtgui/myflatcurve.cc @@ -166,8 +166,41 @@ void MyFlatCurve::draw () { cr->set_line_cap(Cairo::LINE_CAP_BUTT); + // draw the pipette values + if (pipetteR > -1.f || pipetteG > -1.f || pipetteB > -1.f) { + cr->set_line_width (0.75); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + int n=0; + if (pipetteR > -1.f) ++n; + if (pipetteG > -1.f) ++n; + if (pipetteB > -1.f) ++n; + + if (n > 1) { + if (pipetteR > -1.f) { + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteR, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + if (pipetteG > -1.f) { + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteG, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + if (pipetteB > -1.f) { + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteB, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + } + if (pipetteVal > -1.f) { + cr->set_source_rgb (1., 0., 0.); + cr->move_to (double(graphX)+1.5 + double(graphW-3)*pipetteVal, double(graphY)-1.5); + cr->rel_line_to (0, double(-graphH+3)); + cr->stroke (); + } + } // draw the color feedback of the control points - if (colorProvider) { + else if (colorProvider) { //if (curve.type!=FCT_Parametric) for (int i=0; i<(int)curve.x.size(); ++i) { @@ -424,7 +457,6 @@ bool MyFlatCurve::getHandles(int n) { bool MyFlatCurve::handleEvents (GdkEvent* event) { CursorShape new_type = cursor_type; - int src, dst; std::vector::iterator itx, ity, itlt, itrt; snapToElmt = -100; @@ -434,12 +466,12 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { /* graphW and graphH are the size of the graph */ calcDimensions(); - minDistanceX = double(MIN_DISTANCE) / double(graphW-1); - minDistanceY = double(MIN_DISTANCE) / double(graphH-1); - if ((graphW < 0) || (graphH < 0)) return false; + minDistanceX = double(MIN_DISTANCE) / double(graphW-1); + minDistanceY = double(MIN_DISTANCE) / double(graphH-1); + switch (event->type) { case Gdk::CONFIGURE: { // Happen when the the window is resized @@ -477,7 +509,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { add_modal_grab (); // get the pointer position - getCursorPosition(event); + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); getMouseOverArea(); // hide the tangent handles @@ -563,8 +595,8 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { case Gdk::BUTTON_RELEASE: //if (curve.type!=FCT_Parametric) { if (buttonPressed && event->button.button == 1) { + int src, dst; buttonPressed = false; - enum MouseOverAreas prevArea = area; remove_modal_grab (); // Removing any deleted point if we were previously modifying the point position @@ -607,7 +639,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { lit_point = -1; // get the pointer position - getCursorPosition(event); + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); getMouseOverArea(); switch (area) { @@ -659,7 +691,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { snapToElmt = -100; // get the pointer position - getCursorPosition(event); + getCursorPosition(Gdk::EventType(event->type), event->motion.is_hint!=0, int(event->button.x), int(event->button.y), Gdk::ModifierType(event->button.state)); getMouseOverArea(); if (editedHandle == FCT_EditedHandle_CPointUD) { @@ -868,6 +900,158 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { return retval; } +void MyFlatCurve::pipetteMouseOver (EditDataProvider *provider, int modifierKey) { + pipetteR = provider->pipetteVal[0]; + pipetteG = provider->pipetteVal[1]; + pipetteB = provider->pipetteVal[2]; + pipetteVal = 0.f; + int n = 0; + if (pipetteR != -1.f) { + pipetteVal += pipetteR; + ++n; + } + if (pipetteG != -1.f) { + pipetteVal += pipetteG; + ++n; + } + if (pipetteB != -1.f) { + pipetteVal += pipetteB; + ++n; + } + if (n>1) + pipetteVal /= n; + else if (!n) + pipetteVal = -1.f; + + snapToElmt = -100; + + /* graphW and graphH are the size of the graph */ + calcDimensions(); + + if ((graphW < 0) || (graphH < 0)) + return; + + int previous_lit_point = lit_point; + + // hide the handles + tanHandlesDisplayed = false; + + // get the pointer position + int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! + getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(modifierKey)); + getMouseOverArea(); + + if (area==FCT_Area_Point) + area = FCT_Area_V; + + snapToMinDistY = snapToMinDistX = 10.; + snapToValY = snapToValX = 0.; + + if (editedHandle==FCT_EditedHandle_None && lit_point != previous_lit_point) { + setDirty(true); + draw (); + } +} + +void MyFlatCurve::pipetteButton1Pressed(EditDataProvider *provider, int modifierKey) { + buttonPressed = true; + + // get the pointer position + int px = graphX + int(float(graphW)*pipetteVal); // WARNING: converting pipetteVal from float to int, precision loss here! + getCursorPosition(Gdk::EventType(Gdk::BUTTON_PRESS), false, px, graphY, Gdk::ModifierType(modifierKey)); + getMouseOverArea(); + + // hide the tangent handles + tanHandlesDisplayed = false; + + // Action on BUTTON_PRESS and no edited point + switch (area) { + + case (FCT_Area_Insertion): + { + rtengine::FlatCurve rtCurve(getPoints(), 200); + + std::vector::iterator itx, ity, itlt, itrt; + int num = (int)curve.x.size(); + + /* insert a new control point */ + if (num > 0) { + if (clampedX > curve.x[closest_point]) + ++closest_point; + } + itx = curve.x.begin(); + ity = curve.y.begin(); + itlt = curve.leftTangent.begin(); + itrt = curve.rightTangent.begin(); + for (int i=0; ideltaScreen.y, Gdk::ModifierType(modifierKey)); + getMouseOverArea(); + + if (editedHandle == FCT_EditedHandle_CPointY) { + movePoint(false, true); + } +} + void MyFlatCurve::movePoint(bool moveX, bool moveY) { // bounds of the grabbed point @@ -1055,28 +1239,28 @@ void MyFlatCurve::movePoint(bool moveX, bool moveY) { } // Set datas relative to cursor position -void MyFlatCurve::getCursorPosition(GdkEvent* event) { +void MyFlatCurve::getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifierKey) { int tx, ty; int prevCursorX, prevCursorY; double incrementX = 1. / double(graphW); double incrementY = 1. / double(graphH); - switch (event->type) { + switch (evType) { case (Gdk::MOTION_NOTIFY) : - if (event->motion.is_hint) { + if (isHint) { get_window()->get_pointer (tx, ty, mod_type); } else { - tx = (int)event->button.x; - ty = (int)event->button.y; - mod_type = (Gdk::ModifierType)event->button.state; + tx = evX; + ty = evY; + mod_type = modifierKey; } break; case (Gdk::BUTTON_PRESS) : case (Gdk::BUTTON_RELEASE) : - tx = (int)event->button.x; - ty = (int)event->button.y; - mod_type = (Gdk::ModifierType)event->button.state; + tx = evX; + ty = evY; + mod_type = modifierKey; break; default : // The cursor position is not available @@ -1170,6 +1354,7 @@ void MyFlatCurve::getMouseOverArea () { } } } + if (minDist <= minDistanceX) { // the cursor is over the point area = FCT_Area_Point; diff --git a/rtgui/myflatcurve.h b/rtgui/myflatcurve.h index 7f9db613f..879a8e0fd 100644 --- a/rtgui/myflatcurve.h +++ b/rtgui/myflatcurve.h @@ -105,9 +105,10 @@ class MyFlatCurve : public MyCurve { void movePoint(bool moveX, bool moveY); void defaultCurve (double iVal=0.5); void interpolate (); - void getCursorPosition(GdkEvent* event); + void getCursorPosition(Gdk::EventType evType, bool isHint, int evX, int evY, Gdk::ModifierType modifier); void getMouseOverArea (); bool getHandles(int n); + CursorShape motionNotify(CursorShape type, double minDistanceX, double minDistanceY, int num); std::vector get_vector (int veclen); public: @@ -120,6 +121,11 @@ class MyFlatCurve : public MyCurve { bool handleEvents (GdkEvent* event); void reset (double identityValue=0.5); //void updateBackgroundHistogram (unsigned int* hist); + + void pipetteMouseOver (EditDataProvider *provider, int modifierKey); + void pipetteButton1Pressed(EditDataProvider *provider, int modifierKey); + void pipetteButton1Released(EditDataProvider *provider); + void pipetteDrag(EditDataProvider *provider, int modifierKey); }; #endif diff --git a/rtgui/rgbcurves.cc b/rtgui/rgbcurves.cc index 07c169ab5..b73ec3b3d 100644 --- a/rtgui/rgbcurves.cc +++ b/rtgui/rgbcurves.cc @@ -44,6 +44,7 @@ RGBCurves::RGBCurves () : Gtk::VBox(), FoldableToolPanel(this) { curveEditorG->setCurveListener (this); Rshape = static_cast(curveEditorG->addCurve(CT_Diagonal, M("TP_RGBCURVES_RED"))); + Rshape->setEditID(EUID_RGB_R, BT_SINGLEPLANE_FLOAT); milestones.push_back( GradientMilestone(0.0, 0.0, 0.0, 0.0) ); milestones.push_back( GradientMilestone(1.0, 1.0, 0.0, 0.0) ); Rshape->setBottomBarBgGradient(milestones); @@ -51,11 +52,13 @@ RGBCurves::RGBCurves () : Gtk::VBox(), FoldableToolPanel(this) { milestones[1].r = 0.0; milestones[1].g = 1.0; Gshape = static_cast(curveEditorG->addCurve(CT_Diagonal, M("TP_RGBCURVES_GREEN"))); + Gshape->setEditID(EUID_RGB_G, BT_SINGLEPLANE_FLOAT); Gshape->setBottomBarBgGradient(milestones); Gshape->setLeftBarBgGradient(milestones); milestones[1].g = 0.0; milestones[1].b = 1.0; Bshape = static_cast(curveEditorG->addCurve(CT_Diagonal, M("TP_RGBCURVES_BLUE"))); + Bshape->setEditID(EUID_RGB_G, BT_SINGLEPLANE_FLOAT); Bshape->setBottomBarBgGradient(milestones); Bshape->setLeftBarBgGradient(milestones); @@ -94,6 +97,12 @@ void RGBCurves::read (const ProcParams* pp, const ParamsEdited* pedited) { enableListener (); } +void RGBCurves::setEditProvider (EditDataProvider *provider) { + Rshape->setEditProvider(provider); + Gshape->setEditProvider(provider); + Bshape->setEditProvider(provider); +} + void RGBCurves::autoOpenCurve () { // Open up the first curve if selected bool active = Rshape->openIfNonlinear(); diff --git a/rtgui/rgbcurves.h b/rtgui/rgbcurves.h index e3a3d4218..5c439c5c7 100644 --- a/rtgui/rgbcurves.h +++ b/rtgui/rgbcurves.h @@ -43,10 +43,11 @@ class RGBCurves : public Gtk::VBox, public AdjusterListener, public FoldableTool RGBCurves (); ~RGBCurves (); - void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); - void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); - void setBatchMode (bool batchMode); - void autoOpenCurve (); + void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); + void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); + void setBatchMode (bool batchMode); + void setEditProvider (EditDataProvider *provider); + void autoOpenCurve (); void curveChanged (CurveEditor* ce); void updateCurveBackgroundHistogram (LUTu & histToneCurve, LUTu & histLCurve, LUTu & histCCurve, LUTu & histCLurve, LUTu & histLLCurve, LUTu & histLCAM, LUTu & histCCAM, LUTu & histRed, LUTu & histGreen, LUTu & histBlue, LUTu & histLuma); diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc index 7a15af731..3f25affd5 100644 --- a/rtgui/tonecurve.cc +++ b/rtgui/tonecurve.cc @@ -21,6 +21,7 @@ #include #include #include "ppversion.h" +#include "edit.h" using namespace rtengine; using namespace rtengine::procparams; @@ -132,6 +133,7 @@ ToneCurve::ToneCurve () : Gtk::VBox(), FoldableToolPanel(this) { curveEditorG->setCurveListener (this); shape = static_cast(curveEditorG->addCurve(CT_Diagonal, "", toneCurveMode)); + shape->setEditID(EUID_ToneCurve1, BT_IMAGEFLOAT); shape->setBottomBarBgGradient(bottomMilestones); shape->setLeftBarBgGradient(bottomMilestones); @@ -156,6 +158,7 @@ ToneCurve::ToneCurve () : Gtk::VBox(), FoldableToolPanel(this) { curveEditorG2->setCurveListener (this); shape2 = static_cast(curveEditorG2->addCurve(CT_Diagonal, "", toneCurveMode2)); + shape2->setEditID(EUID_ToneCurve2, BT_IMAGEFLOAT); shape2->setBottomBarBgGradient(bottomMilestones); shape2->setLeftBarBgGradient(bottomMilestones); @@ -267,6 +270,11 @@ void ToneCurve::autoOpenCurve () { shape2->openIfNonlinear(); } +void ToneCurve::setEditProvider (EditDataProvider *provider) { + shape->setEditProvider(provider); + shape2->setEditProvider(provider); +} + void ToneCurve::write (ProcParams* pp, ParamsEdited* pedited) { pp->toneCurve.autoexp = autolevels->get_active(); diff --git a/rtgui/tonecurve.h b/rtgui/tonecurve.h index b5fc5c9d0..eacf47f42 100644 --- a/rtgui/tonecurve.h +++ b/rtgui/tonecurve.h @@ -82,6 +82,7 @@ class ToneCurve : public Gtk::VBox, public AdjusterListener, public FoldableTool void setAdjusterBehavior (bool expadd, bool hlcompadd, bool hlcompthreshadd, bool bradd, bool blackadd, bool shcompadd, bool contradd, bool satadd); void trimValues (rtengine::procparams::ProcParams* pp); void autoOpenCurve (); + void setEditProvider (EditDataProvider *provider); void adjusterChanged (Adjuster* a, double newval); diff --git a/rtgui/toolbar.cc b/rtgui/toolbar.cc index c90677dcd..7629c4a14 100644 --- a/rtgui/toolbar.cc +++ b/rtgui/toolbar.cc @@ -18,13 +18,18 @@ */ #include "toolbar.h" #include "multilangmgr.h" -#include "rtimage.h" #include "guiutils.h" ToolBar::ToolBar () : listener (NULL) { + editingMode = false; + + handimg = Gtk::manage (new RTImage ("openhand.png")); + handimg->reference(); + editinghandimg = Gtk::manage (new RTImage ("editmodehand.png")); + editinghandimg->reference(); + handTool = Gtk::manage (new Gtk::ToggleButton ()); - Gtk::Image* handimg = Gtk::manage (new RTImage ("openhand.png")); handTool->add (*handimg); handimg->show (); handTool->set_relief(Gtk::RELIEF_NONE); @@ -74,6 +79,11 @@ ToolBar::ToolBar () : listener (NULL) { straTool->set_tooltip_markup (M("TOOLBAR_TOOLTIP_STRAIGHTEN")); } +ToolBar::~ToolBar () { + handimg->unreference(); + editinghandimg->unreference(); + +} // // Selects the desired tool without notifying the listener // @@ -84,6 +94,8 @@ void ToolBar::setTool (ToolMode tool) { if (wbTool) wbConn.block (true); straConn.block (true); + bool stopEdit = tool==TMHand && handTool->get_active() && editingMode; + handTool->set_active (false); if (wbTool) wbTool->set_active (false); cropTool->set_active (false); @@ -91,7 +103,7 @@ void ToolBar::setTool (ToolMode tool) { if (tool==TMHand){ handTool->set_active (true); - handTool->grab_focus();; // switch focus to the handTool button + handTool->grab_focus(); // switch focus to the handTool button } else if (tool==TMSpotWB) { if (wbTool) wbTool->set_active (true); @@ -101,12 +113,44 @@ void ToolBar::setTool (ToolMode tool) { else if (tool==TMStraighten) straTool->set_active (true); - current = tool; + current = tool; handConn.block (false); cropConn.block (false); if (wbTool) wbConn.block (false); straConn.block (false); + + if (stopEdit) { + stopEditMode(); + if (listener) + listener->editModeSwitchedOff(); + } +} + +void ToolBar::startEditMode() { + if (!editingMode) { + handTool->set_active(true); // will call hand_pressed, with editingMode=false + editingMode = true; + handTool->set_image(*editinghandimg); + } + #ifndef NDEBUG + else + printf("Editing mode already active!\n"); + #endif +} + +void ToolBar::stopEditMode() { + if (editingMode) { + editingMode = false; + /* WARNING: Should we toggle the Hand button on? + * This method can be called while another tool is active, e.g. if the user toggle off + * the Subscriber's Edit button. For now, we keep that other tool active. If one want to + * switch to the Hand tool, uncommenting the following line should suffice (not tested). + * + * handTool->set_active(true); + */ + handTool->set_image(*handimg); + } } void ToolBar::hand_pressed () { @@ -121,6 +165,12 @@ void ToolBar::hand_pressed () { straTool->set_active (false); current = TMHand; } + else { + if (editingMode) + stopEditMode(); + if (listener) + listener->editModeSwitchedOff (); + } handTool->set_active (true); handConn.block (false); cropConn.block (false); diff --git a/rtgui/toolbar.h b/rtgui/toolbar.h index 3217f33a0..3ec50d65a 100644 --- a/rtgui/toolbar.h +++ b/rtgui/toolbar.h @@ -21,15 +21,23 @@ #include #include "toolenum.h" +#include "rtimage.h" class ToolBarListener { public: + virtual ~ToolBarListener() {} + /// Callback when a tool is selected virtual void toolSelected (ToolMode tool) {} + /// Callback when the Edit mode is stopped + virtual void editModeSwitchedOff () {} }; class ToolBar : public Gtk::HBox { + private: + RTImage* handimg; + RTImage* editinghandimg; protected: Gtk::ToggleButton* handTool; @@ -38,6 +46,7 @@ class ToolBar : public Gtk::HBox { Gtk::ToggleButton* straTool; ToolBarListener* listener; ToolMode current; + bool editingMode; // true if the cursor is being used to remotely edit tool's values sigc::connection handConn; sigc::connection wbConn; sigc::connection cropConn; @@ -45,12 +54,16 @@ class ToolBar : public Gtk::HBox { public: ToolBar (); + ~ToolBar (); void setTool (ToolMode tool); ToolMode getTool () { return current; } void setToolBarListener (ToolBarListener* tpl) { listener = tpl; } + void startEditMode(); + void stopEditMode(); + void hand_pressed (); void wb_pressed (); void crop_pressed (); diff --git a/rtgui/toolpanel.h b/rtgui/toolpanel.h index cae6cd949..9478d6a6e 100644 --- a/rtgui/toolpanel.h +++ b/rtgui/toolpanel.h @@ -25,6 +25,7 @@ #include "../rtengine/procparams.h" #include "multilangmgr.h" #include "paramsedited.h" +#include "edit.h" class ToolPanel; class FoldableToolPanel; @@ -54,6 +55,7 @@ class ToolPanel { Gtk::Box* getParent () { return NULL; } void setMultiImage (bool m) { multiImage = m; } void setListener (ToolPanelListener* tpl) { listener = tpl; } + virtual void setEditProvider (EditDataProvider *provider) {} virtual void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL) {} virtual void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL) {} virtual void trimValues (rtengine::procparams::ProcParams* pp) { return; } diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 729043ae0..f2d408f66 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -644,7 +644,19 @@ void ToolPanelCoordinator::toolSelected (ToolMode tool) { } } +void ToolPanelCoordinator::editModeSwitchedOff () { + if (editDataProvider) { + editDataProvider->switchOffEditMode(); + } +} + void ToolPanelCoordinator::dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile) { flatfield->setShortcutPath(dirname); } + +void ToolPanelCoordinator::setEditProvider(EditDataProvider *provider) { + editDataProvider = provider; + for (size_t i=0; isetEditProvider(provider); +} diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 47415a33a..f5a73739c 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -176,6 +176,10 @@ class ToolPanelCoordinator : public ToolPanelListener, void updateVScrollbars (bool hide); void updateTabsHeader (bool useIcons); + private: + + EditDataProvider *editDataProvider; + public: CoarsePanel* coarse; @@ -252,7 +256,11 @@ class ToolPanelCoordinator : public ToolPanelListener, void updateTabsUsesIcons (bool useIcons); bool handleShortcutKey (GdkEventKey* event); + // ToolBarListener interface void toolSelected (ToolMode tool); + void editModeSwitchedOff (); + + void setEditProvider(EditDataProvider *provider); }; #endif diff --git a/tools/source_icons/scalable/editmodehand.file b/tools/source_icons/scalable/editmodehand.file new file mode 100644 index 000000000..93df595c9 --- /dev/null +++ b/tools/source_icons/scalable/editmodehand.file @@ -0,0 +1 @@ +editmodehand.png,w22,actions diff --git a/tools/source_icons/scalable/editmodehand.svg b/tools/source_icons/scalable/editmodehand.svg new file mode 100644 index 000000000..9a9898e68 --- /dev/null +++ b/tools/source_icons/scalable/editmodehand.svg @@ -0,0 +1,581 @@ + + + +image/svg+xml \ No newline at end of file