diff --git a/libs/ui/widgets/kis_gradient_slider.cpp b/libs/ui/widgets/kis_gradient_slider.cpp index b2580eeeb5..16c0619c38 100644 --- a/libs/ui/widgets/kis_gradient_slider.cpp +++ b/libs/ui/widgets/kis_gradient_slider.cpp @@ -1,371 +1,401 @@ /* * This file is part of Krita * * Copyright (c) 2006 Frederic Coiffier * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Local includes. #include "kis_gradient_slider.h" // C++ includes. #include #include // Qt includes. #include #include #include #include #include #include #include #include #define MARGIN 5 #define HANDLE_SIZE 10 KisGradientSlider::KisGradientSlider(QWidget *parent) : QWidget(parent) , m_leftmost(0) , m_rightmost(0) , m_scalingFactor(0) , m_blackCursor(0) , m_whiteCursor(0) , m_gammaCursor(0) , m_black(0) , m_white(255) , m_gamma(1.0) , m_gammaEnabled(false) , m_whiteEnabled(true) , m_feedback(false) + , m_inverted(false) { m_grabCursor = None; setMouseTracking(true); setFocusPolicy(Qt::StrongFocus); } KisGradientSlider::~KisGradientSlider() { } int KisGradientSlider::black() const { return m_black; } int KisGradientSlider::white() const { return m_white; } void KisGradientSlider::paintEvent(QPaintEvent *e) { QWidget::paintEvent(e); int x, y; int wWidth = width() - (2 * MARGIN); int wHeight = height(); const int gradientHeight = qRound((double)wHeight / 7.0 * 2); QPainter p1(this); p1.fillRect(rect(), palette().background()); p1.setPen(Qt::black); p1.drawRect(MARGIN, MARGIN, wWidth, height() - 2 * MARGIN - HANDLE_SIZE); // Draw first gradient - QLinearGradient grayGradient(MARGIN, 0, wWidth, gradientHeight); - grayGradient.setColorAt(0, Qt::black); - grayGradient.setColorAt(1, Qt::white); + QLinearGradient grayGradient(MARGIN, y, wWidth, gradientHeight); + grayGradient.setColorAt(0, m_inverted ? Qt::white : Qt::black); + grayGradient.setColorAt(1, m_inverted ? Qt::black : Qt::white); p1.fillRect(MARGIN, 0, wWidth, gradientHeight, QBrush(grayGradient)); // Draw second gradient y = gradientHeight; p1.fillRect(MARGIN, y, wWidth, gradientHeight, Qt::white); - if (m_blackCursor > 0) { + if (m_blackCursor > 0 && !m_inverted) { p1.fillRect(MARGIN, y, m_blackCursor, gradientHeight, Qt::black); + } else if (m_blackCursor < wWidth && m_inverted) { + p1.fillRect(MARGIN + m_blackCursor, y, wWidth - m_blackCursor, gradientHeight, Qt::black); } - for (x = (int)m_blackCursor + MARGIN; x < (int)m_whiteCursor - MARGIN; ++x) { - double inten = (double)(x - (m_blackCursor + MARGIN)) / (double)((m_whiteCursor - MARGIN) - (m_blackCursor + MARGIN)); + + int left = qMin(m_blackCursor, m_whiteCursor); + int right = qMax(m_blackCursor, m_whiteCursor); + for (x = left; x <= right; ++x) { + double inten = (double)(x - m_blackCursor) / + (double)(m_whiteCursor - m_blackCursor); inten = pow(inten, (1.0 / m_gamma)); int gray = (int)(255 * inten); p1.setPen(QColor(gray, gray, gray)); - p1.drawLine(x, y, x, y + gradientHeight - 1); + p1.drawLine(x + MARGIN, y, x + MARGIN, y + gradientHeight - 1); } // Draw cursors y += gradientHeight; QPoint a[3]; p1.setPen(Qt::darkGray); p1.setRenderHint(QPainter::Antialiasing, true); const int cursorHalfBase = (int)(gradientHeight / 1.5); a[0] = QPoint(m_blackCursor + MARGIN, y); a[1] = QPoint(m_blackCursor + MARGIN + cursorHalfBase, wHeight - 1); a[2] = QPoint(m_blackCursor + MARGIN - cursorHalfBase, wHeight - 1); p1.setBrush(Qt::black); p1.drawPolygon(a, 3); p1.setPen(Qt::black); if (m_gammaEnabled) { - a[0] = QPoint(m_gammaCursor, y); - a[1] = QPoint(m_gammaCursor + cursorHalfBase, wHeight - 1); - a[2] = QPoint(m_gammaCursor - cursorHalfBase, wHeight - 1); + a[0] = QPoint(m_gammaCursor + MARGIN, y); + a[1] = QPoint(m_gammaCursor + MARGIN + cursorHalfBase, wHeight - 1); + a[2] = QPoint(m_gammaCursor + MARGIN - cursorHalfBase, wHeight - 1); p1.setBrush(Qt::gray); p1.drawPolygon(a, 3); } if (m_whiteEnabled) { - a[0] = QPoint(m_whiteCursor - MARGIN, y); - a[1] = QPoint(m_whiteCursor - MARGIN + cursorHalfBase, wHeight - 1); - a[2] = QPoint(m_whiteCursor - MARGIN - cursorHalfBase, wHeight - 1); + a[0] = QPoint(m_whiteCursor + MARGIN, y); + a[1] = QPoint(m_whiteCursor + MARGIN + cursorHalfBase, wHeight - 1); + a[2] = QPoint(m_whiteCursor + MARGIN - cursorHalfBase, wHeight - 1); p1.setBrush(Qt::white); p1.drawPolygon(a, 3); } } void KisGradientSlider::resizeEvent(QResizeEvent *) { - m_scalingFactor = (double)(width() - MARGIN) / 255; + m_scalingFactor = (double)(width() - 2 * MARGIN) / 255; calculateCursorPositions(); update(); } -void KisGradientSlider::mousePressEvent(QMouseEvent * e) +void KisGradientSlider::mousePressEvent(QMouseEvent *e) { eCursor closest_cursor; int distance; if (e->button() != Qt::LeftButton) return; unsigned int x = e->pos().x(); - int xPlusMargin = x + MARGIN; + int xMinusMargin = x - MARGIN; distance = width() + 1; // just a big number - if (abs((int)(xPlusMargin - m_blackCursor)) < distance) { - distance = abs((int)(xPlusMargin - m_blackCursor)); + if (abs((int)(xMinusMargin - m_blackCursor)) < distance) { + distance = abs((int)(xMinusMargin - m_blackCursor)); closest_cursor = BlackCursor; } - if (abs((int)(xPlusMargin - m_whiteCursor)) < distance) { - distance = abs((int)(xPlusMargin - m_whiteCursor)); + if (abs((int)(xMinusMargin - m_whiteCursor)) < distance) { + distance = abs((int)(xMinusMargin - m_whiteCursor)); closest_cursor = WhiteCursor; } if (m_gammaEnabled) { - int gammaDistance = (int)xPlusMargin - m_gammaCursor; + int gammaDistance = (int)xMinusMargin - m_gammaCursor; if (abs(gammaDistance) < distance) { - distance = abs((int)xPlusMargin - m_gammaCursor); + distance = abs((int)xMinusMargin - m_gammaCursor); closest_cursor = GammaCursor; } else if (abs(gammaDistance) == distance) { if ((closest_cursor == BlackCursor) && (gammaDistance > 0)) { distance = abs(gammaDistance); closest_cursor = GammaCursor; } else if ((closest_cursor == WhiteCursor) && (gammaDistance < 0)) { distance = abs(gammaDistance); closest_cursor = GammaCursor; } } } if (distance > 20) { m_grabCursor = None; return; } // Determine cursor values and the leftmost and rightmost points. switch (closest_cursor) { case BlackCursor: - m_blackCursor = x - MARGIN; + m_blackCursor = xMinusMargin; m_grabCursor = closest_cursor; - m_leftmost = 0; - m_rightmost = m_whiteCursor - ((MARGIN + 1) * m_scalingFactor); + if (m_inverted) { + m_leftmost = m_whiteCursor + 1; + m_rightmost = width() - 2 * MARGIN - 1; + } else { + m_leftmost = 0; + m_rightmost = m_whiteCursor - 1; + } if (m_gammaEnabled) m_gammaCursor = calculateGammaCursor(); break; case WhiteCursor: - m_whiteCursor = x + MARGIN; + m_whiteCursor = xMinusMargin; m_grabCursor = closest_cursor; - m_leftmost = m_blackCursor + (MARGIN * m_scalingFactor); - m_rightmost = width() - MARGIN ; + if (m_inverted) { + m_leftmost = 0; + m_rightmost = m_blackCursor - 1; + } else { + m_leftmost = m_blackCursor + 1; + m_rightmost = width() - 2 * MARGIN - 1; + } if (m_gammaEnabled) m_gammaCursor = calculateGammaCursor(); break; case GammaCursor: m_gammaCursor = x; m_grabCursor = closest_cursor; - m_leftmost = m_blackCursor + (MARGIN * m_scalingFactor); - m_rightmost = m_whiteCursor - (MARGIN * m_scalingFactor); + m_leftmost = qMin(m_blackCursor, m_whiteCursor); + m_rightmost = qMax(m_blackCursor, m_whiteCursor); { double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0; double mid = (double)m_blackCursor + delta + MARGIN; - double tmp = (x - mid) / delta; + double tmp = (xMinusMargin - mid) / delta; m_gamma = 1.0 / pow(10, tmp); } break; default: break; } update(); } void KisGradientSlider::mouseReleaseEvent(QMouseEvent * e) { if (e->button() != Qt::LeftButton) return; update(); switch (m_grabCursor) { case BlackCursor: - m_black = qRound( m_blackCursor / m_scalingFactor); + m_black = qRound(m_blackCursor / m_scalingFactor); m_feedback = true; emit sigModifiedBlack(m_black); break; case WhiteCursor: - m_white = qRound( (m_whiteCursor - MARGIN) / m_scalingFactor); + m_white = qRound(m_whiteCursor / m_scalingFactor); m_feedback = true; emit sigModifiedWhite(m_white); break; case GammaCursor: emit sigModifiedGamma(m_gamma); break; default: break; } m_grabCursor = None; m_feedback = false; } void KisGradientSlider::mouseMoveEvent(QMouseEvent * e) { int x = e->pos().x(); - + int xMinusMargin = x - MARGIN; if (m_grabCursor != None) { // Else, drag the selected point - if (x + MARGIN <= m_leftmost) - x = m_leftmost; + if (xMinusMargin <= m_leftmost) + xMinusMargin = m_leftmost; - if (x >= m_rightmost) - x = m_rightmost; + if (xMinusMargin >= m_rightmost) + xMinusMargin = m_rightmost; switch (m_grabCursor) { case BlackCursor: - if (m_blackCursor != x) { - m_blackCursor = x; + if (m_blackCursor != xMinusMargin) { + m_blackCursor = xMinusMargin; if (m_gammaEnabled) { m_gammaCursor = calculateGammaCursor(); } } break; case WhiteCursor: - if (m_whiteCursor != x) { - m_whiteCursor = x + MARGIN; + if (m_whiteCursor != xMinusMargin) { + m_whiteCursor = xMinusMargin; if (m_gammaEnabled) { m_gammaCursor = calculateGammaCursor(); } } break; case GammaCursor: - if (m_gammaCursor != x) { - m_gammaCursor = x; + if (m_gammaCursor != xMinusMargin) { + m_gammaCursor = xMinusMargin; double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0; double mid = (double)m_blackCursor + delta; - double tmp = (x - mid) / delta; + double tmp = (xMinusMargin - mid) / delta; m_gamma = 1.0 / pow(10, tmp); } break; default: break; } } update(); } void KisGradientSlider::calculateCursorPositions() { m_blackCursor = qRound(m_black * m_scalingFactor); - m_whiteCursor = qRound(m_white * m_scalingFactor + MARGIN); + m_whiteCursor = qRound(m_white * m_scalingFactor); m_gammaCursor = calculateGammaCursor(); } unsigned int KisGradientSlider::calculateGammaCursor() { double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0; - double mid = (double)m_blackCursor + delta; - double tmp = log10(1.0 / m_gamma); + double mid = (double)m_blackCursor + delta; + double tmp = log10(1.0 / m_gamma); return (unsigned int)qRound(mid + delta * tmp); } void KisGradientSlider::enableGamma(bool b) { m_gammaEnabled = b; update(); } double KisGradientSlider::getGamma(void) { return m_gamma; } void KisGradientSlider::enableWhite(bool b) { m_whiteEnabled = b; update(); } +void KisGradientSlider::setInverted(bool b) +{ + m_inverted = b; + update(); +} + void KisGradientSlider::slotModifyBlack(int v) { - if (v >= 0 && v <= (int)m_white && !m_feedback) { - m_black = v; - m_blackCursor = qRound(m_black * m_scalingFactor); - m_gammaCursor = calculateGammaCursor(); - update(); - } + if ((m_inverted && (v < m_white || v > width())) || + (!m_inverted && (v < 0 || v > m_white)) || + m_feedback) + return; + + m_black = v; + m_blackCursor = qRound(m_black * m_scalingFactor); + m_gammaCursor = calculateGammaCursor(); + update(); } + void KisGradientSlider::slotModifyWhite(int v) { - if (v >= (int)m_black && v <= width() && !m_feedback) { - m_white = v; - m_whiteCursor = qRound(m_white * m_scalingFactor + MARGIN); - m_gammaCursor = calculateGammaCursor(); - update(); - } + if ((m_inverted && (v < 0 || v > m_white)) || + (!m_inverted && (v < m_black && v > width())) || + m_feedback) + return; + m_white = v; + m_whiteCursor = qRound(m_white * m_scalingFactor); + m_gammaCursor = calculateGammaCursor(); + update(); } + void KisGradientSlider::slotModifyGamma(double v) { if (m_gamma != v) { emit sigModifiedGamma(v); } m_gamma = v; m_gammaCursor = calculateGammaCursor(); update(); } diff --git a/libs/ui/widgets/kis_gradient_slider.h b/libs/ui/widgets/kis_gradient_slider.h index 97a09e828c..2a8450aabf 100644 --- a/libs/ui/widgets/kis_gradient_slider.h +++ b/libs/ui/widgets/kis_gradient_slider.h @@ -1,98 +1,101 @@ /* * This file is part of Krita * * Copyright (c) 2006 Frederic Coiffier * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GRADIENT_SLIDER_H #define KIS_GRADIENT_SLIDER_H // Qt includes. #include #include #include #include #include class KRITAUI_EXPORT KisGradientSlider : public QWidget { Q_OBJECT typedef enum { BlackCursor, GammaCursor, WhiteCursor, None } eCursor; public: KisGradientSlider(QWidget *parent = 0); ~KisGradientSlider() override; int black() const; int white() const; public Q_SLOTS: void slotModifyBlack(int); void slotModifyWhite(int); void slotModifyGamma(double); Q_SIGNALS: void sigModifiedBlack(int); void sigModifiedWhite(int); void sigModifiedGamma(double); protected: void paintEvent(QPaintEvent *) override; void resizeEvent(QResizeEvent *) override; void mousePressEvent(QMouseEvent * e) override; void mouseReleaseEvent(QMouseEvent * e) override; void mouseMoveEvent(QMouseEvent * e) override; private: void calculateCursorPositions(); unsigned int calculateGammaCursor(); public: void enableGamma(bool b); double getGamma(void); void enableWhite(bool b); + void setInverted(bool b); + private: int m_leftmost; int m_rightmost; eCursor m_grabCursor; double m_scalingFactor; int m_blackCursor; int m_whiteCursor; int m_gammaCursor; int m_black; int m_white; double m_gamma; bool m_gammaEnabled; bool m_whiteEnabled; bool m_feedback; + bool m_inverted; }; #endif /* KIS_GRADIENT_SLIDER_H */ diff --git a/plugins/filters/levelfilter/kis_level_filter.cpp b/plugins/filters/levelfilter/kis_level_filter.cpp index 35e8db9021..25ba596fb7 100644 --- a/plugins/filters/levelfilter/kis_level_filter.cpp +++ b/plugins/filters/levelfilter/kis_level_filter.cpp @@ -1,312 +1,344 @@ /* * This file is part of Krita * * Copyright (c) 2006 Frederic Coiffier * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_level_filter.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_paint_device.h" #include "kis_histogram.h" #include "kis_painter.h" #include "kis_gradient_slider.h" #include "kis_processing_information.h" #include "kis_selection.h" #include "kis_types.h" #include "filter/kis_color_transformation_configuration.h" KisLevelFilter::KisLevelFilter() : KisColorTransformationFilter(id(), categoryAdjust(), i18n("&Levels...")) { setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); setSupportsPainting(false); setColorSpaceIndependence(TO_LAB16); } KisLevelFilter::~KisLevelFilter() { } KisConfigWidget * KisLevelFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const { return new KisLevelConfigWidget(parent, dev); } KoColorTransformation* KisLevelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { if (!config) { warnKrita << "No configuration object for level filter\n"; return 0; } Q_ASSERT(config); int blackvalue = config->getInt("blackvalue"); int whitevalue = config->getInt("whitevalue", 255); double gammavalue = config->getDouble("gammavalue", 1.0); int outblackvalue = config->getInt("outblackvalue"); int outwhitevalue = config->getInt("outwhitevalue", 255); quint16 transfer[256]; for (int i = 0; i < 256; i++) { if (i <= blackvalue) transfer[i] = outblackvalue; else if (i < whitevalue) { double a = (double)(i - blackvalue) / (double)(whitevalue - blackvalue); a = (double)(outwhitevalue - outblackvalue) * pow(a, (1.0 / gammavalue)); transfer[i] = int(outblackvalue + a); } else transfer[i] = outwhitevalue; // TODO use floats instead of integer in the configuration transfer[i] = ((int)transfer[i] * 0xFFFF) / 0xFF ; } return cs->createBrightnessContrastAdjustment(transfer); } KisLevelConfigWidget::KisLevelConfigWidget(QWidget * parent, KisPaintDeviceSP dev) : KisConfigWidget(parent) { Q_ASSERT(dev); m_page.setupUi(this); m_page.ingradient->enableGamma(true); m_page.blackspin->setValue(0); m_page.whitespin->setValue(255); m_page.gammaspin->setValue(1.0); m_page.ingradient->slotModifyGamma(1.0); m_page.outblackspin->setValue(0); m_page.outwhitespin->setValue(255); connect(m_page.blackspin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page.whitespin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page.ingradient, SIGNAL(sigModifiedGamma(double)), SIGNAL(sigConfigurationItemChanged())); connect(m_page.blackspin, SIGNAL(valueChanged(int)), m_page.ingradient, SLOT(slotModifyBlack(int))); connect(m_page.whitespin, SIGNAL(valueChanged(int)), m_page.ingradient, SLOT(slotModifyWhite(int))); connect(m_page.gammaspin, SIGNAL(valueChanged(double)), m_page.ingradient, SLOT(slotModifyGamma(double))); connect(m_page.blackspin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyInWhiteLimit(int))); connect(m_page.whitespin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyInBlackLimit(int))); connect(m_page.ingradient, SIGNAL(sigModifiedBlack(int)), m_page.blackspin, SLOT(setValue(int))); connect(m_page.ingradient, SIGNAL(sigModifiedWhite(int)), m_page.whitespin, SLOT(setValue(int))); connect(m_page.ingradient, SIGNAL(sigModifiedGamma(double)), m_page.gammaspin, SLOT(setValue(double))); connect(m_page.outblackspin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page.outblackspin, SIGNAL(valueChanged(int)), m_page.outgradient, SLOT(slotModifyBlack(int))); connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), m_page.outgradient, SLOT(slotModifyWhite(int))); connect(m_page.outblackspin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyOutWhiteLimit(int))); connect(m_page.outwhitespin, SIGNAL(valueChanged(int)), this, SLOT(slotModifyOutBlackLimit(int))); connect(m_page.outgradient, SIGNAL(sigModifiedBlack(int)), m_page.outblackspin, SLOT(setValue(int))); connect(m_page.outgradient, SIGNAL(sigModifiedWhite(int)), m_page.outwhitespin, SLOT(setValue(int))); connect(m_page.butauto, SIGNAL(clicked(bool)), this, SLOT(slotAutoLevel(void))); + connect(m_page.butinvert, SIGNAL(clicked(bool)), this, SLOT(slotInvert(void))); connect((QObject*)(m_page.chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(slotDrawHistogram(bool))); KoHistogramProducer *producer = new KoGenericLabHistogramProducer(); m_histogram.reset( new KisHistogram(dev, dev->exactBounds(), producer, LINEAR) ); m_histlog = false; m_page.histview->resize(288,100); + m_inverted = false; slotDrawHistogram(); } KisLevelConfigWidget::~KisLevelConfigWidget() { } void KisLevelConfigWidget::slotDrawHistogram(bool logarithmic) { int wHeight = m_page.histview->height(); int wHeightMinusOne = wHeight - 1; int wWidth = m_page.histview->width(); if (m_histlog != logarithmic) { // Update the m_histogram if (logarithmic) m_histogram->setHistogramType(LOGARITHMIC); else m_histogram->setHistogramType(LINEAR); m_histlog = logarithmic; } QPalette appPalette = QApplication::palette(); QPixmap pix(wWidth-100, wHeight); pix.fill(QColor(appPalette.color(QPalette::Base))); QPainter p(&pix); p.setPen(QPen(Qt::gray, 1, Qt::SolidLine)); double highest = (double)m_histogram->calculations().getHighest(); qint32 bins = m_histogram->producer()->numberOfBins(); // use nearest neighbour interpolation if (m_histogram->getHistogramType() == LINEAR) { double factor = (double)(wHeight - wHeight / 5.0) / highest; for (int i = 0; i < wWidth; i++) { int binNo = qRound((double)i / wWidth * (bins - 1)); if ((int)m_histogram->getValue(binNo) != 0) p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - (int)m_histogram->getValue(binNo) * factor); } } else { double factor = (double)(wHeight - wHeight / 5.0) / (double)log(highest); for (int i = 0; i < wWidth; i++) { int binNo = qRound((double)i / wWidth * (bins - 1)) ; if ((int)m_histogram->getValue(binNo) != 0) p.drawLine(i, wHeightMinusOne, i, wHeightMinusOne - log((double)m_histogram->getValue(binNo)) * factor); } } m_page.histview->setPixmap(pix); } void KisLevelConfigWidget::slotModifyInBlackLimit(int limit) { m_page.blackspin->setMaximum(limit - 1); } void KisLevelConfigWidget::slotModifyInWhiteLimit(int limit) { m_page.whitespin->setMinimum(limit + 1); } void KisLevelConfigWidget::slotModifyOutBlackLimit(int limit) { - m_page.outblackspin->setMaximum(limit - 1); + if (m_inverted) { + m_page.outblackspin->setMinimum(limit + 1); + } else { + m_page.outblackspin->setMaximum(limit - 1); + } } void KisLevelConfigWidget::slotModifyOutWhiteLimit(int limit) { - m_page.outwhitespin->setMinimum(limit + 1); + if (m_inverted) { + m_page.outwhitespin->setMaximum(limit - 1); + } else { + m_page.outwhitespin->setMinimum(limit + 1); + } } void KisLevelConfigWidget::slotAutoLevel(void) { Q_ASSERT(m_histogram); qint32 num_bins = m_histogram->producer()->numberOfBins(); Q_ASSERT(num_bins > 1); int chosen_low_bin = 0, chosen_high_bin = num_bins-1; int count_thus_far = m_histogram->getValue(0); const int total_count = m_histogram->producer()->count(); const double threshold = 0.006; // find the low and hi point/bins based on summing count percentages // // this implementation is a port of GIMP's auto level implementation // (use a GPLv2 version as reference, specifically commit 51bfd07f18ef045a3e43632218fd92cae9ff1e48) for (int bin=0; bin<(num_bins-1); ++bin) { int next_count_thus_far = count_thus_far + m_histogram->getValue(bin+1); double this_percentage = static_cast(count_thus_far) / total_count; double next_percentage = static_cast(next_count_thus_far) / total_count; //dbgKrita << "bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage; if (fabs(this_percentage - threshold) < fabs(next_percentage - threshold)) { chosen_low_bin = bin; break; } count_thus_far = next_count_thus_far; } count_thus_far = m_histogram->getValue(num_bins-1); for (int bin=(num_bins-1); bin>0; --bin) { int next_count_thus_far = count_thus_far + m_histogram->getValue(bin-1); double this_percentage = static_cast(count_thus_far) / total_count; double next_percentage = static_cast(next_count_thus_far) / total_count; //dbgKrita << "hi-bin" << bin << "this_percentage" << this_percentage << "next_percentage" << next_percentage; if (fabs(this_percentage - threshold) < fabs(next_percentage - threshold)) { chosen_high_bin = bin; break; } count_thus_far = next_count_thus_far; } if (chosen_low_bin < chosen_high_bin) { m_page.blackspin->setValue(chosen_low_bin); m_page.ingradient->slotModifyBlack(chosen_low_bin); m_page.whitespin->setValue(chosen_high_bin); m_page.ingradient->slotModifyWhite(chosen_high_bin); } } +void KisLevelConfigWidget::slotInvert(void) +{ + m_inverted = !m_inverted; + int white = m_page.outwhitespin->value(); + int black = m_page.outblackspin->value(); + + resetOutSpinLimit(); + m_page.outgradient->setInverted(m_inverted); + m_page.outwhitespin->setValue(black); + m_page.outblackspin->setValue(white); +} + KisPropertiesConfigurationSP KisLevelConfigWidget::configuration() const { KisColorTransformationConfiguration * config = new KisColorTransformationConfiguration(KisLevelFilter::id().id(), 1); config->setProperty("blackvalue", m_page.blackspin->value()); config->setProperty("whitevalue", m_page.whitespin->value()); config->setProperty("gammavalue", m_page.gammaspin->value()); config->setProperty("outblackvalue", m_page.outblackspin->value()); config->setProperty("outwhitevalue", m_page.outwhitespin->value()); return config; } void KisLevelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { QVariant value; if (config->getProperty("blackvalue", value)) { m_page.blackspin->setValue(value.toUInt()); m_page.ingradient->slotModifyBlack(value.toUInt()); } if (config->getProperty("whitevalue", value)) { m_page.whitespin->setValue(value.toUInt()); m_page.ingradient->slotModifyWhite(value.toUInt()); } if (config->getProperty("gammavalue", value)) { m_page.gammaspin->setValue(value.toUInt()); m_page.ingradient->slotModifyGamma(value.toDouble()); } if (config->getProperty("outblackvalue", value)) { m_page.outblackspin->setValue(value.toUInt()); m_page.outgradient->slotModifyBlack(value.toUInt()); } if (config->getProperty("outwhitevalue", value)) { m_page.outwhitespin->setValue(value.toUInt()); m_page.outgradient->slotModifyWhite(value.toUInt()); } } + +void KisLevelConfigWidget::resetOutSpinLimit() { + if (m_inverted) { + m_page.outblackspin->setMaximum(255); + m_page.outwhitespin->setMinimum(0); + } else { + m_page.outblackspin->setMinimum(0); + m_page.outwhitespin->setMaximum(255); + } +} diff --git a/plugins/filters/levelfilter/kis_level_filter.h b/plugins/filters/levelfilter/kis_level_filter.h index 4d581d577b..6655fb2516 100644 --- a/plugins/filters/levelfilter/kis_level_filter.h +++ b/plugins/filters/levelfilter/kis_level_filter.h @@ -1,84 +1,88 @@ /* * This file is part of Krita * * Copyright (c) 2006 Frederic Coiffier * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_LEVEL_FILTER_H_ #define _KIS_LEVEL_FILTER_H_ #include "filter/kis_color_transformation_filter.h" #include "kis_config_widget.h" #include "ui_wdg_level.h" class WdgLevel; class QWidget; class KisHistogram; /** * This class affect Intensity Y of the image */ class KisLevelFilter : public KisColorTransformationFilter { public: KisLevelFilter(); ~KisLevelFilter() override; public: // virtual KisFilterConfigurationSP factoryConfiguration() const; KisConfigWidget * createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev) const override; KoColorTransformation* createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const override; static inline KoID id() { return KoID("levels", i18n("Levels")); } }; class KisLevelConfigWidget : public KisConfigWidget { Q_OBJECT public: KisLevelConfigWidget(QWidget * parent, KisPaintDeviceSP dev); ~KisLevelConfigWidget() override; KisPropertiesConfigurationSP configuration() const override; void setConfiguration(const KisPropertiesConfigurationSP config) override; Ui::WdgLevel m_page; protected Q_SLOTS: void slotDrawHistogram(bool logarithmic = false); void slotModifyInBlackLimit(int); void slotModifyInWhiteLimit(int); void slotModifyOutBlackLimit(int); void slotModifyOutWhiteLimit(int); void slotAutoLevel(void); + void slotInvert(void); + + void resetOutSpinLimit(); protected: QScopedPointer m_histogram; bool m_histlog; + bool m_inverted; }; #endif diff --git a/plugins/filters/levelfilter/wdg_level.ui b/plugins/filters/levelfilter/wdg_level.ui index faa3ea90a7..a873f063ee 100644 --- a/plugins/filters/levelfilter/wdg_level.ui +++ b/plugins/filters/levelfilter/wdg_level.ui @@ -1,344 +1,351 @@ WdgLevel 0 0 263 344 0 0 0 0 600 32767 Levels 0 0 0 0 <b>Input Levels</b> false Qt::RightToLeft Logarithmic 0 5 5 200 100 16777215 300 0 true Qt::AlignJustify|Qt::AlignVCenter false 0 -1 256 24 QAbstractSpinBox::PlusMinus 255 Qt::Horizontal QSizePolicy::MinimumExpanding 25 20 QAbstractSpinBox::PlusMinus 3 0.001000000000000 10.000000000000000 0.010000000000000 1.000000000000000 Qt::Horizontal QSizePolicy::MinimumExpanding 25 20 QAbstractSpinBox::PlusMinus 255 Qt::Vertical QSizePolicy::Fixed 0 0 <b>Output Levels</b> false 256 24 QAbstractSpinBox::PlusMinus 255 Qt::Horizontal QSizePolicy::MinimumExpanding 50 20 QAbstractSpinBox::PlusMinus 255 &Auto Levels + + + + &Invert + + + Qt::Horizontal 40 20 Qt::Vertical 20 0 KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
KisGradientSlider QWidget
kis_gradient_slider.h
KisDoubleParseSpinBox QDoubleSpinBox
kis_double_parse_spin_box.h
kis_gradient_slider.h kis_gradient_slider.h