From 8d70ca0f41065ee07c406c9c49bd4384ef2ec166 Mon Sep 17 00:00:00 2001 From: Hombre Date: Tue, 18 Feb 2014 02:14:27 +0100 Subject: [PATCH] On preview widgets for the Gradient tool (center & angle). Also includes some rudimentary bounds checking (optional) when accessing some kind of array. --- rtengine/LUT.h | 35 +- rtengine/alignedbuffer.h | 13 +- rtengine/dcrop.cc | 8 + rtengine/dcrop.h | 3 +- rtengine/editbuffer.cc | 74 ++-- rtengine/editbuffer.h | 3 +- rtengine/iimage.h | 130 ++++++- rtgui/cropwindow.cc | 134 +++++-- rtgui/cropwindow.h | 4 +- rtgui/curveeditor.cc | 2 +- rtgui/diagonalcurveeditorsubgroup.cc | 3 +- rtgui/edit.cc | 175 ++++++--- rtgui/edit.h | 122 ++++++- rtgui/gradient.cc | 235 +++++++++++- rtgui/gradient.h | 27 +- rtgui/imagearea.cc | 18 +- tools/coordinate_system.svg | 525 +++++++++++++++++++++++++++ 17 files changed, 1338 insertions(+), 173 deletions(-) create mode 100644 tools/coordinate_system.svg diff --git a/rtengine/LUT.h b/rtengine/LUT.h index 2560c6929..8dfb0ba6f 100644 --- a/rtengine/LUT.h +++ b/rtengine/LUT.h @@ -75,6 +75,7 @@ #include "sleefsseavx.c" #endif #include +#include "rt_math.h" template class LUT { @@ -204,15 +205,7 @@ public: } // use with integer indices T& operator[](int index) const { - if (((unsigned int)index)(index, 0, size-1) ]; } #if defined( __SSE2__ ) && defined( __x86_64__ ) @@ -325,19 +318,17 @@ public: // use with float indices T operator[](float index) const { int idx = (int)index; // don't use floor! The difference in negative space is no problems here - if (((unsigned int)idx) > maxs) { - if (idx<0) - { - if (clip & LUT_CLIP_BELOW) - return data[0]; - idx=0; - } - else - { - if (clip & LUT_CLIP_ABOVE) - return data[size - 1]; - idx =maxs; - } + if (index<0.f) + { + if (clip & LUT_CLIP_BELOW) + return data[0]; + idx=0; + } + else if (index > float(maxs)) + { + if (clip & LUT_CLIP_ABOVE) + return data[size - 1]; + idx =maxs; } float diff = index - (float) idx; T p1 = data[idx]; diff --git a/rtengine/alignedbuffer.h b/rtengine/alignedbuffer.h index 78b8394ef..ce2f1a421 100644 --- a/rtengine/alignedbuffer.h +++ b/rtengine/alignedbuffer.h @@ -31,6 +31,7 @@ private: void* real ; char alignment; size_t allocatedSize; + int unitSize; public: T* data ; @@ -40,7 +41,7 @@ public: * @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) { + AlignedBuffer (size_t size=0, size_t align=16) : real(NULL), alignment(align), allocatedSize(0), unitSize(0), data(NULL), inUse(false) { if (size) resize(size); } @@ -69,11 +70,12 @@ public: data = NULL; inUse = false; allocatedSize = 0; + unitSize = 0; } else { - int sSize = structSize ? structSize : sizeof(T); + unitSize = structSize ? structSize : sizeof(T); size_t oldAllocatedSize = allocatedSize; - allocatedSize = size*sSize; + allocatedSize = size*unitSize; // 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, @@ -93,6 +95,7 @@ public: } else { allocatedSize = 0; + unitSize = 0; data = NULL; inUse = false; return false; @@ -123,6 +126,10 @@ public: other.inUse = inUse; inUse = tmpInUse; } + + unsigned int getSize() { + return unitSize ? allocatedSize/unitSize : 0; + } }; // Multi processor version, use with OpenMP diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 15b606cb5..e3a27c167 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -23,6 +23,7 @@ #include "rt_math.h" #include "colortemp.h" +// "ceil" rounding #define SKIPS(a,b) ((a) / (b) + ((a) % (b) > 0)) namespace rtengine { @@ -95,6 +96,13 @@ void Crop::setEditSubscriber(EditSubscriber* newSubscriber) { EditBuffer::singlePlaneBuffer.flushData(); } } + if (newSubscriber == NULL && oldSubscriber != NULL && oldSubscriber->getEditingType() == ET_OBJECTS) { + printf("Free object buffers\n"); + EditBuffer::resize(0, 0); // This will delete the objects buffer + } + else if (newSubscriber && newSubscriber->getEditingType() == ET_OBJECTS) { + EditBuffer::resize(cropw, croph, newSubscriber); + } // If oldSubscriber == NULL && newSubscriber != NULL -> the image will be allocated when necessary } diff --git a/rtengine/dcrop.h b/rtengine/dcrop.h index ab3d3eab6..a79815f9f 100644 --- a/rtengine/dcrop.h +++ b/rtengine/dcrop.h @@ -58,7 +58,8 @@ class Crop : public DetailedCrop, public EditBuffer { int cropx, cropy, cropw, croph; /// size of the detail crop image ('skip' taken into account), with border int trafx, trafy, trafw, trafh; /// the size and position to get from the imagesource that is transformed to the requested crop area int rqcropx, rqcropy, rqcropw, rqcroph; /// size of the requested detail crop image (the image might be smaller) (without border) - int borderRequested, upperBorder, leftBorder; + int borderRequested; /// requested extra border size for image processing + int upperBorder, leftBorder; /// extra border size really allocated for image processing bool cropAllocated; DetailedCropListener* cropImageListener; diff --git a/rtengine/editbuffer.cc b/rtengine/editbuffer.cc index 84b6169c8..5adda4b94 100644 --- a/rtengine/editbuffer.cc +++ b/rtengine/editbuffer.cc @@ -36,6 +36,7 @@ EditBuffer::~EditBuffer() { } void EditBuffer::createBuffer(int width, int height) { + //printf("Appel de createBuffer %d x %d\n", width, height); resize (width, height); } @@ -76,30 +77,33 @@ EditUniqueID EditBuffer::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(); + resize(newWidth, newHeight, dataProvider ? dataProvider->getCurrSubscriber() : NULL); +} - if (!objectMap) { +// Resize buffers if they already exist +void EditBuffer::resize(int newWidth, int newHeight, EditSubscriber* newSubscriber) { + if (newSubscriber) { + if (newSubscriber->getEditingType() == ET_OBJECTS) { + if (objectMap && (objectMap->get_width() != newWidth || objectMap->get_height() != newHeight)) + objectMap.clear(); + + if (!objectMap && newWidth && newHeight) { 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(); + objectMap2.clear(); } } - if (!objectMap2) { + if (!objectMap2 && newWidth && newHeight) { objectMap2 = Cairo::ImageSurface::create(Cairo::FORMAT_A8, newWidth, newHeight); } } // OM_255 -> deleting objectMap2, if any else if (objectMap2) - objectMap2->unreference(); + objectMap2.clear(); // Should never happen! if (imgFloatBuffer) { @@ -115,8 +119,8 @@ void EditBuffer::resize(int newWidth, int newHeight) { } } - if (subscriber->getEditingType() == ET_PIPETTE) { - if (subscriber->getEditBufferType() == BT_IMAGEFLOAT) { + if (newSubscriber->getEditingType() == ET_PIPETTE) { + if (newSubscriber->getEditBufferType() == BT_IMAGEFLOAT) { if (!imgFloatBuffer) imgFloatBuffer = new Imagefloat(newWidth, newHeight); else @@ -127,7 +131,7 @@ void EditBuffer::resize(int newWidth, int newHeight) { imgFloatBuffer = NULL; } - if (subscriber->getEditBufferType() == BT_LABIMAGE) { + if (newSubscriber->getEditBufferType() == BT_LABIMAGE) { if (LabBuffer && (LabBuffer->W != newWidth && LabBuffer->H != newHeight)) { delete LabBuffer; LabBuffer = NULL; @@ -140,43 +144,49 @@ void EditBuffer::resize(int newWidth, int newHeight) { LabBuffer = NULL; } - if (subscriber->getEditBufferType() == BT_SINGLEPLANE_FLOAT) { + if (newSubscriber->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(); + if (objectMap ) objectMap.clear(); + if (objectMap2) objectMap2.clear(); } } } 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; + EditSubscriber* subscriber; + if (dataProvider && (subscriber = dataProvider->getCurrSubscriber())) { + switch (subscriber->getEditingType()) { + case ET_PIPETTE: + switch (dataProvider->getCurrSubscriber()->getEditBufferType()) { + case (BT_IMAGEFLOAT): + return imgFloatBuffer != NULL; + case (BT_LABIMAGE): + return LabBuffer != NULL; + case (BT_SINGLEPLANE_FLOAT): + return singlePlaneBuffer.data != NULL; + } + break; + case (ET_OBJECTS): + return bool(objectMap); } } return false; } -unsigned short EditBuffer::getObjectID(const Coord& location) { - unsigned short id = 0; +int EditBuffer::getObjectID(const Coord& location) { + int id = 0; - if (objectMap) + if (objectMap && location.x>0 && location.y>0 && location.xget_width() && location.yget_height()) { 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; + if (objectMap2) + id |= (unsigned short)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )) << 8; + } + return id-1; } void EditBuffer::getPipetteData(float* v, int x, int y, int squareSize) { diff --git a/rtengine/editbuffer.h b/rtengine/editbuffer.h index 17847586e..d8632285e 100644 --- a/rtengine/editbuffer.h +++ b/rtengine/editbuffer.h @@ -48,6 +48,7 @@ protected: PlanarWhateverData singlePlaneBuffer; void createBuffer(int width, int height); + void resize(int newWidth, int newHeight, EditSubscriber* newSubscriber); void resize(int newWidth, int newHeight); void flush(); @@ -70,7 +71,7 @@ public: // return true if the buffer has been allocated bool bufferCreated(); - unsigned short getObjectID(const Coord& location); + int getObjectID(const Coord& location); // get the pipette values void getPipetteData(float* v, int x, int y, int squareSize); }; diff --git a/rtengine/iimage.h b/rtengine/iimage.h index 6f9ec9707..5e21bfe6f 100644 --- a/rtengine/iimage.h +++ b/rtengine/iimage.h @@ -41,6 +41,8 @@ #define TR_HFLIP 8 #define TR_ROT 3 +#define CHECK_BOUNDS 0 + namespace rtengine { extern const char sImage8[]; @@ -120,9 +122,17 @@ namespace rtengine { protected: AlignedBuffer ab; public: + #if CHECK_BOUNDS + int width_, height_; + #endif T** ptrs; - PlanarPtr() : ptrs (NULL) {} + #if CHECK_BOUNDS + PlanarPtr() : width_(0), height_(0), ptrs (NULL) {} + #else + PlanarPtr() : ptrs (NULL){} + #endif + bool resize(int newSize) { if (ab.resize(newSize)) { ptrs=ab.data; @@ -138,14 +148,43 @@ namespace rtengine { T** tmpsPtrs = other.ptrs; other.ptrs = ptrs; ptrs = tmpsPtrs; + + #if CHECK_BOUNDS + int tmp = other.width_; + other.width_ = width_; + width_ = tmp; + tmp = other.height_; + other.height_ = height_; + height_ = tmp; + #endif } - T*& operator() (unsigned row) { return ptrs[row]; } + T*& operator() (unsigned row) { + #if CHECK_BOUNDS + assert (row < height_); + #endif + return ptrs[row]; + } // Will send back the start of a row, starting with a red, green or blue value - T* operator() (unsigned row) const { return ptrs[row]; } + T* operator() (unsigned row) const { + #if CHECK_BOUNDS + assert (row < height_); + #endif + return ptrs[row]; + } // Will send back a value at a given row, col position - T& operator() (unsigned row, unsigned col) { return ptrs[row][col]; } - const T operator() (unsigned row, unsigned col) const { return ptrs[row][col]; } + T& operator() (unsigned row, unsigned col) { + #if CHECK_BOUNDS + assert (row < height_ && col < width_); + #endif + return ptrs[row][col]; + } + const T operator() (unsigned row, unsigned col) const { + #if CHECK_BOUNDS + assert (row < height_ && col < width_); + #endif + return ptrs[row][col]; + } }; template @@ -180,6 +219,10 @@ namespace rtengine { int tmpHeight = other.height; other.height = height; height = tmpHeight; + #if CHECK_BOUNDS + v.width_ = width; + v.height_ = height; + #endif } // use as pointer to data @@ -194,6 +237,10 @@ namespace rtengine { width=W; height=H; + #if CHECK_BOUNDS + v.width_ = width; + v.height_ = height; + #endif if (sizeof(T) > 1) { // 128 bits memory alignment for >8bits data @@ -222,6 +269,10 @@ namespace rtengine { data = NULL; v.resize(0); width = height = -1; + #if CHECK_BOUNDS + v.width_ = v.height_ = -1; + #endif + return; } @@ -491,6 +542,11 @@ namespace rtengine { int tmpHeight = other.height; other.height = height; height = tmpHeight; + #if CHECK_BOUNDS + r.width_ = width; r.height_ = height; + g.width_ = width; g.height_ = height; + b.width_ = width; b.height_ = height; + #endif } // use as pointer to data @@ -505,6 +561,11 @@ namespace rtengine { width=W; height=H; + #if CHECK_BOUNDS + r.width_ = width; r.height_ = height; + g.width_ = width; g.height_ = height; + b.width_ = width; b.height_ = height; + #endif if (sizeof(T) > 1) { // 128 bits memory alignment for >8bits data @@ -539,6 +600,11 @@ namespace rtengine { g.resize(0); b.resize(0); width = height = -1; + #if CHECK_BOUNDS + r.width_ = r.height_ = -1; + g.width_ = g.height_ = -1; + b.width_ = b.height_ = -1; + #endif return; } @@ -943,7 +1009,15 @@ namespace rtengine { T* ptr; int width; public: + #if CHECK_BOUNDS + int width_, height_; + #endif + +#if CHECK_BOUNDS + ChunkyPtr() : ptr (NULL), width(-1), width_(0), height_(0) {} +#else ChunkyPtr() : ptr (NULL), width(-1) {} +#endif void init(T* base, int w=-1) { ptr = base; width=w; } void swap (ChunkyPtr &other) { T* tmpsPtr = other.ptr; @@ -953,13 +1027,38 @@ namespace rtengine { int tmpWidth = other.width; other.width = width; width = tmpWidth; + + #if CHECK_BOUNDS + int tmp = other.width_; + other.width_ = width_; + width_ = tmp; + tmp = other.height_; + other.height_ = height_; + height_ = tmp; + #endif + } // Will send back the start of a row, starting with a red, green or blue value - T* operator() (unsigned row) const { return &ptr[3*(row*width)]; } + T* operator() (unsigned row) const { + #if CHECK_BOUNDS + assert (row < height_); + #endif + return &ptr[3*(row*width)]; + } // Will send back a value at a given row, col position - T& operator() (unsigned row, unsigned col) { return ptr[3*(row*width+col)]; } - const T operator() (unsigned row, unsigned col) const { return ptr[3*(row*width+col)]; } + T& operator() (unsigned row, unsigned col) { + #if CHECK_BOUNDS + assert (row < height_ && col < width_); + #endif + return ptr[3*(row*width+col)]; + } + const T operator() (unsigned row, unsigned col) const { + #if CHECK_BOUNDS + assert (row < height_ && col < width_); + #endif + return ptr[3*(row*width+col)]; + } }; template @@ -997,6 +1096,11 @@ namespace rtengine { int tmpHeight = other.height; other.height = height; height = tmpHeight; + #if CHECK_BOUNDS + r.width_ = width; r.height_ = height; + g.width_ = width; g.height_ = height; + b.width_ = width; b.height_ = height; + #endif } /* @@ -1010,6 +1114,11 @@ namespace rtengine { width=W; height=H; + #if CHECK_BOUNDS + r.width_ = width; r.height_ = height; + g.width_ = width; g.height_ = height; + b.width_ = width; b.height_ = height; + #endif abData.resize(width*height*3); if (!abData.isEmpty()) { @@ -1024,6 +1133,11 @@ namespace rtengine { g.init(NULL); b.init(NULL); width = height = -1; + #if CHECK_BOUNDS + r.width_ = r.height_ = -1; + g.width_ = g.height_ = -1; + b.width_ = b.height_ = -1; + #endif } } diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 69b2135ad..d3991b627 100755 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -345,7 +345,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int 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)) ) { + 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>-1)) ) { editSubscriber->button1Pressed(bstate); state=SEditDrag; } @@ -576,9 +576,9 @@ void CropWindow::pointerMoved (int bstate, int x, int y) { iarea->posImage.set(imgPos.x, imgPos.y); iarea->posScreen.set(x, y); + Coord cropPos; + screenCoordToCropBuffer(x, y, cropPos.x, cropPos.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) { @@ -589,22 +589,33 @@ void CropWindow::pointerMoved (int bstate, int x, int y) { iarea->pipetteVal[0] = iarea->pipetteVal[1] = iarea->pipetteVal[2] = -1.f; } } + else if (editSubscriber->getEditingType()==ET_OBJECTS) { + if (onArea (CropImage, x, y)) + iarea->object = crop->getObjectID(cropPos); + else + iarea->object = -1; + } 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; + Coord currPos; + action_x = x; + action_y = y; + Coord oldPosImage = iarea->posImage+iarea->deltaImage; + //printf(">>> IMG / ImgPrev(%d x %d) = (%d x %d) + (%d x %d)\n", oldPosImage.x, oldPosImage.y, iarea->posImage.x, iarea->posImage.y, iarea->deltaImage.x, iarea->deltaImage.y); + screenCoordToImage (x, y, currPos.x, currPos.y); + iarea->deltaImage = currPos - iarea->posImage; iarea->deltaPrevImage = currPos - oldPosImage; + //printf(" action_ & xy (%d x %d) -> (%d x %d) = (%d x %d) + (%d x %d) / deltaPrev(%d x %d)\n", action_x, action_y, currPos.x, currPos.y, iarea->posImage.x, iarea->posImage.y, iarea->deltaImage.x, iarea->deltaImage.y, iarea->deltaPrevImage.x, iarea->deltaPrevImage.y); - currPos.set(x, y); - iarea->deltaScreen = currPos - iarea->posScreen; + Coord oldPosScreen = iarea->posScreen+iarea->deltaScreen; + //printf(">>> SCR / ScrPrev(%d x %d) = (%d x %d) + (%d x %d)\n", oldPosScreen.x, oldPosScreen.y, iarea->posScreen.x, iarea->posScreen.y, iarea->deltaScreen.x, iarea->deltaScreen.y); + screenCoordToPreview(x, y, currPos.x, currPos.y); + iarea->deltaScreen = currPos - iarea->posScreen; iarea->deltaPrevScreen = currPos - oldPosScreen; + //printf(" action_ & xy (%d x %d) -> (%d x %d) = (%d x %d) + (%d x %d) / deltaPrev(%d x %d)\n", action_x, action_y, currPos.x, currPos.y, iarea->posScreen.x, iarea->posScreen.y, iarea->deltaScreen.x, iarea->deltaScreen.y, iarea->deltaPrevScreen.x, iarea->deltaPrevScreen.y); if (editSubscriber->drag(bstate)) iarea->redraw (); @@ -786,7 +797,12 @@ void CropWindow::updateCursor (int x, int y) { else if (tm==TMHand && (onArea (CropLeft, x, y) || onArea (CropRight, x, y))) cursorManager.setCursor (iarea->get_window(), CSResizeWidth); else if (onArea (CropImage, x, y)) { - if (tm==TMHand) { + int objectID = -1; + if (editSubscriber) + objectID = static_cast(cropHandler.getCrop())->getObjectID(iarea->posImage); + if (objectID > -1) + cursorManager.setCursor (iarea->get_window(), iarea->getCursor(objectID)); + else if (tm==TMHand) { if (onArea (CropObserved, x, y)) cursorManager.setCursor (iarea->get_window(), CSMove); else @@ -799,9 +815,6 @@ 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); } @@ -1106,30 +1119,46 @@ void CropWindow::expose (Cairo::RefPtr cr) { rtengine::Crop* crop = static_cast(cropHandler.getCrop()); if (editSubscriber && crop->bufferCreated()) { - // drawing Subscriber's visible geometry + // clip the region + if (this != iarea->mainCropWindow) { + cr->set_line_width (0.); + cr->rectangle (x+imgX, y+imgY, imgW, imgH); + cr->clip(); + } + // 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); - } + for (std::vector::const_iterator i = visibleGeom.begin(); i != visibleGeom.end(); ++i) + (*i)->drawOuterGeometry(cr, crop, *this); + // drawing inner lines - for (unsigned short currObject=0; currObject < visibleGeom.size(); ++currObject) { - visibleGeom.at(currObject)->drawInnerGeometry(cr, crop, *this); - } + for (std::vector::const_iterator i = visibleGeom.begin(); i != visibleGeom.end(); ++i) + (*i)->drawInnerGeometry(cr, crop, *this); + + if (this != iarea->mainCropWindow) + cr->reset_clip(); // drawing to the "mouse over" channel - if (editSubscriber->getEditingType() == ET_OBJECTS) { const std::vector mouseOverGeom = editSubscriber->getMouseOverGeometry(); if (mouseOverGeom.size()) { + //printf("ObjectMap (%d x %d)\n", crop->getObjectMap()->get_width(), crop->getObjectMap()->get_height()); 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); + crMO->set_operator(Cairo::OPERATOR_SOURCE); + + // clear the bitmap + crMO->set_source_rgba(0., 0., 0., 0.); + crMO->rectangle(0., 0., crop->getObjectMap()->get_width(), crop->getObjectMap()->get_height()); + crMO->set_line_width(0.); + crMO->fill(); Cairo::RefPtr crMO2; if (crop->getObjectMode() > OM_255) { @@ -1137,12 +1166,44 @@ void CropWindow::expose (Cairo::RefPtr cr) { 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); - } - } + crMO2->set_operator(Cairo::OPERATOR_SOURCE); + // clear the bitmap + crMO2->set_source_rgba(0., 0., 0., 0.); + crMO2->rectangle(0., 0., crop->getObjectMap2()->get_width(), crop->getObjectMap2()->get_height()); + crMO2->set_line_width(0.); + crMO2->fill(); + } + std::vector::const_iterator i; + int a; + for (a=0, i=mouseOverGeom.begin(); i != mouseOverGeom.end(); ++i, ++a) { + (*i)->drawToMOChannel(crMO, crMO2, a, crop, *this); + } + + // Debug code: save the "mouse over" image to a new file at each occurrence + #if 0 + { + static unsigned int count=0; + int w = crop->getObjectMap()->get_width(); + int h = crop->getObjectMap()->get_height(); + Glib::RefPtr img = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, w, h); + guint8 *dst = img->get_pixels(); + unsigned char *src1 = crop->getObjectMap()->get_data(); + unsigned char *src2 = crop->getObjectMode() > OM_255 ? crop->getObjectMap()->get_data() : NULL; + memcpy(dst, src1, w*h); + for (int n=0, n3=0; nsave(Glib::ustring::compose("mouseOverImage-%1.png", count++), "png"); + } + #endif + } } } } @@ -1400,12 +1461,29 @@ void CropWindow::screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy) { imgy = cropY + (phyy - ypos - imgY)/zoomSteps[cropZoom].zoom; } +void CropWindow::screenCoordToPreview (int phyx, int phyy, int& prevx, int& prevy) { + + prevx = phyx - xpos - imgX; + prevy = phyy - ypos - imgY; +} + void CropWindow::imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy) { int cropX, cropY; + rtengine::Crop* crop = static_cast(cropHandler.getCrop()); cropHandler.getPosition (cropX, cropY); phyx = (imgx - cropX)*zoomSteps[cropZoom].zoom + xpos + imgX; phyy = (imgy - cropY)*zoomSteps[cropZoom].zoom + ypos + imgY; + //printf("imgx:%d / imgy:%d / cropX:%d / cropY:%d / xpos:%d / ypos:%d / imgX:%d / imgY:%d / leftBorder: %d / upperBorder:%d / phyx:%d / phyy:%d\n", imgx, imgy, cropX, cropY, xpos, ypos, imgX, imgY, crop->getLeftBorder(), crop->getUpperBorder(), phyx, phyy); +} + +void CropWindow::imageCoordToCropBuffer (int imgx, int imgy, int& phyx, int& phyy) { + int cropX, cropY; + rtengine::Crop* crop = static_cast(cropHandler.getCrop()); + cropHandler.getPosition (cropX, cropY); + phyx = (imgx - cropX)*zoomSteps[cropZoom].zoom + /*xpos + imgX +*/ crop->getLeftBorder(); + phyy = (imgy - cropY)*zoomSteps[cropZoom].zoom + /*ypos + imgY +*/ crop->getUpperBorder(); + //printf("imgx:%d / imgy:%d / cropX:%d / cropY:%d / xpos:%d / ypos:%d / imgX:%d / imgY:%d / leftBorder: %d / upperBorder:%d / phyx:%d / phyy:%d\n", imgx, imgy, cropX, cropY, xpos, ypos, imgX, imgY, crop->getLeftBorder(), crop->getUpperBorder(), phyx, phyy); } int CropWindow::scaleValueToImage (int value) { diff --git a/rtgui/cropwindow.h b/rtgui/cropwindow.h index ae0691eb4..27a59be17 100755 --- a/rtgui/cropwindow.h +++ b/rtgui/cropwindow.h @@ -64,7 +64,7 @@ class CropWindow : public LWButtonListener, public CropHandlerListener, public E // crop frame description int titleHeight, sideBorderWidth, lowerBorderWidth, upperBorderWidth, sepWidth, minWidth; // size & position of the crop relative to the top left corner - // of the main preview area (to be confirmed) + // of the main preview area int xpos, ypos, width, height; // size & pos of the drawable area relative to the top left corner of the crop int imgAreaX, imgAreaY, imgAreaW, imgAreaH; @@ -105,7 +105,9 @@ class CropWindow : public LWButtonListener, public CropHandlerListener, public E void screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy); void screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy); + void screenCoordToPreview (int phyx, int phyy, int& prevx, int& prevy); void imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy); + void imageCoordToCropBuffer (int imgx, int imgy, int& phyx, int& phyy); int scaleValueToImage (int value); float scaleValueToImage (float value); double scaleValueToImage (double value); diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index a6f045c50..225e7aed1 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -129,7 +129,7 @@ std::vector FlatCurveEditor::getCurve () { * ceGroup = NULL or the address of the Widget that will receive the CurveTypeToggleButton * text = (optional) label of the curve, displayed in the CurveTypeToggleButton, next to the image */ -CurveEditor::CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) { +CurveEditor::CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) : EditSubscriber(ET_PIPETTE) { bgHistValid = false; remoteDrag = false; diff --git a/rtgui/diagonalcurveeditorsubgroup.cc b/rtgui/diagonalcurveeditorsubgroup.cc index 697882970..01633003a 100644 --- a/rtgui/diagonalcurveeditorsubgroup.cc +++ b/rtgui/diagonalcurveeditorsubgroup.cc @@ -23,7 +23,6 @@ #include #include "guiutils.h" #include "multilangmgr.h" -#include "guiutils.h" #include "mycurve.h" #include "shcselector.h" #include "adjuster.h" @@ -167,7 +166,7 @@ DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, 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) ); + editParamConn = 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")); diff --git a/rtgui/edit.cc b/rtgui/edit.cc index e4b6b32f0..a4f10aaa9 100644 --- a/rtgui/edit.cc +++ b/rtgui/edit.cc @@ -20,6 +20,13 @@ #include "edit.h" #include "../rtengine/editbuffer.h" +void Coord::setFromPolar(PolarCoord polar) { + while (polar.angle < 0.f) polar.angle += 360.f; + while (polar.angle > 360.f) polar.angle -= 360.f; + x = polar.radius * cos(polar.angle/180.f*M_PI); + y = polar.radius * sin(polar.angle/180.f*M_PI); +} + RGBColor Geometry::getInnerLineColor () { RGBColor color; if (flags & AUTO_COLOR) { @@ -47,9 +54,17 @@ RGBColor Geometry::getOuterLineColor () { void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if (flags & ACTIVE) { + RGBColor color; + if (flags & AUTO_COLOR) + color = getOuterLineColor(); + else + color = outerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( getOuterLineWidth() ); + Coord center_ = center; - double radius_ = radiusInImageSpace ? coordSystem.scaleValueToImage(double(radius)) : double(radius); + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToScreen(double(radius)) : double(radius); if (datum == IMAGE) { coordSystem.imageCoordToScreen(center.x, center.y, center_.x, center_.y); } @@ -59,15 +74,24 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Edit 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); + cr->arc(center_.x+0.5, center_.y+0.5, radius_, 0., 2.*M_PI); + cr->stroke(); } } void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if (flags & ACTIVE) { + RGBColor color; + if (flags & AUTO_COLOR) + color = getInnerLineColor(); + else + color = innerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( innerLineWidth ); + Coord center_ = center; - double radius_ = radiusInImageSpace ? coordSystem.scaleValueToImage(double(radius)) : double(radius); + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToScreen(double(radius)) : double(radius); if (datum == IMAGE) { coordSystem.imageCoordToScreen(center.x, center.y, center_.x, center_.y); } @@ -77,10 +101,8 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Edit 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 ); + cr->arc(center_.x+0.5, center_.y+0.5, radius_, 0., 2.*M_PI); if (innerLineWidth > 0.) { cr->fill_preserve(); cr->stroke(); @@ -89,7 +111,7 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Edit cr->fill(); } else if (innerLineWidth > 0.) { - cr->arc(center_.x, center_.y, radius_, 0., 2.*M_PI); + cr->arc(center_.x+0.5, center_.y+0.5, radius_, 0., 2.*M_PI); cr->stroke(); } } @@ -99,9 +121,9 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrset_line_width( getMouseOverLineWidth() ); Coord center_ = center; - double radius_ = radiusInImageSpace ? coordSystem.scaleValueToImage(double(radius)) : double(radius); + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToScreen(double(radius)) : double(radius); if (datum == IMAGE) { - coordSystem.imageCoordToScreen(center.x, center.y, center_.x, center_.y); + coordSystem.imageCoordToCropBuffer(center.x, center.y, center_.x, center_.y); } else if (datum == CLICKED_POINT) { center_ += editBuffer->getDataProvider()->posScreen; @@ -113,7 +135,7 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrset_source_rgba (0.,0., 0., double(a)/255.); - cr->arc(center_.x, center_.y, radius_, 0, 2.*M_PI); + cr->arc(center_.x+0.5, center_.y+0.5, radius_, 0, 2.*M_PI); if (filled) { if (innerLineWidth > 0.) { cr->fill_preserve(); @@ -129,7 +151,7 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrgetObjectMode() == 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); + cr2->arc(center_.x+0.5, center_.y+0.5, radius_, 0, 2.*M_PI); if (filled) { if (innerLineWidth > 0.) { cr2->fill_preserve(); @@ -146,7 +168,15 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if (flags & ACTIVE) { + RGBColor color; + if (flags & AUTO_COLOR) + color = getOuterLineColor(); + else + color = outerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( getOuterLineWidth() ); + Coord begin_ = begin; Coord end_ = end; if (datum == IMAGE) { @@ -161,15 +191,23 @@ void Line::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBu 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->move_to(begin_.x+0.5, begin_.y+0.5); + cr->line_to(end_.x+0.5, end_.y+0.5); cr->stroke(); } } void Line::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if ((flags & ACTIVE) && innerLineWidth > 0.) { + RGBColor color; + if (flags & AUTO_COLOR) + color = getInnerLineColor(); + else + color = innerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width(innerLineWidth); + Coord begin_ = begin; Coord end_ = end; if (datum == IMAGE) { @@ -184,10 +222,8 @@ void Line::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBu 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->move_to(begin_.x+0.5, begin_.y+0.5); + cr->line_to(end_.x+0.5, end_.y+0.5); cr->stroke(); } } @@ -198,8 +234,8 @@ void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrgetDataProvider()->posScreen; @@ -213,16 +249,16 @@ void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrset_source_rgba (0.,0., 0., double(a)/255.); - cr->move_to(begin_.x, begin_.y); - cr->line_to(end_.x, end_.y); + cr->move_to(begin_.x+0.5, begin_.y+0.5); + cr->line_to(end_.x+0.5, end_.y+0.5); 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->move_to(begin_.x+0.5, begin_.y+0.5); + cr2->line_to(end_.x+0.5, end_.y+0.5); cr2->stroke(); } } @@ -230,45 +266,57 @@ void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if ((flags & ACTIVE) && points.size()>1) { - cr->set_source_rgb (outerLineColor.getR(), outerLineColor.getG(), outerLineColor.getB()); + RGBColor color; + if (flags & AUTO_COLOR) + color = getOuterLineColor(); + else + color = outerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); 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 (!i) cr->move_to(currPos.x+0.5, currPos.y+0.5); + else cr->line_to(currPos.x+0.5, currPos.y+0.5); } if (filled) { cr->fill_preserve(); cr->stroke(); } else - cr->fill(); + cr->stroke(); } } 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()); + RGBColor color; + if (flags & AUTO_COLOR) + color = getInnerLineColor(); + else + color = innerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); 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 (!i) cr->move_to(currPos.x+0.5, currPos.y+0.5); + else cr->line_to(currPos.x+0.5, currPos.y+0.5); } if (innerLineWidth > 0.) { @@ -281,17 +329,16 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Ed 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); + if (!i) cr->move_to(currPos.x+0.5, currPos.y+0.5); + else cr->line_to(currPos.x+0.5, currPos.y+0.5); } - cr->fill(); + cr->stroke(); } } } @@ -307,12 +354,12 @@ void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr cr->set_line_width( getOuterLineWidth() ); currPos = points.at(i); - if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + if (datum == IMAGE) coordSystem.imageCoordToCropBuffer(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 (!i) cr->move_to(currPos.x+0.5, currPos.y+0.5); + else cr->line_to(currPos.x+0.5, currPos.y+0.5); } if (filled) { if (innerLineWidth > 0.) { @@ -333,12 +380,12 @@ void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr cr2->set_line_width( getOuterLineWidth() ); currPos = points.at(i); - if (datum == IMAGE) coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); + if (datum == IMAGE) coordSystem.imageCoordToCropBuffer(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 (!i) cr2->move_to(currPos.x+0.5, currPos.y+0.5); + else cr2->line_to(currPos.x+0.5, currPos.y+0.5); } if (filled) { if (innerLineWidth > 0.) { @@ -376,10 +423,16 @@ void Rectangle::setXYXY(Coord topLeft, Coord bottomRight) { void Rectangle::drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if ((flags & ACTIVE)) { - cr->set_source_rgb (outerLineColor.getR(), outerLineColor.getG(), outerLineColor.getB()); + RGBColor color; + if (flags & AUTO_COLOR) + color = getOuterLineColor(); + else + color = outerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); Coord tl, br; - cr->set_line_width( getOuterLineWidth() ); if (datum == IMAGE) coordSystem.imageCoordToScreen(topLeft.x, topLeft.y, tl.x, tl.y); else if (datum == CLICKED_POINT) tl = topLeft + editBuffer->getDataProvider()->posScreen; @@ -389,19 +442,28 @@ void Rectangle::drawOuterGeometry (Cairo::RefPtr &cr, rtengine:: else if (datum == CLICKED_POINT) br = bottomRight + editBuffer->getDataProvider()->posScreen; else if (datum == CURSOR) br = bottomRight +editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; - cr->rectangle(tl.x, tl.y, br.x-tl.x, br.y-tl.y); + cr->rectangle(tl.x+0.5, tl.y+0.5, br.x-tl.x, br.y-tl.y); if (filled) { cr->fill_preserve(); cr->stroke(); } else - cr->fill(); + cr->stroke(); } } void Rectangle::drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { if (flags & ACTIVE) { + RGBColor color; + if (flags & AUTO_COLOR) + color = getInnerLineColor(); + else + color = innerLineColor; + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( innerLineWidth ); + Coord tl, br; if (datum == IMAGE) coordSystem.imageCoordToScreen(topLeft.x, topLeft.y, tl.x, tl.y); else if (datum == CLICKED_POINT) tl = topLeft + editBuffer->getDataProvider()->posScreen; @@ -411,11 +473,8 @@ void Rectangle::drawInnerGeometry (Cairo::RefPtr &cr, rtengine:: else if (datum == CLICKED_POINT) br = bottomRight + editBuffer->getDataProvider()->posScreen; else if (datum == CURSOR) br = bottomRight +editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; - cr->set_source_rgb (innerLineColor.getR(), innerLineColor.getG(), innerLineColor.getB()); - if (filled) { - cr->set_line_width( innerLineWidth ); - cr->rectangle(tl.x, tl.y, br.x-tl.x, br.y-tl.y); + cr->rectangle(tl.x+0.5, tl.y+0.5, br.x-tl.x, br.y-tl.y); if (innerLineWidth > 0.) { cr->fill_preserve(); cr->stroke(); @@ -424,7 +483,7 @@ void Rectangle::drawInnerGeometry (Cairo::RefPtr &cr, rtengine:: cr->fill(); } else if (innerLineWidth > 0.) { - cr->rectangle(tl.x, tl.y, br.x-tl.x, br.y-tl.y); + cr->rectangle(tl.x+0.5, tl.y+0.5, br.x-tl.x, br.y-tl.y); cr->stroke(); } } @@ -435,18 +494,18 @@ void Rectangle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPt cr->set_line_width( getMouseOverLineWidth() ); Coord tl, br; - if (datum == IMAGE) coordSystem.imageCoordToScreen(topLeft.x, topLeft.y, tl.x, tl.y); + if (datum == IMAGE) coordSystem.imageCoordToCropBuffer(topLeft.x, topLeft.y, tl.x, tl.y); else if (datum == CLICKED_POINT) tl = topLeft + editBuffer->getDataProvider()->posScreen; else if (datum == CURSOR) tl = topLeft +editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; - if (datum == IMAGE) coordSystem.imageCoordToScreen(bottomRight.x, bottomRight.y, br.x, br.y); + if (datum == IMAGE) coordSystem.imageCoordToCropBuffer(bottomRight.x, bottomRight.y, br.x, br.y); else if (datum == CLICKED_POINT) br = bottomRight + editBuffer->getDataProvider()->posScreen; else if (datum == CURSOR) br = bottomRight +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->rectangle(tl.x, tl.y, br.x-tl.x, br.y-tl.y); + cr->rectangle(tl.x+0.5, tl.y+0.5, br.x-tl.x, br.y-tl.y); if (filled) { if (innerLineWidth > 0.) { cr->fill_preserve(); @@ -462,7 +521,7 @@ void Rectangle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPt if (editBuffer->getObjectMode() == OM_65535) { a = (id+1)>>8; cr2->set_source_rgba (0.,0., 0., double(a)/255.); - cr->rectangle(tl.x, tl.y, br.x-tl.x, br.y-tl.y); + cr->rectangle(tl.x+0.5, tl.y+0.5, br.x-tl.x, br.y-tl.y); if (filled) { if (innerLineWidth > 0.) { cr2->fill_preserve(); @@ -477,7 +536,7 @@ void Rectangle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPt } } -EditSubscriber::EditSubscriber () : ID(EUID_None), editingType(ET_PIPETTE), bufferType(BT_SINGLEPLANE_FLOAT), provider(NULL) {} +EditSubscriber::EditSubscriber (EditType editType) : ID(EUID_None), editingType(editType), bufferType(BT_SINGLEPLANE_FLOAT), provider(NULL) {} void EditSubscriber::setEditProvider(EditDataProvider *provider) { this->provider = provider; diff --git a/rtgui/edit.h b/rtgui/edit.h index 40038ef39..a4c38cba7 100644 --- a/rtgui/edit.h +++ b/rtgui/edit.h @@ -23,6 +23,7 @@ #include "../rtengine/imagefloat.h" #include "editid.h" #include "cursormanager.h" +#include "../rtengine/rt_math.h" class EditDataProvider; @@ -59,6 +60,8 @@ class EditBuffer; * */ +class PolarCoord; + // Do not confuse with rtengine::Coord2D, this one is for the GUI class Coord { public: @@ -73,9 +76,17 @@ public: this->y = y; } - void setFromPolar(float radius, float angle) { - x = radius * cos(angle/(2*M_PI)); - y = radius * sin(angle/(2*M_PI)); + void setFromPolar(PolarCoord polar); + + /// @brief Clip the coord to stay in the width x height bounds + /// @return true if the x or y coordinate has changed + bool clip(int width, int height) { + int trimmedX = rtengine::LIM(x, 0, width); + int trimmedY = rtengine::LIM(y, 0, height); + bool retval = trimmedX!=x || trimmedY!=y; + x = trimmedX; + y = trimmedY; + return retval; } void operator+=(const Coord & rhs) { @@ -104,6 +115,92 @@ public: } }; +class PolarCoord { +public: + double radius; + double angle; // degree + + PolarCoord() : radius(1.), angle(0.) {} + PolarCoord(double radius, double angle) : radius(radius), angle(angle) {} + + void set (double radius, double angle) { + this->radius = radius; + this->angle = angle; + } + + void setFromCartesian(Coord start, Coord end) { + Coord delta(end.x-start.x, end.y-start.y); + setFromCartesian(delta); + } + + void setFromCartesian(Coord delta) { + if (!delta.x && !delta.y) { + // null vector, we set to a default value + radius = 1.; + angle = 0.; + return; + } + double x_ = double(delta.x); + double y_ = double(delta.y); + radius = sqrt(x_*x_+y_*y_); + if (delta.x>0.) { + if (delta.y>=0.) + angle = atan(y_/x_)/(2*M_PI)*360.; + else if (delta.y<0.) + angle = (atan(y_/x_)+2*M_PI)/(2*M_PI)*360.; + } + else if (delta.x<0.) + angle = (atan(y_/x_)+M_PI)/(2*M_PI)*360.; + else if (delta.x==0.) { + if (delta.y>0.) + angle = 90.; + else + angle = 270.; + } + } + + void operator+=(const PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord += rhsCoord; + setFromCartesian(thisCoord); + } + void operator-=(const PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord -= rhsCoord; + setFromCartesian(thisCoord); + } + void operator*=(double scale) { + radius *= scale; + } + PolarCoord operator+(PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord += rhsCoord; + PolarCoord result; + result.setFromCartesian(thisCoord); + return result; + } + PolarCoord operator-(PolarCoord & rhs) { + Coord thisCoord, rhsCoord; + thisCoord.setFromPolar(*this); + rhsCoord.setFromPolar(rhs); + thisCoord -= rhsCoord; + PolarCoord result; + result.setFromCartesian(thisCoord); + return result; + } + Coord operator*(double scale) { + Coord result(radius*scale, angle); + return result; + } + +}; + /** @brief Coordinate system where the widgets will be drawn * * The EditCoordSystem is used to define a screen and an image coordinate system. @@ -118,6 +215,8 @@ public: 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 the image coords to the edit buffer coords + virtual void imageCoordToCropBuffer (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 @@ -139,8 +238,8 @@ class RGBColor { 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.) {} + explicit RGBColor (double r, double g, double b) : r(r), g(g), b(b) {} + explicit 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; @@ -186,7 +285,7 @@ public: Datum datum; State state; // set by the Subscriber - Geometry () : innerLineColor(), outerLineColor(), flags(ACTIVE|AUTO_COLOR), innerLineWidth(1.f), datum(IMAGE), state(NORMAL) {} + Geometry () : innerLineColor(char(255), char(255), char(255)), outerLineColor(char(0), char(0), char(0)), 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; } @@ -213,8 +312,8 @@ public: 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) {} + Circle (Coord ¢er, int radius, bool filled=false, bool radiusInImageSpace=false) : center(center), radius(radius), filled(filled), radiusInImageSpace(radiusInImageSpace) {} + Circle (int centerX, int centerY, int radius, bool filled=false, bool radiusInImageSpace=false) : center(centerX, centerY), radius(radius), filled(filled), radiusInImageSpace(radiusInImageSpace) {} void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); void drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); @@ -280,7 +379,7 @@ protected: std::vector mouseOverGeometry; public: - EditSubscriber (); + EditSubscriber (EditType editType); virtual ~EditSubscriber () {} void setEditProvider(EditDataProvider *provider); @@ -293,7 +392,10 @@ public: EditUniqueID getEditID(); EditType getEditingType(); BufferType getEditBufferType(); - CursorShape getCursor(int objectID) { return CSOpenHand; } + + /** @brief Get the cursor to be displayed when above handles + @param objectID object currently "hovered" */ + virtual 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 diff --git a/rtgui/gradient.cc b/rtgui/gradient.cc index 0eb43b5de..8a20c88b7 100644 --- a/rtgui/gradient.cc +++ b/rtgui/gradient.cc @@ -2,12 +2,14 @@ * This file is part of RawTherapee. */ #include "gradient.h" +#include "rtimage.h" #include +#include "../rtengine/rt_math.h" using namespace rtengine; using namespace rtengine::procparams; -Gradient::Gradient () : Gtk::VBox(), FoldableToolPanel(this) +Gradient::Gradient () : Gtk::VBox(), FoldableToolPanel(this), EditSubscriber(ET_OBJECTS), lastObject(-1), draggedPointOldAngle(-1000.) { set_border_width(4); @@ -15,6 +17,10 @@ Gradient::Gradient () : Gtk::VBox(), FoldableToolPanel(this) enabled->set_active (false); enaConn = enabled->signal_toggled().connect( sigc::mem_fun(*this, &Gradient::enabledChanged) ); + edit = Gtk::manage (new Gtk::ToggleButton()); + edit->add (*Gtk::manage (new RTImage ("editmodehand.png"))); + editConn = edit->signal_toggled().connect( sigc::mem_fun(*this, &Gradient::editToggled) ); + strength = Gtk::manage (new Adjuster (M("TP_GRADIENT_STRENGTH"), -5, 5, 0.01, 0)); strength->set_tooltip_text (M("TP_GRADIENT_STRENGTH_TOOLTIP")); strength->setAdjusterListener (this); @@ -35,7 +41,10 @@ Gradient::Gradient () : Gtk::VBox(), FoldableToolPanel(this) centerY->set_tooltip_text (M("TP_GRADIENT_CENTER_Y_TOOLTIP")); centerY->setAdjusterListener (this); - pack_start(*enabled); + Gtk::HBox* enaBox = Gtk::manage (new Gtk::HBox()); + enaBox->pack_start(*enabled); + enaBox->pack_end(*edit, false, false, 0); + pack_start(*enaBox); pack_start(*Gtk::manage (new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET, 4); pack_start (*strength); pack_start (*degree); @@ -43,9 +52,52 @@ Gradient::Gradient () : Gtk::VBox(), FoldableToolPanel(this) pack_start (*centerX); pack_start (*centerY); + // Instantiating the Editing geometry; positions will be initialized later + Line *hLine, *vLine; + Circle *centerCircle; + + // Visible geometry + hLine = new Line(); hLine->innerLineWidth=2; + vLine = new Line(); + hLine->datum = vLine->datum = Geometry::IMAGE; + + centerCircle = new Circle(); + centerCircle->datum = Geometry::IMAGE; + centerCircle->radiusInImageSpace = false; + centerCircle->radius = 6; + centerCircle->filled = true; + + EditSubscriber::visibleGeometry.push_back( hLine ); + EditSubscriber::visibleGeometry.push_back( vLine ); + EditSubscriber::visibleGeometry.push_back( centerCircle ); + + // MouseOver geometry + hLine = new Line(); hLine->innerLineWidth=2; + vLine = new Line(); + hLine->datum = vLine->datum = Geometry::IMAGE; + + centerCircle = new Circle(); + centerCircle->datum = Geometry::IMAGE; + centerCircle->radiusInImageSpace = false; + centerCircle->radius = 30; + centerCircle->filled = true; + + EditSubscriber::mouseOverGeometry.push_back( hLine ); + EditSubscriber::mouseOverGeometry.push_back( vLine ); + EditSubscriber::mouseOverGeometry.push_back( centerCircle ); + show_all(); } +Gradient::~Gradient() { + for (std::vector::const_iterator i = visibleGeometry.begin(); i != visibleGeometry.end(); ++i) { + delete *i; + } + for (std::vector::const_iterator i = mouseOverGeometry.begin(); i != mouseOverGeometry.end(); ++i) { + delete *i; + } +} + void Gradient::read (const ProcParams* pp, const ParamsEdited* pedited) { disableListener (); @@ -70,9 +122,43 @@ void Gradient::read (const ProcParams* pp, const ParamsEdited* pedited) lastEnabled = pp->gradient.enabled; + updateGeometry (pp->gradient.centerX, pp->gradient.centerY, pp->gradient.strength, pp->gradient.degree); + enableListener (); } +void Gradient::updateGeometry(int centerX_, int centerY_, double strength_, double degree_) { + EditDataProvider* dataProvider = getEditProvider(); + if (dataProvider) { + int imW, imH; + dataProvider->getImageSize(imW, imH); + + Coord origin(imW/2+centerX_*imW/200.f, imH/2+centerY_*imH/200.f); + + Line *currLine; + Circle *currCircle; + // update left line + currLine = static_cast(visibleGeometry.at(0)); + currLine->begin.setFromPolar(PolarCoord(1500.f, float(-degree_+180))); currLine->begin += origin; + currLine->end.setFromPolar (PolarCoord(1500.f, float(-degree_ ))); currLine->end += origin; + currLine = static_cast(mouseOverGeometry.at(0)); + currLine->begin.setFromPolar(PolarCoord(1500.f, float(-degree_+180))); currLine->begin += origin; + currLine->end.setFromPolar (PolarCoord(1500.f, float(-degree_ ))); currLine->end += origin; + // update right line + currLine = static_cast(visibleGeometry.at(1)); + currLine->begin.setFromPolar(PolarCoord( 700.f, float(-degree_+90 ))); currLine->begin += origin; + currLine->end.setFromPolar (PolarCoord( 700.f, float(-degree_+270))); currLine->end += origin; + currLine = static_cast(mouseOverGeometry.at(1)); + currLine->begin.setFromPolar(PolarCoord( 700.f, float(-degree_+90 ))); currLine->begin += origin; + currLine->end.setFromPolar (PolarCoord( 700.f, float(-degree_+270))); currLine->end += origin; + // update circle's position + currCircle = static_cast(visibleGeometry.at(2)); + currCircle->center = origin; + currCircle = static_cast(mouseOverGeometry.at(2)); + currCircle->center = origin; + } +} + void Gradient::write (ProcParams* pp, ParamsEdited* pedited) { pp->gradient.degree = degree->getValue (); @@ -118,6 +204,8 @@ void Gradient::setDefaults (const ProcParams* defParams, const ParamsEdited* ped void Gradient::adjusterChanged (Adjuster* a, double newval) { if (listener && enabled->get_active()) { + updateGeometry (int(centerX->getValue()), int(centerY->getValue()), strength->getValue(), degree->getValue()); + if (a == degree) listener->panelChanged (EvGradientDegree, degree->getTextValue()); else if (a == feather) @@ -178,3 +266,146 @@ void Gradient::setBatchMode (bool batchMode) centerX->showEditedCB (); centerY->showEditedCB (); } + +void Gradient::setEditProvider (EditDataProvider* provider) { + EditSubscriber::setEditProvider(provider); +} + +void Gradient::editToggled () { + if (edit->get_active()) { + subscribe(); + } + else + unsubscribe(); +} + +// TODO +CursorShape Gradient::getCursor(int objectID) { + return CSOpenHand; +} + +bool Gradient::mouseOver(int modifierKey) { + EditDataProvider* editProvider = getEditProvider(); + if (editProvider && editProvider->object!=lastObject) { + if (lastObject > -1) + EditSubscriber::visibleGeometry.at(lastObject)->state = Geometry::NORMAL; + if (editProvider->object > -1) + EditSubscriber::visibleGeometry.at(editProvider->object)->state = Geometry::PRELIGHT; + lastObject = editProvider->object; + return true; + } + return false; +} + +bool Gradient::button1Pressed(int modifierKey) { + if (!(modifierKey & (GDK_CONTROL_MASK | GDK_CONTROL_MASK))) { + // button press is valid (no modifier key) + PolarCoord pCoord; + EditDataProvider *provider = getEditProvider(); + int imW, imH; + provider->getImageSize(imW, imH); + double halfSizeW = imW/2.; + double halfSizeH = imH/2.; + draggedCenter.set(int(halfSizeW+halfSizeW*(centerX->getValue()/100.)), int(halfSizeH+halfSizeH*(centerY->getValue()/100.))); + + // trick to get the correct angle (clockwise/counter-clockwise) + Coord p1 = draggedCenter; + Coord p2 = provider->posImage; + int p = p1.y; + p1.y = p2.y; + p2.y = p; + + pCoord.setFromCartesian(p1, p2); + draggedPointOldAngle = pCoord.angle; + draggedPointAdjusterAngle = degree->getValue(); + //printf("\ndraggedPointOldAngle=%.3f\n\n", draggedPointOldAngle); + return false; + } + else { + // this will let this class ignore further drag events + if (lastObject > -1) // should theoretically always be true + EditSubscriber::visibleGeometry.at(lastObject)->state = Geometry::NORMAL; + lastObject = -1; + return true; + } +} + +bool Gradient::button1Released() { + draggedPointOldAngle = -1000.; + return true; +} + +bool Gradient::drag(int modifierKey) { + // compute the polar coordinate of the mouse position + EditDataProvider *provider = getEditProvider(); + int imW, imH; + provider->getImageSize(imW, imH); + double halfSizeW = imW/2.; + double halfSizeH = imH/2.; + + if (lastObject==0 || lastObject==1) { + + // Dragging a line to change the angle + PolarCoord draggedPoint; + Coord currPos; + currPos = provider->posImage+provider->deltaImage; + Coord centerPos = draggedCenter; + + // trick to get the correct angle (clockwise/counter-clockwise) + int p = centerPos.y; + centerPos.y = currPos.y; + currPos.y = p; + + draggedPoint.setFromCartesian(centerPos, currPos); + double deltaAngle = draggedPoint.angle - draggedPointOldAngle; + if (deltaAngle>180.) // crossing the boundary (0->360) + deltaAngle -= 360.; + else if (deltaAngle<-180.) // crossing the boundary (360->0) + deltaAngle += 360.; + draggedPointOldAngle = draggedPoint.angle; + + draggedPointAdjusterAngle += deltaAngle; + if (draggedPointAdjusterAngle > 180.) + draggedPointAdjusterAngle = -360. + draggedPointAdjusterAngle; + else if (draggedPointAdjusterAngle < -180.) + draggedPointAdjusterAngle = 360. - draggedPointAdjusterAngle; + //printf("draggedPointOldAngle: %.3f / From %d,%d to %d,%d -> angle = %.3f / ", draggedPointAdjusterAngle, centerPos.x, centerPos.y, currPos.x, currPos.y, draggedPoint.angle); + //printf("currAngle: %.3f = degree: %.3f + deltaAngle: %.3f %s / draggedPointOldAngle: %.3f\n", draggedPointAdjusterAngle, degree->getValue(), deltaAngle, degree->getValue()>180.?">180":degree->getValue()<180.?"<180":"", draggedPointOldAngle); + if (int(draggedPointAdjusterAngle) != degree->getIntValue()) { + degree->setValue(draggedPointAdjusterAngle); + updateGeometry (int(centerX->getValue()), int(centerY->getValue()), strength->getValue(), degree->getValue()); + if (listener) + listener->panelChanged (EvGradientDegree, degree->getTextValue()); + return true; + } + } + else if (lastObject==2) { + // Dragging the circle to change the center + Coord currPos; + draggedCenter += provider->deltaPrevImage; + currPos = draggedCenter; + currPos.clip(imW, imH); + int newCenterX = int((double(currPos.x)-halfSizeW)/halfSizeW*100.); + int newCenterY = int((double(currPos.y)-halfSizeH)/halfSizeH*100.); + if (newCenterX!=centerX->getIntValue() || newCenterY!=centerY->getIntValue()) { + centerX->setValue(newCenterX); + centerY->setValue(newCenterY); + updateGeometry (newCenterX, newCenterY, strength->getValue(), degree->getValue()); + if (listener) + listener->panelChanged (EvGradientCenter, Glib::ustring::compose ("X=%1\nY=%2", centerX->getTextValue(), centerY->getTextValue())); + return true; + } + } + return false; +} + +void Gradient::switchOffEditMode () { + if (edit->get_active()) { + // switching off the toggle button + bool wasBlocked = editConn.block(true); + edit->set_active(false); + if (!wasBlocked) editConn.block(false); + } + EditSubscriber::switchOffEditMode(); // disconnect +} + diff --git a/rtgui/gradient.h b/rtgui/gradient.h index 7b46b773f..458f9468a 100644 --- a/rtgui/gradient.h +++ b/rtgui/gradient.h @@ -7,32 +7,55 @@ #include #include "adjuster.h" #include "toolpanel.h" +#include "edit.h" -class Gradient : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel { +class Gradient : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel, public EditSubscriber { + + private: + int lastObject; protected: Gtk::CheckButton* enabled; + Gtk::ToggleButton* edit; Adjuster* degree; Adjuster* feather; Adjuster* strength; Adjuster* centerX; Adjuster* centerY; + double draggedPointOldAngle; + double draggedPointAdjusterAngle; + Coord draggedCenter; bool lastEnabled; - sigc::connection enaConn; + sigc::connection enaConn, editConn; + + void editToggled (); public: Gradient (); + ~Gradient (); void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); void setBatchMode (bool batchMode); + void updateGeometry (int centerX_, int centerY_, double strength_, double degree_); + void adjusterChanged (Adjuster* a, double newval); void enabledChanged (); void setAdjusterBehavior (bool degreeadd, bool featheradd, bool strengthadd, bool centeradd); void trimValues (rtengine::procparams::ProcParams* pp); + + void setEditProvider (EditDataProvider* provider); + + // EditSubscriber interface + CursorShape getCursor(int objectID); + bool mouseOver(int modifierKey); + bool button1Pressed(int modifierKey); + bool button1Released(); + bool drag(int modifierKey); + void switchOffEditMode (); }; #endif diff --git a/rtgui/imagearea.cc b/rtgui/imagearea.cc index 63b24525b..b41482be0 100644 --- a/rtgui/imagearea.cc +++ b/rtgui/imagearea.cc @@ -181,8 +181,10 @@ bool ImageArea::on_expose_event(GdkEventExpose* event) { Glib::RefPtr window = get_window(); Cairo::RefPtr cr = get_window()->create_cairo_context(); - if (mainCropWindow) + if (mainCropWindow) { + //printf("MainCropWindow (%d x %d)\n", window->get_width(), window->get_height()); mainCropWindow->expose (cr); + } if (options.showInfo==true && infotext!="") { int fnw, fnh; @@ -279,10 +281,19 @@ void ImageArea::subscribe(EditSubscriber *subscriber) { if (listener && listener->getToolBar()) listener->getToolBar()->startEditMode (); + + if (subscriber->getEditingType() == ET_OBJECTS) { + // In this case, no need to reprocess the image, so we redraw the image to display the geometry + queue_draw(); + } } - void ImageArea::unsubscribe() { + bool wasObjectType = false; + EditSubscriber* oldSubscriber = EditDataProvider::getCurrSubscriber(); + if (oldSubscriber && oldSubscriber->getEditingType()==ET_OBJECTS) + wasObjectType = true; + EditDataProvider::unsubscribe(); // Ask the Crops to free-up edit mode buffers mainCropWindow->cropHandler.setEditSubscriber(NULL); @@ -292,6 +303,9 @@ void ImageArea::unsubscribe() { if (listener && listener->getToolBar()) listener->getToolBar()->stopEditMode (); + + if (wasObjectType) + queue_draw(); } void ImageArea::getImageSize (int &w, int&h) { diff --git a/tools/coordinate_system.svg b/tools/coordinate_system.svg new file mode 100644 index 000000000..73227e6ac --- /dev/null +++ b/tools/coordinate_system.svg @@ -0,0 +1,525 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + imgAreaW + imgAreaH + + + + leftBorder + + + + + + upperBorder + + + Requested processing image size (including processing borders) + Allocated processing image size (including possible processing border) + + Allocated pixbuf size for display (no extra border here!) + + + + + + + + + imgX + imgY + + + + + xpos + + + + ypos + + + imgX + + + + + imgY + + + + + CropWindow (mainCropWindow) + CropWindow + +