diff --git a/rtengine/color.cc b/rtengine/color.cc index 58dc60320..a23a261b7 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -361,6 +361,153 @@ void Color::cleanup () } } +void Color::rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b, bool workingSpace) +{ + double xyz_rgb[3][3]; + const double ep = 216.0 / 24389.0; + const double ka = 24389.0 / 27.0; + + double var_R = r / 65535.0; + double var_G = g / 65535.0; + double var_B = b / 65535.0; + + Glib::ustring profileCalc; + profileCalc = "sRGB"; //default + + if (workingSpace) { + profileCalc = profileW; //display working + } + + else {// if you want display = output space + if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { + profileCalc = "sRGB"; + } + + if (profile == "ProPhoto" || profile == "RT_Large_gBT709" || profile == "RT_Large_g10" || profile == "RT_Large_gsRGB") { + profileCalc = "ProPhoto"; + } + + if (profile == "AdobeRGB1998" || profile == "RT_Medium_gsRGB") { + profileCalc = "Adobe RGB"; + } + + if (profile == "WideGamutRGB") { + profileCalc = "WideGamut"; + } + } + + if (workingSpace) {//display working + if (profileW == "sRGB") { //apply sRGB inverse gamma + + if ( var_R > 0.04045 ) { + var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + } else { + var_R = var_R / 12.92; + } + + if ( var_G > 0.04045 ) { + var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + } else { + var_G = var_G / 12.92; + } + + if ( var_B > 0.04045 ) { + var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + } else { + var_B = var_B / 12.92; + } + } else if (profileW == "ProPhoto") { // apply inverse gamma 1.8 + var_R = pow ( var_R, 1.8); + var_G = pow ( var_G, 1.8); + var_B = pow ( var_B, 1.8); + } else { // apply inverse gamma 2.2 + var_R = pow ( var_R, 2.2); + var_G = pow ( var_G, 2.2); + var_B = pow ( var_B, 2.2); + } + } else { //display outout profile + + if (profile == "RT_sRGB" || profile == "RT_Large_gsRGB" || profile == "RT_Medium_gsRGB") { //apply sRGB inverse gamma + if ( var_R > 0.04045 ) { + var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + } else { + var_R = var_R / 12.92; + } + + if ( var_G > 0.04045 ) { + var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + } else { + var_G = var_G / 12.92; + } + + if ( var_B > 0.04045 ) { + var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); + } else { + var_B = var_B / 12.92; + } + } + + else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { // + if ( var_R > 0.0795 ) { + var_R = pow ( ( ( var_R + 0.0954 ) / 1.0954 ), 2.2); + } else { + var_R = var_R / 4.5; + } + + if ( var_G > 0.0795 ) { + var_G = pow ( ( ( var_G + 0.0954 ) / 1.0954 ), 2.2); + } else { + var_G = var_G / 4.5; + } + + if ( var_B > 0.0795 ) { + var_B = pow ( ( ( var_B + 0.0954 ) / 1.0954 ), 2.2); + } else { + var_B = var_B / 4.5; + } + + } else if (profile == "ProPhoto") { // apply inverse gamma 1.8 + + var_R = pow ( var_R, 1.8); + var_G = pow ( var_G, 1.8); + var_B = pow ( var_B, 1.8); + } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { // apply inverse gamma 1.8 + + var_R = pow ( var_R, 1.); + var_G = pow ( var_G, 1.); + var_B = pow ( var_B, 1.); + } + + else {// apply inverse gamma 2.2 + var_R = pow ( var_R, 2.2); + var_G = pow ( var_G, 2.2); + var_B = pow ( var_B, 2.2); + } + } + + // TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileW); + + TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileCalc); + + for (int m = 0; m < 3; m++) + for (int n = 0; n < 3; n++) { + xyz_rgb[m][n] = wprof[m][n]; + } + + double varxx, varyy, varzz; + double var_X = ( xyz_rgb[0][0] * var_R + xyz_rgb[0][1] * var_G + xyz_rgb[0][2] * var_B ) / Color::D50x; + double var_Y = ( xyz_rgb[1][0] * var_R + xyz_rgb[1][1] * var_G + xyz_rgb[1][2] * var_B ) ; + double var_Z = ( xyz_rgb[2][0] * var_R + xyz_rgb[2][1] * var_G + xyz_rgb[2][2] * var_B ) / Color::D50z; + + varxx = var_X > ep ? cbrt(var_X) : ( ka * var_X + 16.0) / 116.0 ; + varyy = var_Y > ep ? cbrt(var_Y) : ( ka * var_Y + 16.0) / 116.0 ; + varzz = var_Z > ep ? cbrt(var_Z) : ( ka * var_Z + 16.0) / 116.0 ; + LAB_l = ( 116 * varyy ) - 16; + LAB_a = 500 * ( varxx - varyy ); + LAB_b = 200 * ( varyy - varzz ); + +} + void Color::rgb2hsl(float r, float g, float b, float &h, float &s, float &l) { diff --git a/rtengine/color.h b/rtengine/color.h index 350cd3b2a..508efeb31 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -175,6 +175,22 @@ public: } + /** + * @brief Convert red/green/blue to L*a*b + * @brief Convert red/green/blue to hue/saturation/luminance + * @param profile output profile name + * @param profileW working profile name + * @param r red channel [0 ; 65535] + * @param g green channel [0 ; 65535] + * @param b blue channel [0 ; 65535] + * @param L Lab L channel [0 ; 1] (return value) + * @param a Lab a channel [0 ; 1] (return value) + * @param b Lab b channel [0; 1] (return value) + * @param workingSpace true: compute the Lab value using the Working color space ; false: use the Output color space + */ + static void rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b, bool workingSpace); + + /** * @brief Convert red/green/blue to hue/saturation/luminance * @param r red channel [0 ; 65535] diff --git a/rtgui/crophandler.cc b/rtgui/crophandler.cc index d47ec91ff..522b34c41 100644 --- a/rtgui/crophandler.cc +++ b/rtgui/crophandler.cc @@ -475,12 +475,12 @@ bool CropHandler::getEnabled () return enabled; } -void CropHandler::colorPick (rtengine::Coord pickerPos, float &r, float &g, float &b, LockableColorPicker::PickerSize size) +void CropHandler::colorPick (rtengine::Coord pickerPos, float &r, float &g, float &b, LockableColorPicker::Size size) { int xSize = (int)size; int ySize = (int)size; - int pixbufW = cropPixbuf->get_width(); + int pixbufW = cropPixbuftrue->get_width(); rtengine::Coord topLeftPos(pickerPos.x - xSize/2, pickerPos.y - ySize/2); if (topLeftPos.x > pixbufW || topLeftPos.y > pixbufW || topLeftPos.x + xSize < 0 || topLeftPos.y + ySize < 0) { @@ -510,9 +510,9 @@ void CropHandler::colorPick (rtengine::Coord pickerPos, float &r, float &g, floa // Accumulating the data std::uint32_t r2=0, g2=0, b2=0; std::uint32_t count = 0; - const guint8* data = cropPixbuf->get_pixels(); + const guint8* data = cropPixbuftrue->get_pixels(); for (int j = topLeftPos.y ; j < topLeftPos.y + ySize ; ++j) { - const guint8* data2 = data + cropPixbuf->get_rowstride()*j; + const guint8* data2 = data + cropPixbuftrue->get_rowstride()*j; for (int i = topLeftPos.x ; i < topLeftPos.x + xSize ; ++i) { const guint8* data3 = data2 + i*3; rtengine::Coord currPos(i, j); diff --git a/rtgui/crophandler.h b/rtgui/crophandler.h index 2f7395216..150b256f4 100644 --- a/rtgui/crophandler.h +++ b/rtgui/crophandler.h @@ -113,7 +113,7 @@ public: void setEnabled (bool e); bool getEnabled (); - void colorPick (rtengine::Coord pickerPos, float &r, float &g, float &b, LockableColorPicker::PickerSize size); + void colorPick (rtengine::Coord pickerPos, float &r, float &g, float &b, LockableColorPicker::Size size); rtengine::DetailedCrop* getCrop() { diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index 0a97b3158..f5d8ede4a 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -322,11 +322,19 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) state = SDragPicker; } else { // Add a new Color Picker - int x2, y2; - screenCoordToImage(x, y, x2, y2); - LockableColorPicker *newPicker = new LockableColorPicker(x2, y2, LockableColorPicker::PickerSize::S15, 0., 0., 0., this); + rtengine::Coord imgPos, cropPos; + screenCoordToImage(x, y, imgPos.x, imgPos.y); + LockableColorPicker *newPicker = new LockableColorPicker(imgPos.x, imgPos.y, LockableColorPicker::Size::S15, 0., 0., 0., this, &cropHandler.colorParams.output, &cropHandler.colorParams.working); colorPickers.push_back(newPicker); hoveredPicker = newPicker; + imageCoordToCropImage(imgPos.x, imgPos.y, cropPos.x, cropPos.y); + float r=0.f, g=0.f, b=0.f; + LockableColorPicker::Validity validity = checkValidity (hoveredPicker, cropPos); + hoveredPicker->setValidity (validity); + if (validity == LockableColorPicker::Validity::INSIDE) { + cropHandler.colorPick(cropPos, r, g, b, hoveredPicker->getSize()); + hoveredPicker->setRGB (r, g, b); + } state = SDragPicker; press_x = x; press_y = y; @@ -854,9 +862,9 @@ void CropWindow::pointerMoved (int bstate, int x, int y) } imageCoordToCropImage(imgPos.x, imgPos.y, cropPos.x, cropPos.y); float r=0.f, g=0.f, b=0.f; - bool isValid = isHoveredPickerFullyInside (cropPos); - hoveredPicker->setValidity (isValid); - if (isValid) { + LockableColorPicker::Validity validity = checkValidity (hoveredPicker, cropPos); + hoveredPicker->setValidity (validity); + if (validity == LockableColorPicker::Validity::INSIDE) { cropHandler.colorPick(cropPos, r, g, b, hoveredPicker->getSize()); } hoveredPicker->setPosition (imgPos, r, g, b); @@ -2032,23 +2040,41 @@ void CropWindow::changeZoom (int zoom, bool notify, int centerx, int centery) iarea->redraw (); } -bool CropWindow::isHoveredPickerFullyInside (const rtengine::Coord &pos) +LockableColorPicker::Validity CropWindow::checkValidity (LockableColorPicker* picker, const rtengine::Coord &pos) { - return true; - if (!cropHandler.cropPixbuf) { - return false; + if (!cropHandler.cropPixbuftrue) { + return LockableColorPicker::Validity::OUTSIDE; } - rtengine::Coord cropPos; + 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; - hoveredPicker->getImagePosition(pickerPos); + picker->getImagePosition(pickerPos); rtengine::Coord minPos(0, 0); - rtengine::Coord maxPos(cropHandler.cropPixbuf->get_width(), cropHandler.cropPixbuf->get_height()); - rtengine::Coord halfPickerSize((int)hoveredPicker->getSize()/2, (int)hoveredPicker->getSize()/2); + 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; - return pickerMinPos >= minPos && pickerMaxPos <= maxPos; + 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) @@ -2360,6 +2386,7 @@ void CropWindow::cropImageUpdated () colorPicker->getImagePosition(imgPos); imageCoordToCropImage(imgPos.x, imgPos.y, cropPos.x, cropPos.y); float r=0.f, g=0.f, b=0.f; + colorPicker->setValidity (checkValidity (colorPicker, cropPos)); cropHandler.colorPick(cropPos, r, g, b, colorPicker->getSize()); colorPicker->setRGB (r, g, b); } diff --git a/rtgui/cropwindow.h b/rtgui/cropwindow.h index d58291776..74cc318c4 100644 --- a/rtgui/cropwindow.h +++ b/rtgui/cropwindow.h @@ -105,7 +105,7 @@ class CropWindow : public LWButtonListener, public CropDisplayHandler, public Ed void drawObservedFrame (Cairo::RefPtr cr, int rw = 0, int rh = 0); void changeZoom (int zoom, bool notify = true, int centerx = -1, int centery = -1); - bool isHoveredPickerFullyInside(const rtengine::Coord &pos); + LockableColorPicker::Validity checkValidity (LockableColorPicker* picker, const rtengine::Coord &pos); // Used by the mainCropWindow only void getObservedFrameArea (int& x, int& y, int& w, int& h, int rw = 0, int rh = 0); @@ -127,6 +127,7 @@ public: { observedCropWin = cw; } + void deleteColorPickers (); void screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy); void screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy); diff --git a/rtgui/imagearea.cc b/rtgui/imagearea.cc index 8827d96e5..f21cd1b94 100644 --- a/rtgui/imagearea.cc +++ b/rtgui/imagearea.cc @@ -117,6 +117,7 @@ void ImageArea::setImProcCoordinator (rtengine::StagedImageProcessor* ipc_) cropWins.clear(); + mainCropWindow->deleteColorPickers (); mainCropWindow->setObservedCropWin (NULL); } diff --git a/rtgui/lockablecolorpicker.cc b/rtgui/lockablecolorpicker.cc index 22383c294..351787121 100644 --- a/rtgui/lockablecolorpicker.cc +++ b/rtgui/lockablecolorpicker.cc @@ -26,21 +26,24 @@ extern Options options; -LockableColorPicker::LockableColorPicker (CropWindow* cropWindow) -: cropWindow(cropWindow), displayedValues(ColorPickerType::RGB), position(0, 0), size(PickerSize::S20), - isValid(false), r(0.f), g(0.f), b(0.f), h(0.f), s(0.f), v(0.f) -{ -} +LockableColorPicker::LockableColorPicker (CropWindow* cropWindow, Glib::ustring *oProfile, Glib::ustring *wProfile) +: cropWindow(cropWindow), displayedValues(ColorPickerType::RGB), position(0, 0), size(Size::S20), + outputProfile(oProfile), workingProfile(wProfile), validity(Validity::OUTSIDE), + r(0.f), g(0.f), b(0.f), h(0.f), s(0.f), v(0.f), L(0.f), a(0.f), bb(0.f) +{} -LockableColorPicker::LockableColorPicker (int x, int y, PickerSize size, const float R, const float G, const float B, CropWindow* cropWindow) +LockableColorPicker::LockableColorPicker (int x, int y, Size size, const float R, const float G, const float B, CropWindow* cropWindow, Glib::ustring *oProfile, Glib::ustring *wProfile) : cropWindow(cropWindow), displayedValues(ColorPickerType::RGB), position(x, y), size(size), - isValid(false), r(R), g(G), b(B) + outputProfile(oProfile), workingProfile(wProfile), validity(Validity::OUTSIDE), + r(R), g(G), b(B), L(0.f), a(0.f), bb(0.f) { float h_, s_, v_; rtengine::Color::rgb2hsv((float)r*65535.f, (float)g*65535.f, (float)b*65535.f, h_, s_, v_); h = (int)(h_*255.f); s = (int)(s_*255.f); v = (int)(v_*255.f); + + rtengine::Color::rgb2lab (*outputProfile, *workingProfile, r * 0xffff / 0xff, g * 0xffff / 0xff, b * 0xffff / 0xff, L, a, bb, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? } void LockableColorPicker::updateBackBuffer () @@ -48,20 +51,17 @@ void LockableColorPicker::updateBackBuffer () int newW, newH; // -------------------- setting some key constants --------------------- - const float circlePadding = 3.f; + const float circlePadding = 3.f; // keep this value odd // --------------------------------------------------------------------- - if (isValid) { + if (validity == Validity::INSIDE) { Gtk::DrawingArea *iArea = cropWindow->getImageArea(); - Cairo::RefPtr bbcr = BackBuffer::getContext(); Glib::RefPtr pangoContext = iArea->get_pango_context (); Pango::FontDescription fontd(options.colorPickerFont); + fontd.set_weight(Pango::WEIGHT_NORMAL); pangoContext->set_font_description (fontd); - // for drawing text - bbcr->set_line_width (0.); - Glib::RefPtr layout[3][2]; switch (displayedValues) { @@ -97,10 +97,10 @@ void LockableColorPicker::updateBackBuffer () int maxHRow2 = rtengine::max(h20, h21); // -------------------- setting some key constants --------------------- - const int textPadding = 2; + const int textPadding = 3; const int textWidth = maxWCol0 + maxWCol1 + textPadding; const int textHeight = maxHRow0 + maxHRow1 + maxHRow2 + 2*textPadding; - const double opacity = 0.75; + const double opacity = 0.62; // --------------------------------------------------------------------- newW = rtengine::max((int)size + 2 * circlePadding, textWidth + 2 * textPadding); @@ -108,29 +108,44 @@ void LockableColorPicker::updateBackBuffer () setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, newW, newH, true); - float center = (float)size/2.f + circlePadding; + Cairo::RefPtr bbcr = BackBuffer::getContext(); + + // cleaning the back buffer + bbcr->set_source_rgba (0., 0., 0., 0.); + bbcr->set_operator (Cairo::OPERATOR_CLEAR); + bbcr->paint (); + bbcr->set_operator (Cairo::OPERATOR_OVER); + + // for drawing text + bbcr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); + bbcr->set_line_width (0.); + + float center = (float)size / 2.f + circlePadding; // black background of the whole color picker bbcr->set_line_width (0.); - bbcr->arc_negative (center, center, center, 0.f, (float)M_PI); - bbcr->line_to (0, 2.f * center + textHeight + 2*textPadding); - bbcr->line_to (textWidth + 2*textPadding, 2.f * center + textHeight + 2*textPadding); - bbcr->line_to (textWidth + 2*textPadding, 2.f * center); - bbcr->line_to (2.f * center, 2.f * center); - bbcr->close_path(); bbcr->set_source_rgba (0., 0., 0., opacity); + bbcr->arc_negative (center, center, center, 0., (double)M_PI); + bbcr->line_to (0, 2. * center + textHeight); + bbcr->arc_negative (2. * textPadding, 2. * center + textHeight, 2. * textPadding, (double)M_PI, (double)M_PI / 2.); + bbcr->line_to (textWidth, 2. * center + textHeight + 2. * textPadding); + bbcr->arc_negative (textWidth, 2. * center + textHeight, 2. * textPadding, (double)M_PI / 2., 0.); + bbcr->line_to (textWidth + 2. * textPadding, 2. * center + 2. * textPadding); + bbcr->arc_negative (textWidth, 2. * center + 2. * textPadding, 2. * textPadding, 0., (double)M_PI * 1.5); + bbcr->line_to (2. * center, 2. * center); + bbcr->close_path(); bbcr->set_line_join (Cairo::LINE_JOIN_BEVEL); bbcr->set_line_cap (Cairo::LINE_CAP_SQUARE); bbcr->fill (); // light grey circle around the color mark - bbcr->arc (center, center, center - circlePadding / 2.f - 0.5f, 0.f, 2.f * (float)M_PI); - bbcr->set_source_rgb (0.7, 0.7, 0.7); - bbcr->set_line_width (circlePadding-2.f); + bbcr->arc (center, center, center - circlePadding / 2., 0., 2. * (double)M_PI); + bbcr->set_source_rgb (0.75, 0.75, 0.75); + bbcr->set_line_width (circlePadding - 2.); bbcr->stroke (); // spot disc with picked color - bbcr->arc (center, center, center - circlePadding - 0.5f, 0.f, 2.f * (float)M_PI); + bbcr->arc (center, center, center - circlePadding, 0., 2. * (double)M_PI); bbcr->set_source_rgb (r, g, b); // <- set the picker color here bbcr->set_line_width (0.); bbcr->fill(); @@ -141,7 +156,7 @@ void LockableColorPicker::updateBackBuffer () bbcr->set_line_cap (Cairo::LINE_CAP_ROUND); bbcr->set_source_rgb (1., 1., 1.); double txtOffsetX = textPadding; - double txtOffsetY = (double)size + 2.f * circlePadding + textPadding; + double txtOffsetY = (double)size + 2. * circlePadding + textPadding; switch (iArea->get_direction()) { case Gtk::TEXT_DIR_RTL: bbcr->move_to (txtOffsetX , txtOffsetY); @@ -190,31 +205,37 @@ void LockableColorPicker::updateBackBuffer () anchorOffset.set (center, center); - } else { + setDirty (false); + } else if (validity == Validity::CROSSING) { newH = newW = (int)size + 2 * circlePadding; setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, newW, newH, true); Cairo::RefPtr bbcr = BackBuffer::getContext(); + bbcr->set_antialias(Cairo::ANTIALIAS_SUBPIXEL); - float center = (float)size/2.f + circlePadding; - bbcr->arc (center, center, center - circlePadding/2.f, 0.f, 2.f * (float)M_PI); + float center = (float)size / 2.f + circlePadding; + bbcr->arc (center, center, center - circlePadding / 2., 0., 2. * (double)M_PI); bbcr->set_source_rgb (0., 0., 0.); bbcr->set_line_width(circlePadding); bbcr->stroke_preserve(); bbcr->set_source_rgb (1., 1., 1.); - bbcr->set_line_width(circlePadding-2.f); + bbcr->set_line_width(circlePadding - 2.); bbcr->stroke (); anchorOffset.set (center, center); + setDirty (false); } - setDirty (false); } void LockableColorPicker::draw (Cairo::RefPtr &cr) { + if (validity == Validity::OUTSIDE) { + return; + } + if (isDirty()) { updateBackBuffer(); } @@ -240,7 +261,9 @@ void LockableColorPicker::setPosition (const rtengine::Coord &newPos, const floa s = (float)s_; v = (float)v_; - if (isValid) { + rtengine::Color::rgb2lab (*outputProfile, *workingProfile, r * 0xffff / 0xff, g * 0xffff / 0xff, b * 0xffff / 0xff, L, a, bb, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? + + if (validity != Validity::OUTSIDE) { setDirty(true); } } @@ -261,7 +284,9 @@ void LockableColorPicker::setRGB (const float R, const float G, const float B) s = (float)s_; v = (float)v_; - if (isValid) { + rtengine::Color::rgb2lab (*outputProfile, *workingProfile, r * 0xffff / 0xff, g * 0xffff / 0xff, b * 0xffff / 0xff, L, a, bb, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? + + if (validity != Validity::OUTSIDE) { setDirty(true); } } @@ -293,15 +318,15 @@ bool LockableColorPicker::isOver (int x, int y) return mousePos >= tl && mousePos <= br; } -void LockableColorPicker::setValidity (bool isValid) +void LockableColorPicker::setValidity (Validity validity) { - if (this->isValid != isValid) { + if (this->validity != validity) { setDirty(true); } - this->isValid = isValid; + this->validity = validity; } -void LockableColorPicker::setSize (PickerSize newSize) +void LockableColorPicker::setSize (Size newSize) { if (size != newSize) { @@ -310,23 +335,23 @@ void LockableColorPicker::setSize (PickerSize newSize) } } -LockableColorPicker::PickerSize LockableColorPicker::getSize () +LockableColorPicker::Size LockableColorPicker::getSize () { return size; } void LockableColorPicker::incSize () { - if (size < PickerSize::S30) { - size = (PickerSize)((int)size + 5); + if (size < Size::S30) { + size = (Size)((int)size + 5); setDirty(true); } } void LockableColorPicker::decSize () { - if (size > PickerSize::S5) { - size = (PickerSize)((int)size - 5); + if (size > Size::S5) { + size = (Size)((int)size - 5); setDirty(true); } } diff --git a/rtgui/lockablecolorpicker.h b/rtgui/lockablecolorpicker.h index 2f79649fe..8be803081 100644 --- a/rtgui/lockablecolorpicker.h +++ b/rtgui/lockablecolorpicker.h @@ -38,7 +38,7 @@ public: class LockableColorPicker : BackBuffer { public: - enum class PickerSize { + enum class Size { S5=5, S10=10, S15=15, @@ -46,26 +46,35 @@ public: S25=25, S30=30 }; + enum class Validity { + INSIDE, + CROSSING, + OUTSIDE + }; private: enum class ColorPickerType { RGB, - HSV + HSV, + LAB }; CropWindow* cropWindow; // the color picker is displayed in a single cropWindow, the one that the user has clicked in ColorPickerType displayedValues; rtengine::Coord position; // Coordinate in image space rtengine::Coord anchorOffset; - PickerSize size; - bool isValid; + Size size; + Glib::ustring *outputProfile; + Glib::ustring *workingProfile; + Validity validity; float r, g, b; // red green blue in [0;1] range float h, s, v; // hue saturation value in [0;1] range + float L, a, bb; // L*a*b value in [0;1] range void updateBackBuffer (); public: - LockableColorPicker (CropWindow* cropWindow); - LockableColorPicker (int x, int y, PickerSize size, const float R, const float G, const float B, CropWindow* cropWindow); + LockableColorPicker (CropWindow* cropWindow, Glib::ustring *oProfile, Glib::ustring *wProfile); + LockableColorPicker (int x, int y, Size size, const float R, const float G, const float B, CropWindow* cropWindow, Glib::ustring *oProfile, Glib::ustring *wProfile); void draw (Cairo::RefPtr &cr); @@ -74,10 +83,10 @@ public: void setRGB (const float R, const float G, const float B); void getImagePosition (rtengine::Coord &imgPos); void getScreenPosition (rtengine::Coord &screenPos); - PickerSize getSize (); + Size getSize (); bool isOver (int x, int y); - void setValidity (bool isValid); - void setSize (PickerSize newSize); + void setValidity (Validity isValid); + void setSize (Size newSize); void incSize (); void decSize (); }; diff --git a/rtgui/navigator.cc b/rtgui/navigator.cc index 049b4ec7e..0142df49f 100644 --- a/rtgui/navigator.cc +++ b/rtgui/navigator.cc @@ -266,7 +266,7 @@ void Navigator::pointerMoved (bool validPos, Glib::ustring profile, Glib::ustrin float LAB_a, LAB_b, LAB_l; //rgb2lab (r, g, b, LAB_l, LAB_a, LAB_b); - rgb2lab (profile, profileW, r, g, b, LAB_l, LAB_a, LAB_b); // TODO: Really sure this function works? + Color::rgb2lab (profile, profileW, r * 0xffff / 0xff, g * 0xffff / 0xff, b * 0xffff / 0xff, LAB_l, LAB_a, LAB_b, options.rtSettings.HistogramWorking); // TODO: Really sure this function works? LAB_A->set_text (Glib::ustring::format(std::fixed, std::setprecision(1), LAB_a)); LAB_B->set_text (Glib::ustring::format(std::fixed, std::setprecision(1), LAB_b)); LAB_L->set_text (Glib::ustring::format(std::fixed, std::setprecision(1), LAB_l)); @@ -328,150 +328,3 @@ void Navigator::cycleUnitsHSV (GdkEventButton *event) { break; } } - -void Navigator::rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b) -{ - double xyz_rgb[3][3]; - const double ep = 216.0 / 24389.0; - const double ka = 24389.0 / 27.0; - - double var_R = r / 255.0; - double var_G = g / 255.0; - double var_B = b / 255.0; - - Glib::ustring profileCalc; - profileCalc = "sRGB"; //default - - if(options.rtSettings.HistogramWorking) { - profileCalc = profileW; //display working - } - - else {// if you want display = output space - if (profile == "RT_sRGB" || profile == "RT_sRGB_gBT709" || profile == "RT_sRGB_g10") { - profileCalc = "sRGB"; - } - - if (profile == "ProPhoto" || profile == "RT_Large_gBT709" || profile == "RT_Large_g10" || profile == "RT_Large_gsRGB") { - profileCalc = "ProPhoto"; - } - - if (profile == "AdobeRGB1998" || profile == "RT_Medium_gsRGB") { - profileCalc = "Adobe RGB"; - } - - if (profile == "WideGamutRGB") { - profileCalc = "WideGamut"; - } - } - - if(options.rtSettings.HistogramWorking) {//display working - if (profileW == "sRGB") { //apply sRGB inverse gamma - - if ( var_R > 0.04045 ) { - var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_R = var_R / 12.92; - } - - if ( var_G > 0.04045 ) { - var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_G = var_G / 12.92; - } - - if ( var_B > 0.04045 ) { - var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_B = var_B / 12.92; - } - } else if (profileW == "ProPhoto") { // apply inverse gamma 1.8 - var_R = pow ( var_R, 1.8); - var_G = pow ( var_G, 1.8); - var_B = pow ( var_B, 1.8); - } else { // apply inverse gamma 2.2 - var_R = pow ( var_R, 2.2); - var_G = pow ( var_G, 2.2); - var_B = pow ( var_B, 2.2); - } - } else { //display outout profile - - if (profile == "RT_sRGB" || profile == "RT_Large_gsRGB" || profile == "RT_Medium_gsRGB") { //apply sRGB inverse gamma - if ( var_R > 0.04045 ) { - var_R = pow ( ( ( var_R + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_R = var_R / 12.92; - } - - if ( var_G > 0.04045 ) { - var_G = pow ( ( ( var_G + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_G = var_G / 12.92; - } - - if ( var_B > 0.04045 ) { - var_B = pow ( ( ( var_B + 0.055 ) / 1.055 ), rtengine::Color::sRGBGammaCurve); - } else { - var_B = var_B / 12.92; - } - } - - else if (profile == "RT_sRGB_gBT709" || profile == "RT_Large_gBT709") { // - if ( var_R > 0.0795 ) { - var_R = pow ( ( ( var_R + 0.0954 ) / 1.0954 ), 2.2); - } else { - var_R = var_R / 4.5; - } - - if ( var_G > 0.0795 ) { - var_G = pow ( ( ( var_G + 0.0954 ) / 1.0954 ), 2.2); - } else { - var_G = var_G / 4.5; - } - - if ( var_B > 0.0795 ) { - var_B = pow ( ( ( var_B + 0.0954 ) / 1.0954 ), 2.2); - } else { - var_B = var_B / 4.5; - } - - } else if (profile == "ProPhoto") { // apply inverse gamma 1.8 - - var_R = pow ( var_R, 1.8); - var_G = pow ( var_G, 1.8); - var_B = pow ( var_B, 1.8); - } else if (profile == "RT_sRGB_g10" || profile == "RT_Large_g10") { // apply inverse gamma 1.8 - - var_R = pow ( var_R, 1.); - var_G = pow ( var_G, 1.); - var_B = pow ( var_B, 1.); - } - - else {// apply inverse gamma 2.2 - var_R = pow ( var_R, 2.2); - var_G = pow ( var_G, 2.2); - var_B = pow ( var_B, 2.2); - } - } - - // TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileW); - - TMatrix wprof = rtengine::ICCStore::getInstance()->workingSpaceMatrix (profileCalc); - - for (int m = 0; m < 3; m++) - for (int n = 0; n < 3; n++) { - xyz_rgb[m][n] = wprof[m][n]; - } - - double varxx, varyy, varzz; - double var_X = ( xyz_rgb[0][0] * var_R + xyz_rgb[0][1] * var_G + xyz_rgb[0][2] * var_B ) / Color::D50x; - double var_Y = ( xyz_rgb[1][0] * var_R + xyz_rgb[1][1] * var_G + xyz_rgb[1][2] * var_B ) ; - double var_Z = ( xyz_rgb[2][0] * var_R + xyz_rgb[2][1] * var_G + xyz_rgb[2][2] * var_B ) / Color::D50z; - - varxx = var_X > ep ? cbrt(var_X) : ( ka * var_X + 16.0) / 116.0 ; - varyy = var_Y > ep ? cbrt(var_Y) : ( ka * var_Y + 16.0) / 116.0 ; - varzz = var_Z > ep ? cbrt(var_Z) : ( ka * var_Z + 16.0) / 116.0 ; - LAB_l = ( 116 * varyy ) - 16; - LAB_a = 500 * ( varxx - varyy ); - LAB_b = 200 * ( varyy - varzz ); - -} diff --git a/rtgui/navigator.h b/rtgui/navigator.h index e0b79d8dd..d7b9d2bae 100644 --- a/rtgui/navigator.h +++ b/rtgui/navigator.h @@ -46,8 +46,6 @@ protected: Gtk::Label *lH, *lS, *lV; Gtk::Label *lLAB_A, *lLAB_B, *lLAB_L; - void rgb2lab (Glib::ustring profile, Glib::ustring profileW, int r, int g, int b, float &LAB_l, float &LAB_a, float &LAB_b); - void setInvalid (int fullWidth = -1, int fullHeight = -1); public: diff --git a/rtgui/toolbar.cc b/rtgui/toolbar.cc index dd425a279..10137c673 100644 --- a/rtgui/toolbar.cc +++ b/rtgui/toolbar.cc @@ -109,17 +109,18 @@ ToolBar::~ToolBar () void ToolBar::setTool (ToolMode tool) { - handConn.block (true); - cropConn.block (true); + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true, cpWasBlocked = true; if (wbTool) { - wbConn.block (true); + wbWasBlocked = wbConn.block (true); } if (colPickerTool) { - cpConn.block (true); + cpWasBlocked = cpConn.block (true); } - straConn.block (true); + bool straWasBlocked = straConn.block (true); bool stopEdit = tool == TMHand && handTool->get_active() && editingMode; @@ -154,17 +155,17 @@ void ToolBar::setTool (ToolMode tool) current = tool; - handConn.block (false); - cropConn.block (false); + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); if (wbTool) { - wbConn.block (false); + if (!wbWasBlocked) wbConn.block (false); } if (colPickerTool) { - cpConn.block (false); + if (!cpWasBlocked) cpConn.block (false); } - straConn.block (false); + if (!straWasBlocked) straConn.block (false); if (stopEdit) { stopEditMode(); @@ -178,7 +179,44 @@ void ToolBar::setTool (ToolMode tool) void ToolBar::startEditMode() { if (!editingMode) { - handTool->set_active(true); // will call hand_pressed, with editingMode=false + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true, cpWasBlocked = true; + if (colPickerTool) { + cpWasBlocked = cpConn.block (true); + } + + if (wbTool) { + wbWasBlocked = wbConn.block (true); + } + + bool straWasBlocked = straConn.block (true); + + if (current != TMHand) { + if (colPickerTool) { + colPickerTool->set_active(false); + } + if (wbTool) { + wbTool->set_active (false); + } + + cropTool->set_active (false); + straTool->set_active (false); + current = TMHand; + } + handTool->set_active (true); + + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); + if (colPickerTool) { + if (!cpWasBlocked) cpConn.block (false); + } + if (wbTool) { + if (!wbWasBlocked) wbConn.block (false); + } + + if (!straWasBlocked) straConn.block (false); + editingMode = true; handTool->set_image(*editinghandimg); } @@ -195,13 +233,6 @@ void ToolBar::stopEditMode() { if (editingMode) { editingMode = false; - /* WARNING: Should we toggle the Hand button on? - * This method can be called while another tool is active, e.g. if the user toggle off - * the Subscriber's Edit button. For now, we keep that other tool active. If one want to - * switch to the Hand tool, uncommenting the following line should suffice (not tested). - * - * handTool->set_active(true); - */ handTool->set_image(*handimg); } } @@ -209,18 +240,25 @@ void ToolBar::stopEditMode() void ToolBar::hand_pressed () { - handConn.block (true); - cropConn.block (true); + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true, cpWasBlocked = true; if (colPickerTool) { - cpConn.block (true); + cpWasBlocked = cpConn.block (true); } if (wbTool) { - wbConn.block (true); + wbWasBlocked = wbConn.block (true); } - straConn.block (true); + bool straWasBlocked = straConn.block (true); + if (editingMode) { + stopEditMode(); + if (listener) { + listener->editModeSwitchedOff (); + } + } if (current != TMHand) { if (colPickerTool) { colPickerTool->set_active(false); @@ -232,27 +270,19 @@ void ToolBar::hand_pressed () cropTool->set_active (false); straTool->set_active (false); current = TMHand; - } else { - if (editingMode) { - stopEditMode(); - } - - if (listener) { - listener->editModeSwitchedOff (); - } } handTool->set_active (true); - handConn.block (false); - cropConn.block (false); + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); if (colPickerTool) { - cpConn.block (false); + if (!cpWasBlocked) cpConn.block (false); } if (wbTool) { - wbConn.block (false); + if (!wbWasBlocked) wbConn.block (false); } - straConn.block (false); + if (!straWasBlocked) straConn.block (false); if (listener) { listener->toolSelected (TMHand); @@ -262,18 +292,25 @@ void ToolBar::hand_pressed () void ToolBar::wb_pressed () { - handConn.block (true); - cropConn.block (true); + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true, cpWasBlocked = true; if (colPickerTool) { - cpConn.block (true); + cpWasBlocked = cpConn.block (true); } if (wbTool) { - wbConn.block (true); + wbWasBlocked = wbConn.block (true); } - straConn.block (true); + bool straWasBlocked = straConn.block (true); if (current != TMSpotWB) { + if (editingMode) { + stopEditMode(); + if (listener) { + listener->editModeSwitchedOff (); + } + } handTool->set_active (false); cropTool->set_active (false); straTool->set_active (false); @@ -287,16 +324,16 @@ void ToolBar::wb_pressed () wbTool->set_active (true); } - handConn.block (false); - cropConn.block (false); + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); if (colPickerTool) { - cpConn.block (false); + if (!cpWasBlocked) cpConn.block (false); } if (wbTool) { - wbConn.block (false); + if (!wbWasBlocked) wbConn.block (false); } - straConn.block (false); + if (!straWasBlocked) straConn.block (false); if (listener) { listener->toolSelected (TMSpotWB); @@ -307,14 +344,15 @@ void ToolBar::colPicker_pressed (GdkEventButton* event) { if (event->button == 1) { - handConn.block (true); - cropConn.block (true); - cpConn.block (true); + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true; + bool cpWasBlocked = cpConn.block (true); if (wbTool) { - wbConn.block (true); + wbWasBlocked = wbConn.block (true); } - straConn.block (true); + bool straWasBlocked = straConn.block (true); cropTool->set_active (false); if (wbTool) { @@ -324,21 +362,27 @@ void ToolBar::colPicker_pressed (GdkEventButton* event) if (current != TMColorPicker) { // Disabling all other tools, enabling the Picker tool and entering the "visible pickers" mode + if (editingMode) { + stopEditMode(); + if (listener) { + listener->editModeSwitchedOff (); + } + } handTool->set_active (false); showColorPickers(true); current = TMColorPicker; } else { // Disabling the picker tool, enabling the Hand tool and keeping the "visible pickers" mode handTool->set_active (true); - colPickerTool->set_active (false); + //colPickerTool->set_active (false); Done by the standard event handler current = TMHand; } - handConn.block (false); - cropConn.block (false); - cpConn.block (false); - wbConn.block (false); - straConn.block (false); + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); + if (!cpWasBlocked) cpConn.block (false); + if (!wbWasBlocked) wbConn.block (false); + if (!straWasBlocked) straConn.block (false); if (listener) { listener->toolSelected (current); @@ -346,14 +390,14 @@ void ToolBar::colPicker_pressed (GdkEventButton* event) } else if (event->button == 3) { if (current == TMColorPicker) { // Disabling the Picker tool and entering into the "invisible pickers" mode - cpConn.block (true); - handConn.block (true); + bool cpWasBlocked = cpConn.block (true); + bool handWasBlocked = handConn.block (true); handTool->set_active (true); colPickerTool->set_active (false); current = TMHand; showColorPickers(false); - cpConn.block (false); - handConn.block (false); + if (!cpWasBlocked) cpConn.block (false); + if (!handWasBlocked) handConn.block (false); } else { // The Picker tool is already disabled, entering into the "invisible pickers" mode switchColorPickersVisibility(); @@ -386,17 +430,26 @@ void ToolBar::switchColorPickersVisibility() void ToolBar::crop_pressed () { - handConn.block (true); - cropConn.block (true); - cpConn.block(true); - - if (wbTool) { - wbConn.block (true); + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true, cpWasBlocked = true; + if (colPickerTool) { + cpConn.block(true); } - straConn.block (true); + if (wbTool) { + wbWasBlocked = wbConn.block (true); + } + + bool straWasBlocked = straConn.block (true); if (current != TMCropSelect) { + if (editingMode) { + stopEditMode(); + if (listener) { + listener->editModeSwitchedOff (); + } + } handTool->set_active (false); if (colPickerTool) { colPickerTool->set_active(false); @@ -410,11 +463,12 @@ void ToolBar::crop_pressed () } cropTool->set_active (true); - handConn.block (false); - cropConn.block (false); - cpConn.block(false); - wbConn.block (false); - straConn.block (false); + cropTool->grab_focus (); + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); + if (!cpWasBlocked) cpConn.block(false); + if (!wbWasBlocked) wbConn.block (false); + if (!straWasBlocked) straConn.block (false); if (listener) { listener->toolSelected (TMCropSelect); @@ -424,19 +478,26 @@ void ToolBar::crop_pressed () void ToolBar::stra_pressed () { - handConn.block (true); - cropConn.block (true); + bool handWasBlocked = handConn.block (true); + bool cropWasBlocked = cropConn.block (true); + bool wbWasBlocked = true, cpWasBlocked = true; if (colPickerTool) { - cpConn.block (true); + cpWasBlocked = cpConn.block (true); } if (wbTool) { - wbConn.block (true); + wbWasBlocked = wbConn.block (true); } - straConn.block (true); + bool straWasBlocked = straConn.block (true); if (current != TMStraighten) { + if (editingMode) { + stopEditMode(); + if (listener) { + listener->editModeSwitchedOff (); + } + } handTool->set_active (false); if (colPickerTool) { colPickerTool->set_active(false); @@ -450,15 +511,15 @@ void ToolBar::stra_pressed () } straTool->set_active (true); - handConn.block (false); - cropConn.block (false); - cpConn.block (false); + if (!handWasBlocked) handConn.block (false); + if (!cropWasBlocked) cropConn.block (false); + if (!cpWasBlocked) cpConn.block (false); if (wbTool) { - wbConn.block (false); + if (!wbWasBlocked) wbConn.block (false); } - straConn.block (false); + if (!straWasBlocked) straConn.block (false); if (listener) { listener->toolSelected (TMStraighten);