rawTherapee/rtgui/cropwindow.cc
Lawrence Lee 8accebe416
Fix incorrect sampled L*a*b* values
Use LCMS to convert values back into L*a*b*. The pipette buffer has the
output or working profile applied with LCMS. Performing the inverse
operation fixes the incorrect values shown in the navigator, histogram
indicator bars, and lockable color pickers.
2023-05-28 18:15:27 -07:00

2783 lines
109 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <iomanip>
#ifdef WIN32
#include <windows.h>
#endif
#include "cropwindow.h"
#include "cursormanager.h"
#include "guiutils.h"
#include "imagearea.h"
#include "lockablecolorpicker.h"
#include "options.h"
#include "rtimage.h"
#include "threadutils.h"
#include "editcallbacks.h"
#include "editbuffer.h"
#include "editwidgets.h"
#include "pointermotionlistener.h"
#include "rtsurface.h"
#include "../rtengine/dcrop.h"
#include "../rtengine/imagesource.h"
#include "../rtengine/procparams.h"
#include "../rtengine/rt_math.h"
using namespace rtengine;
bool CropWindow::initialized = false;
Glib::ustring CropWindow::zoomOuttt;
Glib::ustring CropWindow::zoomIntt;
Glib::ustring CropWindow::zoom100tt;
Glib::ustring CropWindow::closett;
CropWindow::CropWindow (ImageArea* parent, bool isLowUpdatePriority_, bool isDetailWindow)
: ObjectMOBuffer(parent), state(SNormal), press_x(0), press_y(0), action_x(0), action_y(0), pickedObject(-1), pickModifierKey(0), rot_deg(0), onResizeArea(false), deleted(false),
fitZoomEnabled(true), fitZoom(false), cursor_type(CSArrow), /*isLowUpdatePriority(isLowUpdatePriority_),*/ hoveredPicker(nullptr), cropLabel(Glib::ustring("100%")),
backColor(options.bgcolor), decorated(true), isFlawnOver(false), titleHeight(30), sideBorderWidth(3), lowerBorderWidth(3),
upperBorderWidth(1), sepWidth(2), xpos(30), ypos(30), width(0), height(0), imgAreaX(0), imgAreaY(0), imgAreaW(0), imgAreaH(0),
imgX(-1), imgY(-1), imgW(1), imgH(1), iarea(parent), cropZoom(0), zoomVersion(0), exposeVersion(0), cropgl(nullptr),
pmlistener(nullptr), pmhlistener(nullptr), scrollAccum(0.0), observedCropWin(nullptr),
crop_custom_ratio(0.f)
{
initZoomSteps();
Glib::RefPtr<Pango::Context> context = parent->get_pango_context () ;
Pango::FontDescription fontd = context->get_font_description ();
fontd.set_weight (Pango::WEIGHT_BOLD);
fontd.set_size(8 * Pango::SCALE);
context->set_font_description (fontd);
Glib::RefPtr<Pango::Layout> cllayout = parent->create_pango_layout("1000%");
int iw, ih;
cllayout->get_pixel_size (iw, ih);
titleHeight = ih;
if (!initialized) {
zoomOuttt = "Zoom Out";
zoomIntt = "Zoom In";
zoom100tt = "Zoom 100/%";
closett = "Close";
initialized = true;
}
bZoomOut = new LWButton(Cairo::RefPtr<RTSurface>(new RTSurface("magnifier-minus-small.png")), 0, nullptr, LWButton::Left, LWButton::Center, &zoomOuttt);
bZoomIn = new LWButton(Cairo::RefPtr<RTSurface>(new RTSurface("magnifier-plus-small.png")), 1, nullptr, LWButton::Left, LWButton::Center, &zoomIntt);
bZoom100 = new LWButton(Cairo::RefPtr<RTSurface>(new RTSurface("magnifier-1to1-small.png")), 2, nullptr, LWButton::Left, LWButton::Center, &zoom100tt);
//bZoomFit = new LWButton (Cairo::RefPtr<RTSurface>(new RTSurface("magnifier-fit.png")), 3, NULL, LWButton::Left, LWButton::Center, "Zoom Fit");
bClose = new LWButton(Cairo::RefPtr<RTSurface>(new RTSurface("cancel-small.png")), 4, nullptr, LWButton::Right, LWButton::Center, &closett);
buttonSet.add (bZoomOut);
buttonSet.add (bZoomIn);
buttonSet.add (bZoom100);
buttonSet.add (bClose);
buttonSet.setColors (Gdk::RGBA("black"), Gdk::RGBA("white"));
buttonSet.setButtonListener (this);
int bsw, bsh;
buttonSet.getMinimalDimensions (bsw, bsh);
if (bsh > titleHeight) {
titleHeight = bsh;
}
minWidth = bsw + iw + 2 * sideBorderWidth;
cropHandler.setDisplayHandler(this);
cropHandler.newImage (parent->getImProcCoordinator(), isDetailWindow);
}
CropWindow::~CropWindow ()
{
for (auto colorPicker : colorPickers) {
delete colorPicker;
}
}
void CropWindow::initZoomSteps()
{
zoomSteps.push_back(ZoomStep(" 1%", 0.01, 999, true));
zoomSteps.push_back(ZoomStep(" 2%", 0.02, 500, true));
zoomSteps.push_back(ZoomStep(" 5%", 0.05, 200, true));
zoomSteps.push_back(ZoomStep(" 6%", 1.0/15.0, 150, true));
zoomSteps.push_back(ZoomStep(" 8%", 1.0/12.0, 120, true));
char lbl[64];
for (int s = 100; s >= 11; --s) {
float z = 10.f / s;
snprintf(lbl, sizeof(lbl), "% 2d%%", int(z * 100));
bool is_major = (s == s/10 * 10);
zoomSteps.push_back(ZoomStep(lbl, z, s, is_major));
}
zoom11index = zoomSteps.size();
for (int s = 1; s <= 8; ++s) {
snprintf(lbl, sizeof(lbl), "%d00%%", s);
zoomSteps.push_back(ZoomStep(lbl, s, s * 1000, true));
}
zoomSteps.push_back(ZoomStep("1600%", 16, 16000, true));
}
void CropWindow::enable()
{
cropHandler.setEnabled (true);
}
void CropWindow::setPosition (int x, int y)
{
if (y < 0) {
y = 0;
}
xpos = x;
ypos = y;
if (decorated) {
buttonSet.arrangeButtons (xpos + sideBorderWidth, ypos + upperBorderWidth, width - 2 * sideBorderWidth, titleHeight);
}
}
void CropWindow::getPosition (int& x, int& y)
{
x = xpos;
y = ypos;
}
void CropWindow::getCropPosition (int& x, int& y)
{
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
if (state != SCropImgMove) {
x = cropX;
y = cropY;
} else {
x = cropX + action_x;
y = cropY + action_y;
}
}
void CropWindow::getCropRectangle (int& x, int& y, int& w, int& h)
{
cropHandler.getPosition (x, y);
cropHandler.getSize (w, h);
}
void CropWindow::setCropPosition (int x, int y, bool update)
{
cropHandler.setAnchorPosition (x, y, update);
for (auto listener : listeners) {
listener->cropPositionChanged (this);
}
}
void CropWindow::centerCrop (bool update)
{
cropHandler.centerAnchor (update);
for (auto listener : listeners) {
listener->cropPositionChanged (this);
}
}
void CropWindow::setSize (int w, int h, bool norefresh)
{
width = w;
height = h;
fitZoom = false;
if (width < minWidth) {
width = minWidth;
}
if (height < 64) {
height = 64;
}
if (decorated) {
imgAreaX = sideBorderWidth;
imgAreaY = upperBorderWidth + titleHeight + sepWidth;
imgAreaW = width - 2 * sideBorderWidth;
imgAreaH = height - lowerBorderWidth - titleHeight - sepWidth - upperBorderWidth;
buttonSet.arrangeButtons (xpos + sideBorderWidth, ypos + upperBorderWidth, width - 2 * sideBorderWidth, titleHeight);
} else {
imgAreaX = imgAreaY = 0;
imgAreaW = width;
imgAreaH = height;
}
if (!norefresh) {
ObjectMOBuffer::resize(imgAreaW, imgAreaH);
cropHandler.setWSize (imgAreaW, imgAreaH);
}
//iarea->redraw ();
}
void CropWindow::getSize (int& w, int& h)
{
w = width;
h = height;
}
void CropWindow::getCropSize (int& w, int& h)
{
w = imgAreaW;
h = imgAreaH;
}
void CropWindow::getCropAnchorPosition (int& x, int& y)
{
cropHandler.getAnchorPosition(x, y);
}
void CropWindow::setCropAnchorPosition (int x, int y)
{
cropHandler.setAnchorPosition(x, y);
}
bool CropWindow::isInside (int x, int y)
{
return x >= xpos && x < xpos + width && y >= ypos && y < ypos + height;
}
void CropWindow::leaveNotify (GdkEventCrossing* event)
{
EditSubscriber* subscriber = iarea->getCurrSubscriber();
if (state == SNormal && subscriber && subscriber->getEditingType() == ET_PIPETTE) {
iarea->setPipetteVal1(-1.f);
iarea->setPipetteVal2(-1.f);
iarea->setPipetteVal3(-1.f);
if (subscriber->mouseOver(0)) {
iarea->redraw();
}
}
}
void CropWindow::flawnOver (bool isFlawnOver)
{
this->isFlawnOver = isFlawnOver;
}
void CropWindow::scroll (int state, GdkScrollDirection direction, int x, int y, double deltaX, double deltaY)
{
double delta = 0.0;
if (std::fabs(deltaX) > std::fabs(deltaY)) {
delta = deltaX;
} else {
delta = deltaY;
}
if (direction == GDK_SCROLL_SMOOTH) {
scrollAccum += delta;
//Only change zoom level if we've accumulated +/- 1.0 of deltas. This conditional handles the previous delta=0.0 case
if (std::fabs(scrollAccum) < 1.0) {
return;
}
}
bool isUp = direction == GDK_SCROLL_UP || (direction == GDK_SCROLL_SMOOTH && scrollAccum < 0.0);
scrollAccum = 0.0;
if ((state & GDK_CONTROL_MASK) && onArea(ColorPicker, x, y)) {
// resizing a color picker
if (isUp) {
hoveredPicker->incSize();
updateHoveredPicker();
iarea->redraw ();
} else {
hoveredPicker->decSize();
updateHoveredPicker();
iarea->redraw ();
}
} else {
// not over a color picker, we zoom in/out
int newCenterX = x;
int newCenterY = y;
screenCoordToImage(newCenterX, newCenterY, newCenterX, newCenterY);
if (isUp && !isMaxZoom()) {
zoomIn (true, newCenterX, newCenterY);
} else if (!isUp && !isMinZoom()) {
zoomOut (true, newCenterX, newCenterY);
}
}
}
void CropWindow::buttonPress (int button, int type, int bstate, int x, int y)
{
bool needRedraw = true; // most common case ; not redrawing are exceptions
const auto editSubscriber = iarea->getCurrSubscriber();
iarea->grabFocus (this);
if (button == 1) {
if (type == GDK_2BUTTON_PRESS && onArea (CropImage, x, y) && iarea->getToolMode () != TMColorPicker && (state == SNormal || state == SCropImgMove)) {
if (fitZoomEnabled) {
if (fitZoom) {
state = SNormal;
zoomVersion = exposeVersion;
screenCoordToImage (x, y, action_x, action_y);
changeZoom (zoom11index, true, action_x, action_y);
fitZoom = false;
} else if (options.cropAutoFit) {
zoomFitCrop();
} else {
zoomFit();
}
} else {
zoom11 ();
}
state = SNormal;
}
else if (type == GDK_BUTTON_PRESS && state == SNormal) {
if (onArea (CropToolBar, x, y)) {
if (!decorated || !buttonSet.pressNotify (x, y)) {
state = SCropWinMove;
action_x = x;
action_y = y;
press_x = xpos;
press_y = ypos;
}
} else if (onArea (CropResize, x, y)) {
state = SCropWinResize;
action_x = x;
action_y = y;
press_x = width;
press_y = height;
} else {
if (onArea (CropImage, x, y)) { // events inside of the image domain
crop_custom_ratio = 0.f;
if ((bstate & GDK_SHIFT_MASK) && cropHandler.cropParams->w > 0 && cropHandler.cropParams->h > 0) {
crop_custom_ratio = float(cropHandler.cropParams->w) / float(cropHandler.cropParams->h);
}
if (iarea->getToolMode () == TMColorPicker) {
if (hoveredPicker) {
if ((bstate & GDK_CONTROL_MASK) && !(bstate & GDK_SHIFT_MASK)) {
hoveredPicker->decSize();
updateHoveredPicker();
needRedraw = true;
} else if (!(bstate & GDK_CONTROL_MASK) && (bstate & GDK_SHIFT_MASK)) {
hoveredPicker->rollDisplayedValues();
needRedraw = true;
} else if (!(bstate & GDK_CONTROL_MASK) && !(bstate & GDK_SHIFT_MASK)) {
// Color Picker drag starts
state = SDragPicker;
}
} else {
// Add a new Color Picker
rtengine::Coord imgPos;
screenCoordToImage(x, y, imgPos.x, imgPos.y);
LockableColorPicker *newPicker = new LockableColorPicker(this, cropHandler.colorParams.get());
colorPickers.push_back(newPicker);
hoveredPicker = newPicker;
updateHoveredPicker(&imgPos);
state = SDragPicker;
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
needRedraw = true;
}
} else if ((iarea->getToolMode () == TMHand
|| iarea->getToolMode() == TMPerspective)
&& editSubscriber
&& cropgl
&& cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y)
&& editSubscriber->getEditingType() == ET_OBJECTS
&& iarea->getObject() >= 0
)
{
needRedraw = editSubscriber->button1Pressed(bstate);
if (editSubscriber->isDragging()) {
state = SEditDrag1;
} else if (editSubscriber->isPicking()) {
state = SEditPick1;
pickedObject = iarea->getObject();
pickModifierKey = bstate;
} else if (iarea->getToolMode() == TMPerspective) {
state = SCropImgMove;
}
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
} else if (onArea (CropTopLeft, x, y)) {
state = SResizeTL;
press_x = x;
action_x = cropHandler.cropParams->x;
press_y = y;
action_y = cropHandler.cropParams->y;
} else if (onArea (CropTopRight, x, y)) {
state = SResizeTR;
press_x = x;
action_x = cropHandler.cropParams->w;
press_y = y;
action_y = cropHandler.cropParams->y;
} else if (onArea (CropBottomLeft, x, y)) {
state = SResizeBL;
press_x = x;
action_x = cropHandler.cropParams->x;
press_y = y;
action_y = cropHandler.cropParams->h;
} else if (onArea (CropBottomRight, x, y)) {
state = SResizeBR;
press_x = x;
action_x = cropHandler.cropParams->w;
press_y = y;
action_y = cropHandler.cropParams->h;
} else if (onArea (CropTop, x, y)) {
state = SResizeH1;
press_y = y;
action_y = cropHandler.cropParams->y;
} else if (onArea (CropBottom, x, y)) {
state = SResizeH2;
press_y = y;
action_y = cropHandler.cropParams->h;
} else if (onArea (CropLeft, x, y)) {
state = SResizeW1;
press_x = x;
action_x = cropHandler.cropParams->x;
} else if (onArea (CropRight, x, y)) {
state = SResizeW2;
press_x = x;
action_x = cropHandler.cropParams->w;
} else if ((bstate & GDK_SHIFT_MASK) && onArea (CropInside, x, y)) {
state = SCropMove;
press_x = x;
press_y = y;
action_x = cropHandler.cropParams->x;
action_y = cropHandler.cropParams->y;
} else if (onArea (CropObserved, x, y)) {
state = SObservedMove;
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
} else if (iarea->getToolMode () == TMStraighten) {
state = SRotateSelecting;
press_x = x;
press_y = y;
action_x = x;
action_y = y;
rot_deg = 0;
} else if (iarea->getToolMode () == TMSpotWB) {
int spotx, spoty;
screenCoordToImage (x, y, spotx, spoty);
iarea->spotWBSelected (spotx, spoty);
} else if (iarea->getToolMode () == TMCropSelect && cropgl) {
state = SCropSelecting;
screenCoordToImage (x, y, press_x, press_y);
cropHandler.cropParams->enabled = true;
cropHandler.cropParams->x = press_x;
cropHandler.cropParams->y = press_y;
cropHandler.cropParams->w = cropHandler.cropParams->h = 1;
cropgl->cropInit (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h);
} else if (iarea->getToolMode () == TMHand) {
if (editSubscriber) {
if ((cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK))) || editSubscriber->getEditingType() == ET_OBJECTS) {
needRedraw = editSubscriber->button1Pressed(bstate);
if (editSubscriber->isDragging()) {
state = SEditDrag1;
} else if (editSubscriber->isPicking()) {
state = SEditPick1;
pickedObject = iarea->getObject();
pickModifierKey = bstate;
}
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
}
}
if (state != SEditDrag1 && state != SEditPick1) {
state = SCropImgMove;
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
}
} else { // if(zoomSteps[cropZoom].zoom > cropHandler.getFitZoom()) { // only allow move when image is only partial visible
state = SCropImgMove;
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
}
} else if (iarea->getToolMode () == TMHand || iarea->getToolMode() == TMPerspective) { // events outside of the image domain
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
needRedraw = editSubscriber->button1Pressed(bstate);
if (editSubscriber->isDragging()) {
state = SEditDrag1;
} else if (editSubscriber->isPicking()) {
state = SEditPick1;
pickedObject = iarea->getObject();
pickModifierKey = bstate;
}
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
}
} else if (iarea->getToolMode () == TMColorPicker && hoveredPicker) {
if ((bstate & GDK_CONTROL_MASK) && !(bstate & GDK_SHIFT_MASK)) {
if (hoveredPicker->decSize()) {
updateHoveredPicker();
needRedraw = true;
}
} else if (!(bstate & GDK_CONTROL_MASK) && (bstate & GDK_SHIFT_MASK)) {
hoveredPicker->rollDisplayedValues();
} else if (!(bstate & GDK_CONTROL_MASK) && !(bstate & GDK_SHIFT_MASK)) {
// Color Picker drag starts
state = SDragPicker;
}
}
}
}
} else if (button == 2) {
if (iarea->getToolMode () == TMHand) {
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
needRedraw = editSubscriber->button2Pressed(bstate);
if (editSubscriber->isDragging()) {
state = SEditDrag2;
} else if (editSubscriber->isPicking()) {
state = SEditPick2;
pickedObject = iarea->getObject();
pickModifierKey = bstate;
}
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
}
}
} else if (button == 3) {
if (iarea->getToolMode () == TMHand || iarea->getToolMode() == TMPerspective) {
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
needRedraw = editSubscriber->button3Pressed(bstate);
if (editSubscriber->isDragging()) {
state = SEditDrag3;
} else if (editSubscriber->isPicking()) {
state = SEditPick3;
pickedObject = iarea->getObject();
pickModifierKey = bstate;
}
press_x = x;
press_y = y;
action_x = 0;
action_y = 0;
}
}
else if (iarea->getToolMode () == TMColorPicker && type == GDK_BUTTON_PRESS && state == SNormal) {
if (hoveredPicker) {
if((bstate & GDK_CONTROL_MASK) && (bstate & GDK_SHIFT_MASK)) {
// Deleting all pickers !
for (auto colorPicker : colorPickers) {
delete colorPicker;
}
colorPickers.clear();
hoveredPicker = nullptr;
state = SDeletePicker;
needRedraw = true;
} else if ((bstate & GDK_CONTROL_MASK) && !(bstate & GDK_SHIFT_MASK)) {
if (hoveredPicker->incSize()) {
updateHoveredPicker();
needRedraw = true;
}
} else if (!(bstate & GDK_CONTROL_MASK) && !(bstate & GDK_SHIFT_MASK)) {
// Deleting the hovered picker
for (std::vector<LockableColorPicker*>::iterator i = colorPickers.begin(); i != colorPickers.end(); ++i) {
if (*i == hoveredPicker) {
colorPickers.erase(i);
delete hoveredPicker;
hoveredPicker = nullptr;
state = SDeletePicker;
needRedraw = true;
break;
}
}
}
}
}
}
if (needRedraw) {
iarea->redraw ();
}
updateCursor (x, y);
}
void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y)
{
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
bool needRedraw = false;
if (state == SCropWinResize) {
int newWidth = press_x + x - action_x;
int newHeight = press_y + y - action_y;
setSize(newWidth, newHeight);
if (decorated) {
options.detailWindowWidth = newWidth;
options.detailWindowHeight = newHeight;
}
state = SNormal;
for (auto listener : listeners) {
listener->cropWindowSizeChanged (this);
}
needRedraw = true;
if (fitZoom && options.cropAutoFit) {
zoomFitCrop();
}
} else if (state == SCropWinMove) {
if (iarea->showColorPickers () && !colorPickers.empty()) {
needRedraw = true;
}
} else if (state == SCropImgMove) {
cropHandler.update ();
state = SNormal;
for (std::list<CropWindowListener*>::iterator i = listeners.begin(); i != listeners.end(); ++i) {
(*i)->cropPositionChanged (this);
}
needRedraw = true;
} else if (state == SRotateSelecting) {
iarea->straightenReady (rot_deg);
iarea->setToolHand ();
needRedraw = true;
} else if (state == SObservedMove) {
observedCropWin->remoteMoveReady ();
state = SNormal;
needRedraw = true;
} else if (state == SEditDrag1 || state == SEditDrag2 || state == SEditDrag3) {
if (editSubscriber) {
if (state == SEditDrag1) {
needRedraw = editSubscriber->button1Released();
} else if (state == SEditDrag2) {
needRedraw = editSubscriber->button2Released();
} else if (state == SEditDrag3) {
needRedraw = editSubscriber->button3Released();
}
rtengine::Crop* crop = static_cast<rtengine::Crop*>(cropHandler.getCrop());
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);
Coord cropPos;
if (state == SEditDrag1 && editSubscriber->getEditingType() == ET_PIPETTE) {
screenCoordToCropBuffer (x, y, cropPos.x, cropPos.y);
iarea->setObject(onArea (CropImage, x, y) && !onArea (CropObserved, x, y) ? 1 : 0);
//iarea->setObject(cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) ? 1 : 0);
if (iarea->getObject()) {
crop->getPipetteData(cropPos.x, cropPos.y, iarea->getPipetteRectSize());
//printf("PipetteData: %.3f %.3f %.3f\n", iarea->pipetteVal[0], iarea->pipetteVal[1], iarea->pipetteVal[2]);
} else {
iarea->setPipetteVal1(-1.f);
iarea->setPipetteVal2(-1.f);
iarea->setPipetteVal3(-1.f);
}
} else if (editSubscriber->getEditingType() == ET_OBJECTS) {
screenCoordToCropCanvas (x, y, cropPos.x, cropPos.y);
iarea->setObject(ObjectMOBuffer::getObjectID(cropPos));
}
needRedraw |= editSubscriber->mouseOver(bstate);
} else {
iarea->setObject(0);
}
iarea->deltaImage.set(0, 0);
iarea->deltaScreen.set(0, 0);
iarea->deltaPrevImage.set(0, 0);
iarea->deltaPrevScreen.set(0, 0);
} else if (state == SEditPick1 || state == SEditPick2 || state == SEditPick3) {
if (editSubscriber) {
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);
Coord cropPos;
screenCoordToCropCanvas (x, y, cropPos.x, cropPos.y);
iarea->setObject(ObjectMOBuffer::getObjectID(cropPos));
int buttonMask = ((state == SEditPick1) ? GDK_BUTTON1_MASK : 0)
| ((state == SEditPick2) ? GDK_BUTTON2_MASK : 0)
| ((state == SEditPick3) ? GDK_BUTTON3_MASK : 0);
bool elemPicked = iarea->getObject() == pickedObject && bstate == (pickModifierKey | buttonMask);
if (state == SEditPick1) {
needRedraw = editSubscriber->pick1 (elemPicked);
} else if (state == SEditPick2) {
needRedraw = editSubscriber->pick2 (elemPicked);
} else if (state == SEditPick3) {
needRedraw = editSubscriber->pick3 (elemPicked);
}
pickedObject = -1;
iarea->setObject(-1);
pickModifierKey = 0;
needRedraw |= editSubscriber->mouseOver (bstate);
} else {
iarea->setObject(0);
}
} else if (state == SDeletePicker) {
needRedraw = true;
} else if (state == SNormal && iarea->getToolMode() == TMColorPicker && !hoveredPicker && button == 3) {
iarea->setToolHand ();
}
if (state != SEditDrag1 && state != SEditDrag2 && state != SEditDrag3) {
iarea->deltaImage.set(0, 0);
iarea->deltaScreen.set(0, 0);
iarea->deltaPrevImage.set(0, 0);
iarea->deltaPrevScreen.set(0, 0);
}
if (cropgl && (state == SCropSelecting || state == SResizeH1 || state == SResizeH2 || state == SResizeW1 || state == SResizeW2 || state == SResizeTL || state == SResizeTR || state == SResizeBL || state == SResizeBR || state == SCropMove)) {
cropgl->cropManipReady ();
iarea->setToolHand ();
needRedraw = true;
if (fitZoom && options.cropAutoFit) {
zoomFitCrop();
}
}
if (decorated) {
buttonSet.releaseNotify (x, y);
}
if (deleted) {
iarea->flawnOverWindow = nullptr;
delete this;
return;
}
if (state != SDeletePicker && state != SEditDrag3 && state != SEditPick3 && button == 3 && !(bstate & (GDK_SHIFT_MASK|GDK_CONTROL_MASK))) {
iarea->setPipetteVal1(-1.f);
iarea->setPipetteVal2(-1.f);
iarea->setPipetteVal3(-1.f);
needRedraw = iarea->getObject() == 1;
if (editSubscriber && editSubscriber->getEditingType() == ET_PIPETTE) {
editSubscriber->mouseOver(0);
}
iarea->setToolHand ();
}
state = SNormal;
iarea->grabFocus (nullptr);
if (needRedraw) {
iarea->redraw ();
}
updateCursor (x, 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);
iarea->redraw ();
} else if (state == SCropWinResize) {
setSize (press_x + x - action_x, press_y + y - action_y, true);
for (auto listener : listeners) {
listener->cropWindowSizeChanged (this);
}
iarea->redraw ();
} else if (state == SCropImgMove) {
// multiplier is the amplification factor ; disabled if the user selected "1" (no amplification)
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;
}
int newAction_x = (press_x - x) / zoomSteps[cropZoom].zoom * factor;
int newAction_y = (press_y - y) / zoomSteps[cropZoom].zoom * factor;
int deltaX = newAction_x - action_x;
int deltaY = newAction_y - action_y;
action_x = newAction_x;
action_y = newAction_y;
cropHandler.moveAnchor(deltaX, deltaY, false);
for (auto listener : listeners) {
listener->cropPositionChanged (this);
}
iarea->redraw ();
} else if (state == SRotateSelecting) {
action_x = x;
action_y = y;
iarea->redraw ();
} else if (state == SNormal && iarea->getToolMode () == TMSpotWB) {
action_x = x;
action_y = y;
iarea->redraw ();
} else if (state == SResizeH1 && cropgl) {
int oy = cropHandler.cropParams->y;
cropHandler.cropParams->y = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->h += oy - cropHandler.cropParams->y;
cropgl->cropHeight1Resized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeH2 && cropgl) {
cropHandler.cropParams->h = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropgl->cropHeight2Resized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeW1 && cropgl) {
int ox = cropHandler.cropParams->x;
cropHandler.cropParams->x = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->w += ox - cropHandler.cropParams->x;
cropgl->cropWidth1Resized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeW2 && cropgl) {
cropHandler.cropParams->w = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
cropgl->cropWidth2Resized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeTL && cropgl) {
int ox = cropHandler.cropParams->x;
cropHandler.cropParams->x = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->w += ox - cropHandler.cropParams->x;
int oy = cropHandler.cropParams->y;
cropHandler.cropParams->y = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->h += oy - cropHandler.cropParams->y;
cropgl->cropTopLeftResized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeTR && cropgl) {
cropHandler.cropParams->w = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
int oy = cropHandler.cropParams->y;
cropHandler.cropParams->y = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->h += oy - cropHandler.cropParams->y;
cropgl->cropTopRightResized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeBL && cropgl) {
int ox = cropHandler.cropParams->x;
cropHandler.cropParams->x = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->w += ox - cropHandler.cropParams->x;
cropHandler.cropParams->h = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropgl->cropBottomLeftResized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SResizeBR && cropgl) {
cropHandler.cropParams->w = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->h = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropgl->cropBottomRightResized (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h, crop_custom_ratio);
iarea->redraw ();
} else if (state == SCropMove && cropgl) {
cropHandler.cropParams->x = action_x + (x - press_x) / zoomSteps[cropZoom].zoom;
cropHandler.cropParams->y = action_y + (y - press_y) / zoomSteps[cropZoom].zoom;
cropgl->cropMoved (cropHandler.cropParams->x, cropHandler.cropParams->y, cropHandler.cropParams->w, cropHandler.cropParams->h);
iarea->redraw ();
} else if (state == SCropSelecting && cropgl) {
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);
if (cx2 > cx1) {
cropHandler.cropParams->x = cx1;
cropHandler.cropParams->w = cx2 - cx1 + 1;
} else {
cropHandler.cropParams->x = cx2;
cropHandler.cropParams->w = cx1 - cx2 + 1;
}
if (cy2 > cy1) {
cropHandler.cropParams->y = cy1;
cropHandler.cropParams->h = cy2 - cy1 + 1;
} else {
cropHandler.cropParams->y = cy2;
cropHandler.cropParams->h = cy1 - cy2 + 1;
}
iarea->redraw ();
} else if (state == SObservedMove) {
int new_action_x = x - press_x;
int new_action_y = y - press_y;
observedCropWin->remoteMove ((new_action_x - action_x) / zoomSteps[cropZoom].zoom, (new_action_y - action_y) / zoomSteps[cropZoom].zoom);
action_x = new_action_x;
action_y = new_action_y;
iarea->redraw ();
} else if (state == SDragPicker) {
Coord imgPos;
action_x = x - press_x;
action_y = y - press_y;
screenCoordToImage (x, y, imgPos.x, imgPos.y);
if (imgPos.x < 0) {
imgPos.x = 0;
}else if (imgPos.x >= iarea->getImProcCoordinator()->getFullWidth()) {
imgPos.x = iarea->getImProcCoordinator()->getFullWidth()-1;
}
if (imgPos.y < 0) {
imgPos.y = 0;
}else if (imgPos.y >= iarea->getImProcCoordinator()->getFullHeight()) {
imgPos.y = iarea->getImProcCoordinator()->getFullHeight()-1;
}
updateHoveredPicker (&imgPos);
iarea->redraw ();
} else if (state == SNormal && iarea->getToolMode () == TMColorPicker && onArea(ColorPicker, x, y)) {
// TODO: we could set the hovered picker as Highlighted here
// Keep this if statement, the onArea will find out the hoveredPicker and will be used to update the cursor
} else if (editSubscriber) {
rtengine::Crop* crop = static_cast<rtengine::Crop*>(cropHandler.getCrop());
if (state == SNormal || state == SEditPick1 || state == SEditPick2 || state == SEditPick3) {
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);
Coord cropPos;
if (editSubscriber->getEditingType() == ET_PIPETTE) {
screenCoordToCropBuffer (x, y, cropPos.x, cropPos.y);
iarea->setObject(onArea (CropImage, x, y) && !onArea (CropObserved, x, y) ? 1 : 0);
//iarea->setObject(cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) ? 1 : 0);
if (iarea->getObject()) {
crop->getPipetteData(cropPos.x, cropPos.y, iarea->getPipetteRectSize());
//printf("PipetteData: %.3f %.3f %.3f\n", iarea->pipetteVal[0], iarea->pipetteVal[1], iarea->pipetteVal[2]);
} else {
iarea->setPipetteVal1(-1.f);
iarea->setPipetteVal2(-1.f);
iarea->setPipetteVal3(-1.f);
}
} else if (editSubscriber->getEditingType() == ET_OBJECTS) {
screenCoordToCropCanvas (x, y, cropPos.x, cropPos.y);
iarea->setObject(ObjectMOBuffer::getObjectID(cropPos));
}
if (editSubscriber->mouseOver(bstate)) {
iarea->redraw ();
}
} else if (state == SEditDrag1 || state == SEditDrag2 || state == SEditDrag3) {
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);
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);
currPos.set(x, 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 (state == SEditDrag1) {
if (editSubscriber->drag1(bstate)) {
iarea->redraw ();
}
} else if (state == SEditDrag2) {
if (editSubscriber->drag2(bstate)) {
iarea->redraw ();
}
} else if (state == SEditDrag3) {
if (editSubscriber->drag3(bstate)) {
iarea->redraw ();
}
}
}
}
updateCursor (x, y);
bool oRA = onArea (CropResize, x, y);
if (oRA != onResizeArea) {
onResizeArea = oRA;
iarea->redraw ();
}
if (decorated) {
buttonSet.motionNotify (x, y);
}
if (pmlistener) {
int mx, my;
screenCoordToImage (x, y, mx, my);
MyMutex::MyLock lock(cropHandler.cimg);
if (!onArea (CropImage, x, y) || !cropHandler.cropPixbuftrue) {
cropHandler.getFullImageSize(mx, my);
// pmlistener->pointerMoved (false, cropHandler.colorParams->working, mx, my, -1, -1, -1);
// if (pmhlistener) pmhlistener->pointerMoved (false, cropHandler.colorParams->working, mx, my, -1, -1, -1);
/* Glib::ustring outputProfile;
outputProfile =cropHandler.colorParams->output ;
printf("Using \"%s\" output\n", outputProfile.c_str());
if(outputProfile==options.rtSettings.srgb) printf("OK SRGB2");
*/
pmlistener->pointerMoved (false, *cropHandler.colorParams, mx, my, -1, -1, -1);
if (pmhlistener) {
pmhlistener->pointerMoved (false, *cropHandler.colorParams, mx, my, -1, -1, -1);
}
} else {
/*
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;
if(decorated) {
vx -= sideBorderWidth;
vy -= (titleHeight + upperBorderWidth + sepWidth);
}
// 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.cropPixbuftrue->get_width();
int imheight = cropHandler.cropPixbuftrue->get_height();
if (vx < imwidth && vy < imheight) {
guint8* pix = cropHandler.cropPixbuftrue->get_pixels() + vy * cropHandler.cropPixbuftrue->get_rowstride() + vx * 3;
int rval = pix[0];
int gval = pix[1];
int bval = pix[2];
bool isRaw = false;
rtengine::StagedImageProcessor* ipc = iarea->getImProcCoordinator();
if(ipc) {
procparams::ProcParams params;
ipc->getParams(&params, true);
isRaw = params.raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::NONE) || params.raw.xtranssensor.method == RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::NONE);
if(isRaw) {
ImageSource *isrc = static_cast<ImageSource*>(ipc->getInitialImage());
isrc->getRawValues(mx, my, params.coarse.rotate, rval, gval, bval);
}
}
// Updates the Navigator
// TODO: possible double color conversion if rval, gval, bval come from cropHandler.cropPixbuftrue ? see issue #4583
pmlistener->pointerMoved (true, *cropHandler.colorParams, mx, my, rval, gval, bval, isRaw);
if (pmhlistener) {
// Updates the HistogramRGBArea
pmhlistener->pointerMoved (true, *cropHandler.colorParams, mx, my, rval, gval, bval);
}
}
}
}
}
bool CropWindow::onArea (CursorArea a, int x, int y)
{
int CROPRESIZEBORDER = rtengine::max<int>(9 / zoomSteps[cropZoom].zoom, 3);
int x1, y1, w, h;
switch (a) {
case CropWinButtons:
return decorated && buttonSet.inside (x, y);
case CropToolBar:
return x > xpos && y > ypos && x < xpos + width - 1 && y < ypos + imgAreaY;
case CropImage:
return x >= xpos + imgX + imgAreaX && y >= ypos + imgY + imgAreaY && x < xpos + imgX + imgAreaX + imgW && y < ypos + imgY + imgAreaY + imgH;
case ColorPicker:
for (auto colorPicker : colorPickers) {
if (colorPicker->isOver(x, y)) {
hoveredPicker = colorPicker;
return true;
}
}
hoveredPicker = nullptr;
return false;
case CropBorder:
return
(x >= xpos + imgAreaX && y >= ypos + imgAreaY && x < xpos + imgAreaX + imgAreaW && y < ypos + imgAreaY + imgAreaH) &&
!(x >= xpos + imgX && y >= ypos + imgY && x < xpos + imgX + imgW && y < ypos + imgY + imgH);
case CropTopLeft:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
y1 >= cropHandler.cropParams->y - CROPRESIZEBORDER &&
y1 <= cropHandler.cropParams->y + CROPRESIZEBORDER &&
y >= ypos + imgY &&
x1 >= cropHandler.cropParams->x - CROPRESIZEBORDER &&
x1 <= cropHandler.cropParams->x + CROPRESIZEBORDER &&
x >= xpos + imgX;
case CropTopRight:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
y1 >= cropHandler.cropParams->y - CROPRESIZEBORDER &&
y1 <= cropHandler.cropParams->y + CROPRESIZEBORDER &&
y >= ypos + imgY &&
x1 >= cropHandler.cropParams->x + cropHandler.cropParams->w - 1 - CROPRESIZEBORDER &&
x1 <= cropHandler.cropParams->x + cropHandler.cropParams->w - 1 + CROPRESIZEBORDER &&
x < xpos + imgX + imgW;
case CropBottomLeft:
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 &&
y < ypos + imgY + imgH &&
x1 >= cropHandler.cropParams->x - CROPRESIZEBORDER &&
x1 <= cropHandler.cropParams->x + CROPRESIZEBORDER &&
x >= xpos + imgX;
case CropBottomRight:
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 &&
y < ypos + imgY + imgH &&
x1 >= cropHandler.cropParams->x + cropHandler.cropParams->w - 1 - CROPRESIZEBORDER &&
x1 <= cropHandler.cropParams->x + cropHandler.cropParams->w - 1 + CROPRESIZEBORDER &&
x < xpos + imgX + imgW;
case CropTop:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
x1 > cropHandler.cropParams->x + CROPRESIZEBORDER &&
x1 < cropHandler.cropParams->x + cropHandler.cropParams->w - 1 - CROPRESIZEBORDER &&
y1 > cropHandler.cropParams->y - CROPRESIZEBORDER &&
y1 < cropHandler.cropParams->y + CROPRESIZEBORDER &&
y >= ypos + imgY;
case CropBottom:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
x1 > cropHandler.cropParams->x + CROPRESIZEBORDER &&
x1 < cropHandler.cropParams->x + cropHandler.cropParams->w - 1 - CROPRESIZEBORDER &&
y1 > cropHandler.cropParams->y + cropHandler.cropParams->h - 1 - CROPRESIZEBORDER &&
y1 < cropHandler.cropParams->y + cropHandler.cropParams->h - 1 + CROPRESIZEBORDER &&
y < ypos + imgY + imgH;
case CropLeft:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
y1 > cropHandler.cropParams->y + CROPRESIZEBORDER &&
y1 < cropHandler.cropParams->y + cropHandler.cropParams->h - 1 - CROPRESIZEBORDER &&
x1 > cropHandler.cropParams->x - CROPRESIZEBORDER &&
x1 < cropHandler.cropParams->x + CROPRESIZEBORDER &&
x >= xpos + imgX;
case CropRight:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
y1 > cropHandler.cropParams->y + CROPRESIZEBORDER &&
y1 < cropHandler.cropParams->y + cropHandler.cropParams->h - 1 - CROPRESIZEBORDER &&
x1 > cropHandler.cropParams->x + cropHandler.cropParams->w - 1 - CROPRESIZEBORDER &&
x1 < cropHandler.cropParams->x + cropHandler.cropParams->w - 1 + CROPRESIZEBORDER &&
x < xpos + imgX + imgW;
case CropInside:
screenCoordToImage (x, y, x1, y1);
return cropHandler.cropParams->enabled &&
y1 > cropHandler.cropParams->y &&
y1 < cropHandler.cropParams->y + cropHandler.cropParams->h - 1 &&
x1 > cropHandler.cropParams->x &&
x1 < cropHandler.cropParams->x + cropHandler.cropParams->w - 1;
case CropResize:
return decorated && x >= xpos + width - 16 && y >= ypos + height - 16 && x < xpos + width && y < ypos + height;
case CropObserved:
if (!observedCropWin) {
return false;
}
getObservedFrameArea (x1, y1, w, h);
return x >= x1 && x <= x1 + w && y >= y1 && y <= y1 + h;
}
return false;
}
void CropWindow::updateCursor (int x, int y)
{
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
ToolMode tm = iarea->getToolMode ();
CursorShape newType = cursor_type;
if (state == SNormal) {
if (onArea (CropWinButtons, x, y)) {
newType = CSArrow;
} else if (onArea (CropToolBar, x, y)) {
newType = CSMove;
} else if (iarea->getObject() > -1 && editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(iarea->getObject(), cursorX, cursorY);
} else if (onArea (CropResize, x, y)) {
newType = CSResizeDiagonal;
} else if (tm == TMColorPicker && hoveredPicker) {
newType = CSMove;
} else if (tm == TMHand && (onArea (CropTopLeft, x, y))) {
newType = CSResizeTopLeft;
} else if (tm == TMHand && (onArea (CropTopRight, x, y))) {
newType = CSResizeTopRight;
} else if (tm == TMHand && (onArea (CropBottomLeft, x, y))) {
newType = CSResizeBottomLeft;
} else if (tm == TMHand && (onArea (CropBottomRight, x, y))) {
newType = CSResizeBottomRight;
} else if (tm == TMHand && (onArea (CropTop, x, y) || onArea (CropBottom, x, y))) {
newType = CSResizeHeight;
} else if (tm == TMHand && (onArea (CropLeft, x, y) || onArea (CropRight, x, y))) {
newType = CSResizeWidth;
} else if (onArea (CropImage, x, y)) {
int objectID = -1;
if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
Coord cropPos;
screenCoordToCropCanvas (iarea->posScreen.x, iarea->posScreen.y, cropPos.x, cropPos.y);
objectID = ObjectMOBuffer::getObjectID(cropPos);
}
if (objectID > -1) {
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else if (tm == TMHand) {
if (onArea (CropObserved, x, y)) {
newType = CSMove;
} else {
newType = CSCrosshair;
}
} else if (tm == TMSpotWB) {
newType = CSSpotWB;
} else if (tm == TMCropSelect) {
newType = CSCropSelect;
} else if (tm == TMStraighten) {
newType = CSStraighten;
} else if (tm == TMColorPicker) {
newType = CSAddColPicker;
}
} else {
int objectID = -1;
if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
Coord cropPos;
screenCoordToCropCanvas (iarea->posScreen.x, iarea->posScreen.y, cropPos.x, cropPos.y);
objectID = ObjectMOBuffer::getObjectID(cropPos);
}
if (objectID > -1) {
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else {
newType = CSArrow;
}
}
} else if (state == SCropSelecting) {
newType = CSCropSelect;
} else if (state == SRotateSelecting) {
newType = CSStraighten;
} else if (state == SCropMove || state == SCropWinMove || state == SObservedMove) {
newType = CSMove;
} else if (state == SHandMove || state == SCropImgMove) {
newType = CSHandClosed;
} else if (state == SResizeW1 || state == SResizeW2) {
newType = CSResizeWidth;
} else if (state == SResizeH1 || state == SResizeH2) {
newType = CSResizeHeight;
} else if (state == SResizeTL) {
newType = CSResizeTopLeft;
} else if (state == SResizeTR) {
newType = CSResizeTopRight;
} else if (state == SResizeBL) {
newType = CSResizeBottomLeft;
} else if (state == SResizeBR) {
newType = CSResizeBottomRight;
} else if (state == SCropWinResize) {
newType = CSResizeDiagonal;
} else if (state == SDragPicker) {
newType = CSMove2D;
} else if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS) {
int objectID = iarea->getObject();
if (objectID > -1) {
int cursorX;
int cursorY;
screenCoordToImage (x, y, cursorX, cursorY);
newType = editSubscriber->getCursor(objectID, cursorX, cursorY);
} else {
newType = CSArrow;
}
}
if (newType != cursor_type) {
cursor_type = newType;
CursorManager::setWidgetCursor(iarea->get_window(), cursor_type);
}
}
void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr)
{
MyMutex::MyLock lock(cropHandler.cimg);
bool isPreviewImg = false;
if (decorated) {
drawDecoration (cr);
}
int x = xpos, y = ypos;
// draw the background
backColor = iarea->previewModePanel->GetbackColor();
Glib::RefPtr<Gtk::StyleContext> style = iarea->get_style_context();
options.bgcolor = backColor;
if (backColor == 0) {
style->render_background(cr, x + imgAreaX, y + imgAreaY, imgAreaW, imgAreaH);
} else {
if (backColor == 1) {
cr->set_source_rgb (0, 0, 0);
} else if (backColor == 2) {
cr->set_source_rgb (1, 1, 1);
} else if (backColor == 3) {
cr->set_source_rgb (0.467, 0.467, 0.467);
}
cr->set_line_width (0.);
cr->rectangle (x + imgAreaX, y + imgAreaY, imgAreaW, imgAreaH);
cr->stroke_preserve ();
cr->fill ();
}
// draw image
if (state == SCropImgMove || state == SCropWinResize) {
// draw a rough image
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
Glib::RefPtr<Gdk::Pixbuf> rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom);
if (rough) {
int posX = x + imgAreaX + imgX;
int posY = y + imgAreaY + imgY;
Gdk::Cairo::set_source_pixbuf(cr, rough, posX, posY);
cr->rectangle(posX, posY, rtengine::min (rough->get_width (), imgAreaW-imgX), rtengine::min (rough->get_height (), imgAreaH-imgY));
cr->fill();
// if (cropHandler.cropParams->enabled)
// drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams);
}
if (observedCropWin) {
drawObservedFrame (cr);
}
} else {
CropParams cropParams = *cropHandler.cropParams;
if (state == SNormal) {
switch (options.cropGuides) {
case Options::CROP_GUIDE_NONE:
cropParams.guide = procparams::CropParams::Guide::NONE;
break;
case Options::CROP_GUIDE_FRAME:
cropParams.guide = procparams::CropParams::Guide::FRAME;
break;
default:
break;
}
}
bool useBgColor = (state == SNormal || state == SDragPicker || state == SDeletePicker || state == SEditDrag1);
if (cropHandler.cropPixbuf) {
imgW = cropHandler.cropPixbuf->get_width ();
imgH = cropHandler.cropPixbuf->get_height ();
exposeVersion++;
const bool showR = iarea->previewModePanel->showR(); // will show clipping if R channel is clipped
const bool showG = iarea->previewModePanel->showG(); // will show clipping if G channel is clipped
const bool showB = iarea->previewModePanel->showB(); // will show clipping if B channel is clipped
const bool showL = iarea->previewModePanel->showL(); // will show clipping if L value is clipped
const bool showFocusMask = iarea->indClippedPanel->showFocusMask();
bool showcs = iarea->indClippedPanel->showClippedShadows();
bool showch = iarea->indClippedPanel->showClippedHighlights();
// While the Right-side ALT is pressed, auto-enable highlight and shadow clipping indicators
// TODO: Add linux/MacOS specific functions for alternative
#ifdef WIN32
if (GetKeyState(VK_RMENU) < 0) {
showcs = true;
showch = true;
}
#endif
if (showcs || showch || showR || showG || showB || showL || showFocusMask) {
Glib::RefPtr<Gdk::Pixbuf> tmp = cropHandler.cropPixbuf->copy ();
guint8* pix = tmp->get_pixels();
guint8* pixWrkSpace = cropHandler.cropPixbuftrue->get_pixels();
const int pixRowStride = tmp->get_rowstride ();
const int pixWSRowStride = cropHandler.cropPixbuftrue->get_rowstride ();
const int bHeight = tmp->get_height();
const int bWidth = tmp->get_width();
if (showFocusMask) { // modulate preview to display focus mask
const int blur_radius2 = 1; // radius of small kernel. 1 => 3x3 kernel
const int blur_dim2 = 2 * blur_radius2 + 1; // dimension of small kernel
const int blur_radius = (blur_dim2 * blur_dim2) / 2; // radius of big kernel
const float kernel_size = SQR(2.f * blur_radius + 1.f); // count of pixels in the big blur kernel
const float rkernel_size = 1.0f / kernel_size; // reciprocal of kernel_size to avoid divisions
const float kernel_size2 = SQR(2.f * blur_radius2 + 1.f); // count of pixels in the small blur kernel
const float rkernel_size2 = 1.0f / kernel_size2; // reciprocal of kernel_size to avoid divisions
// allocate buffer for precalculated Luminance
float* tmpL = (float*)malloc(bHeight * bWidth * sizeof(float) );
// allocate buffers for sums and sums of squares of small kernel
float* tmpLsum = (float*)malloc((bHeight) * (bWidth) * sizeof(float) );
float* tmpLsumSq = (float*)malloc((bHeight) * (bWidth) * sizeof(float) );
float* tmpstdDev2 = (float*)malloc((bHeight) * (bWidth) * sizeof(float) );
float maxstdDev_L2 = 0.f;
#ifdef _OPENMP
#pragma omp parallel
#endif
{
#ifdef _OPENMP
#pragma omp for
#endif
// precalculate Luminance
for(int i = 0; i < bHeight; i++) {
guint8* currWS = pixWrkSpace + i * pixWSRowStride;
float* currL = tmpL + i * bWidth;
for(int j = 0; j < bWidth; j++) {
*currL = 0.299f * (currWS)[0] + 0.587f * (currWS)[1] + 0.114f * (currWS)[2];
currL++;
currWS += 3;
}
}
float maxthrstdDev_L2 = 0.f;
#ifdef _OPENMP
#pragma omp for nowait
#endif
// precalculate sum and sum of squares of small kernel
for(int i = blur_radius2; i < bHeight - blur_radius2; i++) {
for(int j = blur_radius2; j < bWidth - blur_radius2; j++) {
float sumL = 0.f;
float sumLSqu = 0.f;
for(int kh = -blur_radius2; kh <= blur_radius2; kh++) {
for(int kw = -blur_radius2; kw <= blur_radius2; kw++) {
float curL = tmpL[(i + kh) * bWidth + j + kw];
sumL += curL;
sumLSqu += SQR(curL);
}
}
tmpLsum[i * bWidth + j] = sumL;
tmpLsumSq[i * bWidth + j] = sumLSqu;
float stdDev_L2 = rkernel_size2 * sqrtf(sumLSqu * kernel_size2 - sumL * sumL);
if(stdDev_L2 > maxthrstdDev_L2) {
maxthrstdDev_L2 = stdDev_L2;
}
tmpstdDev2[i * bWidth + j] = stdDev_L2;
}
}
#ifdef _OPENMP
#pragma omp critical
#endif
{
if(maxthrstdDev_L2 > maxstdDev_L2) {
maxstdDev_L2 = maxthrstdDev_L2;
}
}
}
const float focus_thresh = 80.f;
maxstdDev_L2 = std::min(maxstdDev_L2, focus_thresh);
const float focus_threshby10 = focus_thresh / 10.f;
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic,16)
#endif
for (int i = blur_radius + 1; i < bHeight - blur_radius; i++) {
guint8* curr = pix + i * pixRowStride + 3 * (blur_radius + 1);
guint8* currWs = pixWrkSpace + i * pixWSRowStride + 3 * (blur_radius + 1);
for (int j = blur_radius + 1; j < bWidth - blur_radius; j++) {
//*************
// 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
//float focus_thresh2;
//float opacity = 0.9;//TODO: implement opacity
//TODO: evaluate effects of altering sampling frequency
//TODO: dynamically determine appropriate values based on image analysis
// calculate average in +-blur_radius pixels area around the current pixel
// speed up: calculate sum of squares in the same loops
float sum_L = 0.f;
float sumsq_L = 0.f;
// use precalculated values of small kernel to reduce number of iterations
for (int kh = -blur_radius + blur_radius2; kh <= blur_radius - blur_radius2; kh += blur_dim2) {
float* currLsum = &tmpLsum[(i + kh) * bWidth + j - blur_radius + 1];
float* currLsumSqu = &tmpLsumSq[(i + kh) * bWidth + j - blur_radius + 1];
for (int k = -blur_radius + blur_radius2; k <= blur_radius - blur_radius2; k += blur_dim2, currLsum += blur_dim2, currLsumSqu += blur_dim2) {
sum_L += *currLsum;
sumsq_L += *currLsumSqu;
}
}
//float sum_L2 = tmpLsum[i * bWidth + j];
//float sumsq_L2 = tmpLsumSq[i * bWidth + j];
//*************
// averages
// Optimized formulas to avoid divisions
float stdDev_L = rkernel_size * sqrtf(sumsq_L * kernel_size - sum_L * sum_L);
float stdDev_L2 = tmpstdDev2[i * bWidth + j];
// float stdDev_L2 = rkernel_size2 * sqrtf(sumsq_L2 * kernel_size2 - sum_L2 * sum_L2);
//TODO: try to normalize by average L of the entire (preview) image
//detection method 1: detect focus in features
//there is no strict condition between stdDev_L and stdDev_L2 themselves
/* if (stdDev_L2>focus_thresh2
&& (stdDev_L <focus_thresh)){ // this excludes false positives due to high contrast edges
curr[1]=255;
curr[0]=0;
curr[2]=0;
}*/
//detection method 2: detect focus in texture
// key point is std deviation on lower scale is higher than for the larger scale
// plus some boundary conditions
if (focus_thresh >= stdDev_L2 //TODO: could vary this to bypass noise better
&& stdDev_L2 > stdDev_L //this is the key to select fine detail within lower contrast on larger scale
&& stdDev_L > focus_threshby10 //options.highlightThreshold
) {
// transparency depends on sdtDev_L2 and maxstdDev_L2
float transparency = 1.f - std::min(stdDev_L2 / maxstdDev_L2, 1.0f) ;
// first row of circle
guint8* currtmp = &curr[0] + (-3 * pixRowStride);
guint8* currtmpWS = &currWs[0] + (-3 * pixWSRowStride);
for(int jj = -3; jj <= 3; jj += 3) {
guint8* currtmpl = currtmp + jj;
guint8* currtmpWSl = currtmpWS + jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f - transparency) * 255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
// second row of circle
currtmp = &curr[0] + (-2 * pixRowStride);
currtmpWS = &currWs[0] + (-2 * pixWSRowStride);
for(int jj = -6; jj <= 6; jj += 3) {
guint8* currtmpl = currtmp + jj;
guint8* currtmpWSl = currtmpWS + jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f - transparency) * 255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
// three middle row of circle
for(int ii = -1; ii <= 1; ii++) {
currtmp = &curr[0] + (ii * pixRowStride);
currtmpWS = &currWs[0] + (ii * pixWSRowStride);
for(int jj = -9; jj <= 9; jj += 3) {
guint8* currtmpl = currtmp + jj;
guint8* currtmpWSl = currtmpWS + jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f - transparency) * 255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
}
// second last row of circle
currtmp = &curr[0] + (2 * pixRowStride);
currtmpWS = &currWs[0] + (2 * pixWSRowStride);
for(int jj = -6; jj <= 6; jj += 3) {
guint8* currtmpl = currtmp + jj;
guint8* currtmpWSl = currtmpWS + jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f - transparency) * 255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
// last row of circle
currtmp = &curr[0] + (3 * pixRowStride);
currtmpWS = &currWs[0] + (3 * pixWSRowStride);
for(int jj = -3; jj <= 3; jj += 3) {
guint8* currtmpl = currtmp + jj;
guint8* currtmpWSl = currtmpWS + jj;
//transparent green
currtmpl[0] = transparency * currtmpWSl[0];
currtmpl[1] = transparency * currtmpWSl[1] + (1.f - transparency) * 255.f;
currtmpl[2] = transparency * currtmpWSl[2];
}
}
curr += 3;
currWs += 3;
}
}
free(tmpL);
free(tmpLsum);
free(tmpLsumSq);
free(tmpstdDev2);
} else { // !showFocusMask
const int hlThreshold = options.highlightThreshold;
const int shThreshold = options.shadowThreshold;
const float ShawdowFac = 64.f / (options.shadowThreshold + 1);
const float HighlightFac = 64.f / (256 - options.highlightThreshold);
const bool showclippedAny = (!showR && !showG && !showB && !showL); // will show clipping if any (all) of RGB channels is (shadow) clipped
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic,16)
#endif
for (int i = 0; i < bHeight; i++) {
guint8* curr = pix + i * pixRowStride;
guint8* currWS = pixWrkSpace + i * pixWSRowStride;
for (int j = 0; j < bWidth; j++) {
// we must compare clippings in working space, since the cropPixbuf is in sRGB, with mon profile
bool changedHL = false;
bool changedSH = false;
int delta = 0;
// for efficiency, pre-calculate currWS_L as it may be needed in both
// if (showch) and if (showcs) branches
int currWS_L = 0;
if (showL && (showch || showcs)) {
currWS_L = (int)(0.299f * currWS[0] + 0.587f * currWS[1] + 0.114f * currWS[2]);
}
if (showch) {
if ((showclippedAny || showR) && currWS[0] >= hlThreshold ) {
delta += 255 - currWS[0];
changedHL = true;
}
if ((showclippedAny || showG) && currWS[1] >= hlThreshold ) {
delta += 255 - currWS[1];
changedHL = true;
}
if ((showclippedAny || showB) && currWS[2] >= hlThreshold ) {
delta += 255 - currWS[2];
changedHL = true;
}
if (showL && currWS_L >= hlThreshold ) {
delta += 255 - currWS_L ;
changedHL = true;
}
if (changedHL) {
delta *= HighlightFac;
if (showclippedAny) {
curr[0] = curr[1] = curr[2] = delta; // indicate clipped highlights in gray
} else {
curr[0] = 255; // indicate clipped highlights in red
curr[1] = curr[2] = delta;
}
}
}
if (showcs) {
bool scR = currWS[0] <= shThreshold;
bool scG = currWS[1] <= shThreshold;
bool scB = currWS[2] <= shThreshold;
if (((showclippedAny && (scG && scB)) || showR) && scR ) {
delta += currWS[0];
changedSH = true;
}
if (((showclippedAny && (scR && scB)) || showG) && scG ) {
delta += currWS[1];
changedSH = true;
}
if (((showclippedAny && (scR && scG)) || showB) && scB ) {
delta += currWS[2];
changedSH = true;
}
if (showL && currWS_L <= shThreshold ) {
delta += currWS_L ;
changedSH = true;
}
if (changedSH) {
if (showclippedAny) {
delta = 255 - (delta * ShawdowFac);
curr[0] = curr[1] = curr[2] = delta; // indicate clipped shadows in gray
} else {
delta *= ShawdowFac;
curr[2] = 255;
curr[0] = curr[1] = delta; // indicate clipped shadows in blue
}
}
} //if (showcs)
// modulate the preview of channels & L;
if (!changedHL && !changedSH && !showclippedAny) { //This condition allows clipping indicators for RGB channels to remain in color
if (showR) {
curr[1] = curr[2] = curr[0]; //Red channel in grayscale
}
if (showG) {
curr[0] = curr[2] = curr[1]; //Green channel in grayscale
}
if (showB) {
curr[0] = curr[1] = curr[2]; //Blue channel in grayscale
}
if (showL) { //Luminosity
// see http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness for more info
//int L = (int)(0.212671*curr[0]+0.715160*curr[1]+0.072169*curr[2]);
int L = (int)(0.299 * curr[0] + 0.587 * curr[1] + 0.114 * curr[2]); //Lightness - this matches Luminosity mode in Photoshop CS5
curr[0] = curr[1] = curr[2] = L;
}
}
/*
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;
}
}
}
int posX = x + imgAreaX + imgX;
int posY = y + imgAreaY + imgY;
Gdk::Cairo::set_source_pixbuf(cr, tmp, posX, posY);
cr->rectangle(posX, posY, rtengine::min (tmp->get_width (), imgAreaW-imgX), rtengine::min (tmp->get_height (), imgAreaH-imgY));
cr->fill();
} else {
int posX = x + imgAreaX + imgX;
int posY = y + imgAreaY + imgY;
Gdk::Cairo::set_source_pixbuf(cr, cropHandler.cropPixbuf, posX, posY);
cr->rectangle(posX, posY, rtengine::min (cropHandler.cropPixbuf->get_width (), imgAreaW-imgX), rtengine::min (cropHandler.cropPixbuf->get_height (), imgAreaH-imgY));
cr->fill();
}
if (cropHandler.cropParams->enabled) {
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
drawCrop (cr, x + imgAreaX + imgX, y + imgAreaY + imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropParams, (this == iarea->mainCropWindow), useBgColor, cropHandler.isFullDisplay ());
}
if (observedCropWin) {
drawObservedFrame (cr);
}
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
if (editSubscriber && editSubscriber->getEditingType() == ET_OBJECTS && bufferCreated()) {
cr->set_line_width (0.);
cr->rectangle (x + imgAreaX, y + imgAreaY, imgAreaW, imgAreaH);
cr->clip();
// drawing Subscriber's visible geometry
const std::vector<Geometry*> 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 (auto geom : visibleGeom) {
geom->drawOuterGeometry(cr, this, *this);
}
// drawing inner lines
for (auto geom : visibleGeom) {
geom->drawInnerGeometry(cr, this, *this);
}
// drawing to the "mouse over" channel
const auto mouseOverGeom = editSubscriber->getMouseOverGeometry();
if (mouseOverGeom.size()) {
if (mouseOverGeom.size() > 65534) {
// once it has been switched to OM_65535, it won't return back to OM_255
// to avoid constant memory allocations in some particular situation.
// It will return to OM_255 on a new editing session
setObjectMode(OM_65535);
}
Cairo::RefPtr<Cairo::Context> crMO = Cairo::Context::create(ObjectMOBuffer::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., ObjectMOBuffer::getObjectMap()->get_width(), ObjectMOBuffer::getObjectMap()->get_height());
crMO->set_line_width(0.);
crMO->fill();
int a=0;
for (auto moGeom : mouseOverGeom) {
moGeom->drawToMOChannel(crMO, a, this, *this);
++a;
}
}
if (this != iarea->mainCropWindow) {
cr->reset_clip();
}
}
isPreviewImg = true;
} else {
// cropHandler.cropPixbuf is null
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
Glib::RefPtr<Gdk::Pixbuf> rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom);
if (rough) {
int posX = x + imgAreaX + imgX;
int posY = y + imgAreaY + imgY;
Gdk::Cairo::set_source_pixbuf(cr, rough, posX, posY);
cr->rectangle(posX, posY, rtengine::min (rough->get_width (), imgAreaW-imgX), rtengine::min (rough->get_height (), imgAreaH-imgY));
cr->fill();
if (cropHandler.cropParams->enabled) {
drawCrop (cr, x + imgAreaX + imgX, y + imgAreaY + imgY, rough->get_width(), rough->get_height(), cropX, cropY, zoomSteps[cropZoom].zoom, cropParams, (this == iarea->mainCropWindow), useBgColor, cropHandler.isFullDisplay ());
}
if (observedCropWin) {
drawObservedFrame (cr);
}
}
}
}
if (state == SRotateSelecting) {
drawStraightenGuide (cr);
}
if (state == SNormal && isFlawnOver) {
EditSubscriber *editSubscriber = iarea->getCurrSubscriber();
if (iarea->getToolMode () == TMHand && editSubscriber && editSubscriber->getEditingType() == ET_PIPETTE && iarea->getObject()) {
drawUnscaledSpotRectangle (cr, iarea->getPipetteRectSize ());
} else if (iarea->getToolMode () == TMSpotWB) {
drawScaledSpotRectangle (cr, iarea->getSpotWBRectSize ());
}
}
style->render_frame (cr, x + imgAreaX, y + imgAreaY, imgAreaW, imgAreaH);
if ((state == SNormal || state == SDragPicker) && isPreviewImg && iarea->showColorPickers()) {
for (auto colorPicker : colorPickers) {
colorPicker->draw(cr);
}
}
//t2.set ();
// printf ("etime --> %d, %d\n", t2.etime (t1), t4.etime (t3));
}
void CropWindow::setEditSubscriber (EditSubscriber* newSubscriber) {
// Delete, create, update all buffers based upon newSubscriber's type
if (newSubscriber) {
ObjectMOBuffer::resize (imgAreaW, imgAreaH);
} else {
ObjectMOBuffer::flush ();
}
cropHandler.setEditSubscriber(newSubscriber);
}
// zoom* is called from the zoomPanel or the scroll wheel in the preview area
void CropWindow::zoomIn (bool toCursor, int cursorX, int cursorY)
{
int x = -1;
int y = -1;
if (toCursor) {
x = cursorX;
y = cursorY;
} else {
if (zoomSteps[cropZoom].zoom <= cropHandler.getFitZoom()) {
if (cropHandler.cropParams->enabled) {
x = cropHandler.cropParams->x + cropHandler.cropParams->w / 2;
y = cropHandler.cropParams->y + cropHandler.cropParams->h / 2;
} else {
int fw, fh;
cropHandler.getFullImageSize(fw, fh);
x = fw / 2;
y = fh / 2;
}
zoomVersion = exposeVersion;
} else if (zoomVersion != exposeVersion) {
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;
int y1 = cropHandler.cropParams->y + cropHandler.cropParams->h / 2;
double cropd = sqrt(cropHandler.cropParams->h * cropHandler.cropParams->h + cropHandler.cropParams->w * cropHandler.cropParams->w) * zoomSteps[cropZoom].zoom;
double imd = sqrt(imgW * imgW + imgH * imgH);
double d;
// the more we can see of the crop, the more gravity towards crop center
if (cropd > imd) {
d = 0.8;
} else if (cropd < imd * 0.5) {
d = 0.0;
} else {
d = 1.6 * (cropd - imd * 0.5) / imd;
}
x = d * x + (1.0 - d) * x1;
y = d * y + (1.0 - d) * y1;
}
zoomVersion = exposeVersion;
}
}
int z = cropZoom + 1;
while (z < int(zoomSteps.size()) && !zoomSteps[z].is_major) {
++z;
}
changeZoom (z, true, x, y);
fitZoom = false;
}
void CropWindow::zoomOut (bool toCursor, int cursorX, int cursorY)
{
int x = -1;
int y = -1;
if (toCursor) {
x = cursorX;
y = cursorY;
} else {
screenCoordToImage(xpos + imgX + imgW / 2, ypos + imgY + imgH / 2, x, y);
}
zoomVersion = exposeVersion;
int z = cropZoom - 1;
while (z >= 0 && !zoomSteps[z].is_major) {
--z;
}
changeZoom (z, true, x, y);
fitZoom = false;
}
void CropWindow::zoom11 (bool notify)
{
int x = -1;
int y = -1;
if (zoomSteps[cropZoom].zoom <= cropHandler.getFitZoom()) {
if (cropHandler.cropParams->enabled) {
x = cropHandler.cropParams->x + cropHandler.cropParams->w / 2;
y = cropHandler.cropParams->y + cropHandler.cropParams->h / 2;
} else {
int fw, fh;
cropHandler.getFullImageSize(fw, fh);
x = fw / 2;
y = fh / 2;
}
zoomVersion = exposeVersion;
} else {
screenCoordToImage(xpos + imgX + imgW / 2, ypos + imgY + imgH / 2, x, y);
}
changeZoom (zoom11index, notify, x, y, notify);
fitZoom = false;
}
double CropWindow::getZoom ()
{
return zoomSteps[cropZoom].zoom;
}
bool CropWindow::isMinZoom ()
{
return cropZoom <= 0;
}
bool CropWindow::isMaxZoom ()
{
return cropZoom >= int(zoomSteps.size())-1;
}
void CropWindow::setZoom (double zoom)
{
int cz = int(zoomSteps.size())-1;
if (zoom < zoomSteps[0].zoom) {
cz = 0;
} else
for (int i = 0; i < int(zoomSteps.size())-1; i++)
if (zoomSteps[i].zoom <= zoom && zoomSteps[i + 1].zoom > zoom) {
cz = i;
break;
}
changeZoom (cz, false);
}
double CropWindow::getZoomFitVal ()
{
double z = cropHandler.getFitZoom ();
int cz = int(zoomSteps.size())-1;
if (z < zoomSteps[0].zoom) {
cz = 0;
} else
for (int i = 0; i < int(zoomSteps.size())-1; i++)
if (zoomSteps[i].zoom <= z && zoomSteps[i + 1].zoom > z) {
cz = i;
break;
}
return zoomSteps[cz].zoom;
}
void CropWindow::zoomFit ()
{
double z = cropHandler.getFitZoom ();
int cz = int(zoomSteps.size())-1;
if (z < zoomSteps[0].zoom) {
cz = 0;
} else
for (int i = 0; i < int(zoomSteps.size())-1; i++)
if (zoomSteps[i].zoom <= z && zoomSteps[i + 1].zoom > z) {
cz = i;
break;
}
zoomVersion = exposeVersion;
changeZoom (cz, true, -1, -1);
fitZoom = true;
}
void CropWindow::zoomFitCrop ()
{
if(cropHandler.cropParams->enabled) {
double z = cropHandler.getFitCropZoom ();
int cz = int(zoomSteps.size())-1;
if (z < zoomSteps[0].zoom) {
cz = 0;
} else
for (int i = 0; i < int(zoomSteps.size())-1; i++)
if (zoomSteps[i].zoom <= z && zoomSteps[i + 1].zoom > z) {
cz = i;
break;
}
zoomVersion = exposeVersion;
int centerX, centerY;
centerX = cropHandler.cropParams->x + cropHandler.cropParams->w / 2;
centerY = cropHandler.cropParams->y + cropHandler.cropParams->h / 2;
setCropAnchorPosition(centerX, centerY);
changeZoom (cz, true, centerX, centerY);
fitZoom = options.cropAutoFit;
} else {
zoomFit();
}
}
void CropWindow::buttonPressed (LWButton* button, int actionCode, void* actionData)
{
if (button == bZoomIn) { // zoom in
zoomIn ();
} else if (button == bZoomOut) { // zoom out
zoomOut ();
} else if (button == bZoom100) { // zoom 100
zoom11 ();
} else if (button == bClose) { // close
if(iarea->getImProcCoordinator()->updateTryLock()) {
deleted = true;
iarea->cropWindowClosed (this);
iarea->getImProcCoordinator()->updateUnLock();
}
}
}
void CropWindow::redrawNeeded (LWButton* button)
{
iarea->redraw ();
}
void CropWindow::updateHoveredPicker (rtengine::Coord *imgPos)
{
if (!hoveredPicker) {
return;
}
rtengine::Coord cropPos;
if (imgPos) {
imageCoordToCropImage(imgPos->x, imgPos->y, cropPos.x, cropPos.y);
hoveredPicker->setPosition (*imgPos);
} else {
rtengine::Coord imgPos2;
hoveredPicker->getImagePosition(imgPos2);
imageCoordToCropImage(imgPos2.x, imgPos2.y, cropPos.x, cropPos.y);
}
LockableColorPicker::Validity validity = checkValidity (hoveredPicker, cropPos);
hoveredPicker->setValidity (validity);
{
MyMutex::MyLock lock(cropHandler.cimg);
if (validity == LockableColorPicker::Validity::INSIDE) {
float r=0.f, g=0.f, b=0.f;
float rpreview=0.f, gpreview=0.f, bpreview=0.f;
cropHandler.colorPick(cropPos, r, g, b, rpreview, gpreview, bpreview, hoveredPicker->getSize());
hoveredPicker->setRGB (r, g, b, rpreview, gpreview, bpreview);
}
}
}
void CropWindow::changeZoom (int zoom, bool notify, int centerx, int centery, bool needsRedraw)
{
if (zoom < 0) {
zoom = 0;
} else if (zoom > int(zoomSteps.size())-1) {
zoom = int(zoomSteps.size())-1;
}
cropZoom = zoom;
cropLabel = zoomSteps[cropZoom].label;
cropHandler.setZoom (zoomSteps[cropZoom].czoom, centerx, centery);
if (notify)
for (auto listener : listeners) {
listener->cropZoomChanged (this);
}
if (needsRedraw)
iarea->redraw ();
}
LockableColorPicker::Validity CropWindow::checkValidity (LockableColorPicker* picker, const rtengine::Coord &pos)
{
if (!cropHandler.cropPixbuftrue) {
return LockableColorPicker::Validity::OUTSIDE;
}
rtengine::Coord cropTopLeft, cropBottomRight, cropSize;
int skip;
cropHandler.getWindow(cropTopLeft.x, cropTopLeft.y, cropSize.x, cropSize.y, skip);
cropBottomRight = cropTopLeft + cropSize;
rtengine::Coord pickerPos, cropPickerPos;
picker->getImagePosition(pickerPos);
rtengine::Coord minPos(0, 0);
rtengine::Coord maxPos(cropHandler.cropPixbuftrue->get_width(), cropHandler.cropPixbuftrue->get_height());
rtengine::Coord halfPickerSize((int)picker->getSize()/2, (int)picker->getSize()/2);
imageCoordToCropImage (pickerPos.x, pickerPos.y, cropPickerPos.x, cropPickerPos.y);
imageCoordToCropImage (cropTopLeft.x, cropTopLeft.y, minPos.x, minPos.y);
imageCoordToCropImage (cropBottomRight.x, cropBottomRight.y, maxPos.x, maxPos.y);
rtengine::Coord pickerMinPos = cropPickerPos - halfPickerSize;
rtengine::Coord pickerMaxPos = cropPickerPos + halfPickerSize;
if (pickerMaxPos.x < minPos.x || pickerMaxPos.y < minPos.y || pickerMinPos.x > maxPos.x || pickerMinPos.y > maxPos.y) {
return LockableColorPicker::Validity::OUTSIDE;
} else if (pickerMinPos >= minPos && pickerMaxPos < maxPos) {
return LockableColorPicker::Validity::INSIDE;
} else {
return LockableColorPicker::Validity::CROSSING;
}
}
void CropWindow::deleteColorPickers ()
{
for (auto colorPicker : colorPickers) {
delete colorPicker;
}
colorPickers.clear();
}
void CropWindow::screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy)
{
rtengine::Crop* crop = static_cast<rtengine::Crop*>(cropHandler.getCrop());
cropx = phyx - xpos - imgX - imgAreaX;
cropy = phyy - ypos - imgY - imgAreaY;
if (zoomSteps[cropZoom].zoom > 1.) {
cropx = int(double(cropx) / zoomSteps[cropZoom].zoom);
cropy = int(double(cropy) / zoomSteps[cropZoom].zoom);
} else {
float czoom = float((zoomSteps[cropZoom].czoom/10) * 10) / float(zoomSteps[cropZoom].czoom);
cropx = cropx / czoom;
cropy = cropy / czoom;
}
cropx += crop->getLeftBorder();
cropy += crop->getUpperBorder();
}
void CropWindow::screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy)
{
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
imgx = cropX + (phyx - xpos - imgX - imgAreaX) / zoomSteps[cropZoom].zoom;
imgy = cropY + (phyy - ypos - imgY - imgAreaY) / zoomSteps[cropZoom].zoom;
}
void CropWindow::screenCoordToCropCanvas (int phyx, int phyy, int& prevx, int& prevy)
{
prevx = phyx - xpos - imgAreaX;
prevy = phyy - ypos - imgAreaY;
}
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 + imgAreaX;
phyy = (imgy - cropY) * zoomSteps[cropZoom].zoom + ypos + imgY + imgAreaY;
}
void CropWindow::imageCoordToCropCanvas (int imgx, int imgy, int& phyx, int& phyy)
{
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
phyx = (imgx - cropX) * zoomSteps[cropZoom].zoom + imgX;
phyy = (imgy - cropY) * zoomSteps[cropZoom].zoom + imgY;
}
void CropWindow::imageCoordToCropBuffer (int imgx, int imgy, int& phyx, int& phyy)
{
int cropX, cropY;
rtengine::Crop* crop = static_cast<rtengine::Crop*>(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();
}
void CropWindow::imageCoordToCropImage (int imgx, int imgy, int& phyx, int& phyy)
{
int cropX, cropY;
cropHandler.getPosition (cropX, cropY);
phyx = (imgx - cropX) * zoomSteps[cropZoom].zoom;
phyy = (imgy - cropY) * zoomSteps[cropZoom].zoom;
}
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::scaleValueToCanvas (int value)
{
return int(double(value) * zoomSteps[cropZoom].zoom);
}
float CropWindow::scaleValueToCanvas (float value)
{
return float(double(value) * zoomSteps[cropZoom].zoom);
}
double CropWindow::scaleValueToCanvas (double value)
{
return value * zoomSteps[cropZoom].zoom;
}
void CropWindow::drawDecoration (Cairo::RefPtr<Cairo::Context> cr)
{
int x = xpos, y = ypos;
// prepare label
Glib::RefPtr<Pango::Context> context = iarea->get_pango_context () ;
Pango::FontDescription fontd = context->get_font_description ();
fontd.set_weight (Pango::WEIGHT_BOLD);
fontd.set_size(8 * Pango::SCALE);
context->set_font_description (fontd);
Glib::RefPtr<Pango::Layout> cllayout = iarea->create_pango_layout(cropLabel);
int iw, ih;
cllayout->get_pixel_size (iw, ih);
// draw decoration (border)
int h = height, w = width;
cr->set_source_rgb (0.1, 0.1, 0.1);
cr->set_line_width (1.0);
cr->move_to (x + 2.5, y + titleHeight + 2.5 );
cr->line_to (x + 2.5, y + h - 2.5);
cr->line_to (x + w - 2.5, y + h - 2.5);
cr->line_to (x + w - 2.5, y + titleHeight + 2.5 );
cr->set_source_rgba (0.0, 0.0, 0.0, 0.5);
cr->rectangle (x + 2.5, y + 0.5, w - 5, titleHeight + 2);
cr->stroke_preserve ();
cr->fill ();
// draw label
cr->set_source_rgba (1, 1, 1, 0.5);
cr->move_to (x + 10 + sideBorderWidth + bZoomIn->getIcon()->getWidth() + bZoomOut->getIcon()->getWidth() + bZoom100->getIcon()->getWidth(), y + 1 + upperBorderWidth + (titleHeight - ih) / 2);
cllayout->add_to_cairo_context (cr);
cr->fill ();
buttonSet.redraw (cr);
}
void CropWindow::drawStraightenGuide (Cairo::RefPtr<Cairo::Context> cr)
{
if (action_x != press_x || action_y != press_y) {
double arg = (press_x - action_x) / sqrt(double((press_x - action_x) * (press_x - action_x) + (press_y - action_y) * (press_y - action_y)));
double sol1, sol2;
double pi = rtengine::RT_PI;
if (press_y > action_y) {
sol1 = acos(arg) * 180 / pi;
sol2 = -acos(-arg) * 180 / pi;
} else {
sol1 = acos(-arg) * 180 / pi;
sol2 = -acos(arg) * 180 / pi;
}
if (fabs(sol1) < fabs(sol2)) {
rot_deg = sol1;
} else {
rot_deg = sol2;
}
if (rot_deg < -45) {
rot_deg = 90.0 + rot_deg;
} else if (rot_deg > 45) {
rot_deg = - 90.0 + rot_deg;
}
} else {
rot_deg = 0;
}
Glib::RefPtr<Pango::Context> context = iarea->get_pango_context () ;
Pango::FontDescription fontd = context->get_font_description ();
fontd.set_weight (Pango::WEIGHT_BOLD);
fontd.set_size (8 * Pango::SCALE);
context->set_font_description (fontd);
Glib::RefPtr<Pango::Layout> deglayout = iarea->create_pango_layout(Glib::ustring::compose ("%1 deg", Glib::ustring::format(std::setprecision(2), rot_deg)));
int x1 = press_x;
int y1 = press_y;
int y2 = action_y;
int x2 = action_x;
/* if (x1<0) x1 = 0;
if (y1<0) y1 = 0;
if (x2<0) x2 = 0;
if (y2<0) y2 = 0;
if (x2>=image->getWidth()) x2 = image->getWidth()-1;
if (y2>=image->getHeight()) y2 = image->getHeight()-1;
if (x1>=image->getWidth()) x1 = image->getWidth()-1;
if (y1>=image->getHeight()) y1 = image->getHeight()-1;
*/
cr->set_line_width (1);
cr->set_source_rgba (1.0, 1.0, 1.0, 0.618);
cr->move_to (x1 + 0.5, y1 + 0.5);
cr->line_to (x2 + 0.5, y2 + 0.5);
cr->stroke ();
cr->set_source_rgba (0.0, 0.0, 0.0, 0.618);
std::valarray<double> ds (1);
ds[0] = 4;
cr->set_dash (ds, 0);
cr->move_to (x1 + 0.5, y1 + 0.5);
cr->line_to (x2 + 0.5, y2 + 0.5);
cr->stroke ();
if (press_x != action_x && press_y != action_y) {
cr->set_source_rgb (0.0, 0.0, 0.0);
cr->move_to ((x1 + x2) / 2 + 1, (y1 + y2) / 2 + 1);
deglayout->add_to_cairo_context (cr);
cr->move_to ((x1 + x2) / 2 + 1, (y1 + y2) / 2 - 1);
deglayout->add_to_cairo_context (cr);
cr->move_to ((x1 + x2) / 2 - 1, (y1 + y2) / 2 + 1);
deglayout->add_to_cairo_context (cr);
cr->move_to ((x1 + x2) / 2 + 1, (y1 + y2) / 2 + 1);
deglayout->add_to_cairo_context (cr);
cr->fill ();
cr->set_source_rgb (1.0, 1.0, 1.0);
cr->move_to ((x1 + x2) / 2, (y1 + y2) / 2);
deglayout->add_to_cairo_context (cr);
cr->fill ();
}
}
void CropWindow::drawScaledSpotRectangle (Cairo::RefPtr<Cairo::Context> cr, int 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 + imgAreaX - 0.5, ypos + imgY + imgAreaY - 0.5, imgAreaW, imgAreaH);
cr->clip ();
cr->set_source_rgb (1.0, 1.0, 1.0);
cr->rectangle (x1 * zoomSteps[cropZoom].zoom - 1.5, y1 * zoomSteps[cropZoom].zoom - 1.5, x2 * zoomSteps[cropZoom].zoom - x1 * zoomSteps[cropZoom].zoom + 2, y2 * zoomSteps[cropZoom].zoom - y1 * zoomSteps[cropZoom].zoom + 2);
cr->stroke ();
cr->set_source_rgb (0.0, 0.0, 0.0);
cr->rectangle (x1 * zoomSteps[cropZoom].zoom - 0.5, y1 * zoomSteps[cropZoom].zoom - 0.5, x2 * zoomSteps[cropZoom].zoom - x1 * zoomSteps[cropZoom].zoom, y2 * zoomSteps[cropZoom].zoom - y1 * zoomSteps[cropZoom].zoom);
cr->stroke ();
cr->reset_clip ();
}
void CropWindow::drawUnscaledSpotRectangle (Cairo::RefPtr<Cairo::Context> 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 + imgAreaX - 0.5, ypos + imgY + imgAreaY - 0.5, imgAreaW, imgAreaH);
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 observedCropX, observedCropY, observedCropW, observedCropH;
observedCropWin->getCropRectangle (observedCropX, observedCropY, observedCropW, observedCropH);
int mainCropX, mainCropY, mainCropW, mainCropH;
getCropRectangle (mainCropX, mainCropY, mainCropW, mainCropH);
// translate it to screen coordinates
if (rw) { // rw and rh are the rough image's dimension
x = xpos + imgAreaX + (imgAreaW - rw) / 2 + (observedCropX - mainCropX) * zoomSteps[cropZoom].zoom;
y = ypos + imgAreaY + (imgAreaH - rh) / 2 + (observedCropY - mainCropY) * zoomSteps[cropZoom].zoom;
} else {
x = xpos + imgX + (observedCropX - mainCropX) * zoomSteps[cropZoom].zoom;
y = ypos + imgY + (observedCropY - mainCropY) * zoomSteps[cropZoom].zoom;
}
w = observedCropW * zoomSteps[cropZoom].zoom;
h = observedCropH * zoomSteps[cropZoom].zoom;
}
void CropWindow::drawObservedFrame (Cairo::RefPtr<Cairo::Context> cr, int rw, int rh)
{
int x, y, w, h;
getObservedFrameArea (x, y, w, h, rw, rh);
// draw a black "shadow" line
cr->set_source_rgba( 0, 0, 0, 0.65);
cr->set_line_width (1);
cr->rectangle (x - 0.5, y - 0.5, w + 4, h + 4);
cr->stroke ();
// draw a "frame" line. Color of frame line can be set in preferences
cr->set_source_rgba(options.navGuideBrush[0], options.navGuideBrush[1], options.navGuideBrush[2], options.navGuideBrush[3]); //( 1, 1, 1, 1.0);
cr->rectangle (x - 1.5, y - 1.5, w + 4, h + 4);
cr->stroke ();
}
void CropWindow::cropImageUpdated ()
{
MyMutex::MyLock lock(cropHandler.cimg);
for (auto colorPicker : colorPickers) {
Coord imgPos, cropPos;
colorPicker->getImagePosition(imgPos);
imageCoordToCropImage(imgPos.x, imgPos.y, cropPos.x, cropPos.y);
float r=0.f, g=0.f, b=0.f;
float rpreview=0.f, gpreview=0.f, bpreview=0.f;
colorPicker->setValidity (checkValidity (colorPicker, cropPos));
cropHandler.colorPick(cropPos, r, g, b, rpreview, gpreview, bpreview, colorPicker->getSize());
colorPicker->setRGB (r, g, b, rpreview, gpreview, bpreview);
}
iarea->redraw ();
}
void CropWindow::cropWindowChanged ()
{
if (!decorated) {
iarea->syncBeforeAfterViews ();
}
iarea->redraw ();
}
void CropWindow::initialImageArrived ()
{
for (auto listener : listeners) {
listener->initialImageArrived();
}
}
void CropWindow::setDisplayPosition (int x, int y) {
imgX = x;
imgY = y;
}
void CropWindow::remoteMove (int deltaX, int deltaY)
{
state = SCropImgMove;
cropHandler.moveAnchor(deltaX, deltaY, false);
for (auto listener : listeners) {
listener->cropPositionChanged (this);
}
}
void CropWindow::remoteMoveReady ()
{
cropHandler.update ();
state = SNormal;
for (auto listener : listeners) {
listener->cropPositionChanged (this);
}
}
void CropWindow::delCropWindowListener (CropWindowListener* l)
{
std::list<CropWindowListener*>::iterator i = listeners.begin();
while (i != listeners.end())
if (*i == l) {
i = listeners.erase (i);
} else {
++i;
}
}
ImageArea* CropWindow::getImageArea()
{
return iarea;
}
void CropWindow::setCropGUIListener (CropGUIListener* cgl)
{
cropgl = cgl;
}
void CropWindow::setPointerMotionListener (PointerMotionListener* pml)
{
pmlistener = pml;
if (pml) {
pml->signal_cycle_rgb().connect( sigc::mem_fun(*this, &CropWindow::cycleRGB) );
pml->signal_cycle_hsv().connect( sigc::mem_fun(*this, &CropWindow::cycleHSV) );
}
}
PointerMotionListener* CropWindow::getPointerMotionListener ()
{
return pmlistener;
}
void CropWindow::setPointerMotionHListener (PointerMotionListener* pml)
{
pmhlistener = pml;
}
// crop window listeners
void CropWindow::addCropWindowListener (CropWindowListener* l)
{
listeners.push_back (l);
}
void CropWindow::cycleRGB ()
{
bool redraw = false;
for (auto colorPicker : colorPickers) {
redraw |= colorPicker->cycleRGB ();
}
if (redraw) {
iarea->redraw ();
}
}
void CropWindow::cycleHSV ()
{
bool redraw = false;
for (auto colorPicker : colorPickers) {
redraw |= colorPicker->cycleHSV ();
}
if (redraw) {
iarea->redraw ();
}
}