diff --git a/libs/global/kis_global.h b/libs/global/kis_global.h index 6316973033..a2ca24bbc8 100644 --- a/libs/global/kis_global.h +++ b/libs/global/kis_global.h @@ -1,265 +1,269 @@ /* * Copyright (c) 2000 Matthias Elter * Copyright (c) 2002 Patrick Julien * * 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 KISGLOBAL_H_ #define KISGLOBAL_H_ #include #include #include "kis_assert.h" #include #include const quint8 quint8_MAX = UCHAR_MAX; const quint16 quint16_MAX = 65535; const qint32 qint32_MAX = (2147483647); const qint32 qint32_MIN = (-2147483647 - 1); const quint8 MAX_SELECTED = UCHAR_MAX; const quint8 MIN_SELECTED = 0; const quint8 SELECTION_THRESHOLD = 1; enum OutlineStyle { OUTLINE_NONE = 0, OUTLINE_CIRCLE, OUTLINE_FULL, OUTLINE_TILT, N_OUTLINE_STYLE_SIZE }; enum CursorStyle { CURSOR_STYLE_NO_CURSOR = 0, CURSOR_STYLE_TOOLICON, CURSOR_STYLE_POINTER, CURSOR_STYLE_SMALL_ROUND, CURSOR_STYLE_CROSSHAIR, CURSOR_STYLE_TRIANGLE_RIGHTHANDED, CURSOR_STYLE_TRIANGLE_LEFTHANDED, CURSOR_STYLE_BLACK_PIXEL, CURSOR_STYLE_WHITE_PIXEL, N_CURSOR_STYLE_SIZE }; enum OldCursorStyle { OLD_CURSOR_STYLE_TOOLICON = 0, OLD_CURSOR_STYLE_CROSSHAIR = 1, OLD_CURSOR_STYLE_POINTER = 2, OLD_CURSOR_STYLE_OUTLINE = 3, OLD_CURSOR_STYLE_NO_CURSOR = 4, OLD_CURSOR_STYLE_SMALL_ROUND = 5, OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT = 6, OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS = 7, OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED = 8, OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED = 9, OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED = 10, OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED = 11 }; const double PRESSURE_MIN = 0.0; const double PRESSURE_MAX = 1.0; const double PRESSURE_DEFAULT = PRESSURE_MAX; const double PRESSURE_THRESHOLD = 5.0 / 255.0; // copy of lcms.h #define INTENT_PERCEPTUAL 0 #define INTENT_RELATIVE_COLORIMETRIC 1 #define INTENT_SATURATION 2 #define INTENT_ABSOLUTE_COLORIMETRIC 3 #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // converts \p a to [0, 2 * M_PI) range -inline qreal normalizeAngle(qreal a) { - if (a < 0.0) { - a = 2 * M_PI + fmod(a, 2 * M_PI); +template +typename std::enable_if::value, T>::type +normalizeAngle(T a) { + if (a < T(0.0)) { + a = T(2 * M_PI) + std::fmod(a, T(2 * M_PI)); } - return a >= 2 * M_PI ? fmod(a, 2 * M_PI) : a; + return a >= T(2 * M_PI) ? std::fmod(a, T(2 * M_PI)) : a; } // converts \p a to [0, 360.0) range -inline qreal normalizeAngleDegrees(qreal a) { - if (a < 0.0) { - a = 360.0 + fmod(a, 360.0); +template +typename std::enable_if::value, T>::type +normalizeAngleDegrees(T a) { + if (a < T(0.0)) { + a = T(360.0) + std::fmod(a, T(360.0)); } - return a >= 360.0 ? fmod(a, 360.0) : a; + return a >= T(360.0) ? std::fmod(a, T(360.0)) : a; } inline qreal shortestAngularDistance(qreal a, qreal b) { qreal dist = fmod(qAbs(a - b), 2 * M_PI); if (dist > M_PI) dist = 2 * M_PI - dist; return dist; } inline qreal incrementInDirection(qreal a, qreal inc, qreal direction) { qreal b1 = a + inc; qreal b2 = a - inc; qreal d1 = shortestAngularDistance(b1, direction); qreal d2 = shortestAngularDistance(b2, direction); return d1 < d2 ? b1 : b2; } inline qreal bisectorAngle(qreal a, qreal b) { const qreal diff = shortestAngularDistance(a, b); return incrementInDirection(a, 0.5 * diff, b); } template inline PointType snapToClosestAxis(PointType P) { if (qAbs(P.x()) < qAbs(P.y())) { P.setX(0); } else { P.setY(0); } return P; } template inline T pow2(const T& x) { return x * x; } template inline T kisDegreesToRadians(T degrees) { return degrees * M_PI / 180.0; } template inline T kisRadiansToDegrees(T radians) { return radians * 180.0 / M_PI; } template inline T kisGrowRect(const T &rect, U offset) { return rect.adjusted(-offset, -offset, offset, offset); } inline qreal kisDistance(const QPointF &pt1, const QPointF &pt2) { return std::sqrt(pow2(pt1.x() - pt2.x()) + pow2(pt1.y() - pt2.y())); } inline qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2) { return pow2(pt1.x() - pt2.x()) + pow2(pt1.y() - pt2.y()); } #include inline qreal kisDistanceToLine(const QPointF &m, const QLineF &line) { const QPointF &p1 = line.p1(); const QPointF &p2 = line.p2(); qreal distance = 0; if (qFuzzyCompare(p1.x(), p2.x())) { distance = qAbs(m.x() - p2.x()); } else if (qFuzzyCompare(p1.y(), p2.y())) { distance = qAbs(m.y() - p2.y()); } else { qreal A = 1; qreal B = - (p1.x() - p2.x()) / (p1.y() - p2.y()); qreal C = - p1.x() - B * p1.y(); distance = qAbs(A * m.x() + B * m.y() + C) / std::sqrt(pow2(A) + pow2(B)); } return distance; } inline QPointF kisProjectOnVector(const QPointF &base, const QPointF &v) { const qreal prod = base.x() * v.x() + base.y() * v.y(); const qreal lengthSq = pow2(base.x()) + pow2(base.y()); qreal coeff = prod / lengthSq; return coeff * base; } #include inline QRect kisEnsureInRect(QRect rc, const QRect &bounds) { if(rc.right() > bounds.right()) { rc.translate(bounds.right() - rc.right(), 0); } if(rc.left() < bounds.left()) { rc.translate(bounds.left() - rc.left(), 0); } if(rc.bottom() > bounds.bottom()) { rc.translate(0, bounds.bottom() - rc.bottom()); } if(rc.top() < bounds.top()) { rc.translate(0, bounds.top() - rc.top()); } return rc; } #include "kis_pointer_utils.h" /** * A special wrapper object that converts Qt-style mutexes and locks * into an object that supports Std's (and Boost's) "Lockable" * concept. Basically, it converts tryLock() into try_lock() to comply * with the syntax. */ template struct StdLockableWrapper { StdLockableWrapper(T *lock) : m_lock(lock) {} void lock() { m_lock->lock(); } bool try_lock() { return m_lock->tryLock(); } void unlock() { m_lock->unlock(); } private: T *m_lock; }; #endif // KISGLOBAL_H_ diff --git a/plugins/color/colorspaceextensions/kis_hsv_adjustment.cpp b/plugins/color/colorspaceextensions/kis_hsv_adjustment.cpp index 51c4131ce6..ff9ad8e720 100644 --- a/plugins/color/colorspaceextensions/kis_hsv_adjustment.cpp +++ b/plugins/color/colorspaceextensions/kis_hsv_adjustment.cpp @@ -1,677 +1,972 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2 * of the License. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_hsv_adjustment.h" #include #ifdef HAVE_OPENEXR #include #endif #include #include #include #include #include #include #include #include #include #define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v ) #define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v ) template void clamp(float* r, float* g, float* b); #define FLOAT_CLAMP( v ) *v = (*v < 0.0) ? 0.0 : ( (*v>1.0) ? 1.0 : *v ) template<> void clamp(float* r, float* g, float* b) { FLOAT_CLAMP(r); FLOAT_CLAMP(g); FLOAT_CLAMP(b); } template<> void clamp(float* r, float* g, float* b) { FLOAT_CLAMP(r); FLOAT_CLAMP(g); FLOAT_CLAMP(b); } #ifdef HAVE_OPENEXR template<> void clamp(float* r, float* g, float* b) { Q_UNUSED(r); Q_UNUSED(g); Q_UNUSED(b); } #endif template<> void clamp(float* r, float* g, float* b) { Q_UNUSED(r); Q_UNUSED(g); Q_UNUSED(b); } +#include "kis_global.h" + +struct AddPolicy +{ + static inline float adjustValue(float v, float dv) { + return v + dv; + } +}; + + +struct MultiplyPolicy +{ + static inline float adjustValue(float v, float dv) { + if (dv < 0) { + v *= dv + 1.0f; + } else { + v += dv * (1.0f - v); + } + return v; + } +}; + +static inline void writeRGBSimple(float *r, float *g, float *b, + int sextant, + float x, float m, float M) +{ + switch (sextant) { + case 0: *r = M; *g = x + m; *b = m; break; + case 1: *r = x + m; *g = M; *b = m; break; + case 2: *r = m; *g = M; *b = x + m; break; + case 3: *r = m; *g = x + m; *b = M; break; + case 4: *r = x + m; *g = m; *b = M; break; + case 5: *r = M; *g = m; *b = x + m; break; + } +} + +struct HSVPolicy +{ + inline bool hasChroma(float v) { + static const float EPSILON = 1e-9f; + return v > EPSILON; + } + + inline float valueFromRGB(float r, float g, float b, float m, float M) { + Q_UNUSED(r); + Q_UNUSED(g); + Q_UNUSED(b); + Q_UNUSED(m); + return M; + } + + inline float fixupValueAfterChroma(float c, float v) { + return qMax(v, c); + } + + inline float fixupChromaAfterValue(float c, float v) { + return qMin(v, c); + } + + inline void writeRGB(float *r, float *g, float *b, + int sextant, + float x, float c, float v) { + + const float m = v - c; + writeRGBSimple(r, g, b, sextant, x, m, v); + } +}; + +struct HSLPolicy +{ + inline bool hasChroma(float v) { + static const float EPSILON = 1e-9f; + return v > EPSILON && v < 1.0f - EPSILON; + } + + inline float valueFromRGB(float r, float g, float b, float m, float M) { + Q_UNUSED(r); + Q_UNUSED(g); + Q_UNUSED(b); + return 0.5f * (M + m); + } + + inline float fixupValueAfterChroma(float c, float v) { + if (v >= 0.5f) { + if (c > 2.0f - 2.0f * v) { + v = 1.0f - 0.5f * c; + } + } else { + if (c > 2.0f * v) { + v = 0.5f * c; + } + } + return v; + } + + inline float fixupChromaAfterValue(float c, float v) { + if (v >= 0.5f) { + c = qMin(c, 2.0f - 2.0f * v); + } else { + c = qMin(c, 2.0f * v); + } + + return c; + } + + inline void writeRGB(float *r, float *g, float *b, + int sextant, + float x, float c, float v) { + + const float M = v + 0.5f * c; + const float m = v - 0.5f * c; + + writeRGBSimple(r, g, b, sextant, x, m, M); + } +}; + +struct HCIPolicy +{ + inline bool hasChroma(float v) { + static const float EPSILON = 1e-9f; + return v > EPSILON && v < 1.0f - EPSILON; + } + + inline float valueFromRGB(float r, float g, float b, float m, float M) { + Q_UNUSED(m); + Q_UNUSED(M); + return (r + g + b) / 3.0f; + } + + inline float fixupValueAfterChroma(float c, float v) { + static const float oneThird = 1.0f / 3.0f; + static const float twoThirds = 2.0f / 3.0f; + + if (v >= oneThird) { + v = qMin(v, 1.0f - twoThirds * c); + } else { + v = qMax(v, oneThird * c); + } + return v; + } + + inline float fixupChromaAfterValue(float c, float v) { + static const float oneThird = 1.0f / 3.0f; + + if (v >= oneThird) { + c = qMin(c, 1.5f * (1.0f - v)); + } else { + c = qMin(c, 3.0f * v); + } + + return c; + } + + inline void writeRGB(float *r, float *g, float *b, + int sextant, + float x, float c, float v) { + + static const float oneThird = 1.0f / 3.0f; + + const float m = v - oneThird * (c + x); + const float M = c + m; + + writeRGBSimple(r, g, b, sextant, x, m, M); + } +}; + +struct HCYPolicy +{ + HCYPolicy(float _rCoeff = 0.299f, float _gCoeff = 0.587f, float _bCoeff = 0.114f) + : rCoeff(_rCoeff), + gCoeff(_gCoeff), + bCoeff(_bCoeff) + { + } + + const float rCoeff = 0.299f; + const float gCoeff = 0.587f; + const float bCoeff = 0.114f; + + inline bool hasChroma(float v) { + static const float EPSILON = 1e-9f; + return v > EPSILON && v < 1.0f - EPSILON; + } + + inline float valueFromRGB(float r, float g, float b, float m, float M) { + Q_UNUSED(m); + Q_UNUSED(M); + return rCoeff * r + gCoeff * g + bCoeff * b; + } + + inline float fixupValueAfterChroma(float c, float v) { + // NOTE: no sliding in HCY, because the shape of the triangle + // depends on Hue, which complicated code a lot. And it + // seems to work fine without it :) + return v; + } + + inline float fixupChromaAfterValue(float c, float v) { + // NOTE: no sliding in HCY, because the shape of the triangle + // depends on Hue, which complicated code a lot. And it + // seems to work fine without it :) + return c; + } + + inline void writeRGB(float *r, float *g, float *b, + int sextant, + float x, float c, float v) { + + + switch (sextant) { + case 0: *r = c; *g = x; *b = 0; break; + case 1: *r = x; *g = c; *b = 0; break; + case 2: *r = 0; *g = c; *b = x; break; + case 3: *r = 0; *g = x; *b = c; break; + case 4: *r = x; *g = 0; *b = c; break; + case 5: *r = c; *g = 0; *b = x; break; + } + + const float m = v - *r * rCoeff - *g * gCoeff - *b * bCoeff; + *r += m; + *g += m; + *b += m; + } +}; + +template +void HSVTransform(float *r, float *g, float *b, float dh, float ds, float dv, ValuePolicy valuePolicy) +{ + static const float EPSILON = 1e-9f; + + float h; + + float M = qMax(*r, qMax(*g, *b)); + float m = qMin(*r, qMin(*g, *b)); + + float chroma = M - m; + + float v = valuePolicy.valueFromRGB(*r, *g, *b, m, M); + + if (!valuePolicy.hasChroma(v)) { + chroma = 0.0f; + h = 0.0f; + v = qBound(0.0f, ValueAdjustPolicy::adjustValue(v, dv), 1.0f); + } else { + if (chroma > EPSILON) { + if (*r == M) + h = (*g - *b) / chroma; + else if (*g == M) + h = 2 + (*b - *r) / chroma; + else + h = 4 + (*r - *g) / chroma; + + h *= 60; + h += dh * 180; + + h = normalizeAngleDegrees(h); + chroma = qBound(0.0f, chroma * (ds + 1.0f), 1.0f); + } else { + h = 0.0f; + } + + v = valuePolicy.fixupValueAfterChroma(chroma, v); + v = ValueAdjustPolicy::adjustValue(v, dv); + v = qBound(0.0f, v, 1.0f); + chroma = valuePolicy.fixupChromaAfterValue(chroma, v); + } + + if (v <= EPSILON) { + *r = *g = *b = 0.0; + } else { + h /= 60.0f; + const int sextant = static_cast(h); + const float fract = h - sextant; + + const float x = + sextant & 0x1 ? + chroma - chroma * fract : + chroma * fract; + + valuePolicy.writeRGB(r, g, b, sextant, x, chroma, v); + } +} + template class KisHSVAdjustment : public KoColorTransformation { typedef traits RGBTrait; typedef typename RGBTrait::Pixel RGBPixel; public: KisHSVAdjustment() : m_adj_h(0.0), m_adj_s(0.0), m_adj_v(0.0), m_lumaRed(0.0), m_lumaGreen(0.0), m_lumaBlue(0.0), m_type(0), - m_colorize(false) + m_colorize(false), + m_compatibilityMode(true) { } public: void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override { //if (m_model="RGBA" || m_colorize) { /*It'd be nice to have LCH automatically selector for LAB in the future, but I don't know how to select LAB * */ const RGBPixel* src = reinterpret_cast(srcU8); RGBPixel* dst = reinterpret_cast(dstU8); float h, s, v; float r = 0.0; float g = 0.0; float b = 0.0; qreal lumaR, lumaG, lumaB; //Default to rec 709 when there's no coefficients given// if (m_lumaRed<=0 || m_lumaGreen<=0 || m_lumaBlue<=0) { lumaR = 0.2126; lumaG = 0.7152; lumaB = 0.0722; } else { lumaR = m_lumaRed; lumaG = m_lumaGreen; lumaB = m_lumaBlue; } while (nPixels > 0) { if (m_colorize) { h = m_adj_h * 360; if (h >= 360.0) h = 0; s = m_adj_s; r = SCALE_TO_FLOAT(src->red); g = SCALE_TO_FLOAT(src->green); b = SCALE_TO_FLOAT(src->blue); float luminance = r * lumaR + g * lumaG + b * lumaB; if (m_adj_v > 0) { luminance *= (1.0 - m_adj_v); luminance += 1.0 - (1.0 - m_adj_v); } else if (m_adj_v < 0 ){ luminance *= (m_adj_v + 1.0); } v = luminance; HSLToRGB(h, s, v, &r, &g, &b); } else { if (m_type == 0) { - RGBToHSV(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v); - h += m_adj_h * 180; - if (h > 360) h -= 360; - if (h < 0) h += 360; - s += m_adj_s; - v += m_adj_v; - HSVToRGB(h, s, v, &r, &g, &b); + if (!m_compatibilityMode) { + r = SCALE_TO_FLOAT(src->red); + g = SCALE_TO_FLOAT(src->green); + b = SCALE_TO_FLOAT(src->blue); + HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HSVPolicy()); + } else { + RGBToHSV(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v); + h += m_adj_h * 180; + h = normalizeAngleDegrees(h); + s += m_adj_s; + v += m_adj_v; + HSVToRGB(h, s, v, &r, &g, &b); + } } else if (m_type == 1) { - RGBToHSL(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v); - - h += m_adj_h * 180; - if (h > 360) h -= 360; - if (h < 0) h += 360; - - s *= (m_adj_s + 1.0); - if (s < 0.0) s = 0.0; - if (s > 1.0) s = 1.0; - - if (m_adj_v < 0) - v *= (m_adj_v + 1.0); - else - v += (m_adj_v * (1.0 - v)); - - - HSLToRGB(h, s, v, &r, &g, &b); + if (!m_compatibilityMode) { + r = SCALE_TO_FLOAT(src->red); + g = SCALE_TO_FLOAT(src->green); + b = SCALE_TO_FLOAT(src->blue); + HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HSLPolicy()); + } else { + RGBToHSL(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v); + + h += m_adj_h * 180; + h = normalizeAngleDegrees(h); + s *= (m_adj_s + 1.0); + if (m_adj_v < 0) { + v *= (m_adj_v + 1.0); + } else { + v += (m_adj_v * (1.0 - v)); + } + HSLToRGB(h, s, v, &r, &g, &b); + } } else if (m_type == 2) { - qreal red = SCALE_TO_FLOAT(src->red); - qreal green = SCALE_TO_FLOAT(src->green); - qreal blue = SCALE_TO_FLOAT(src->blue); - qreal hue, sat, intensity; - RGBToHCI(red, green, blue, &hue, &sat, &intensity); - - hue *=360.0; - hue += m_adj_h * 180; - //if (intensity+m_adj_v>1.0){hue+=180.0;} - if (hue < 0) hue += 360; - hue = fmod(hue, 360.0); - - sat *= (m_adj_s + 1.0); - //sat = qBound(0.0, sat, 1.0); - - intensity += (m_adj_v); - - HCIToRGB(hue/360.0, sat, intensity, &red, &green, &blue); - - r = red; - g = green; - b = blue; + if (!m_compatibilityMode) { + r = SCALE_TO_FLOAT(src->red); + g = SCALE_TO_FLOAT(src->green); + b = SCALE_TO_FLOAT(src->blue); + HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HCIPolicy()); + } else { + qreal red = SCALE_TO_FLOAT(src->red); + qreal green = SCALE_TO_FLOAT(src->green); + qreal blue = SCALE_TO_FLOAT(src->blue); + qreal hue, sat, intensity; + RGBToHCI(red, green, blue, &hue, &sat, &intensity); + + hue *= 360.0; + hue += m_adj_h * 180; + hue = normalizeAngleDegrees(hue); + sat *= (m_adj_s + 1.0); + intensity += m_adj_v; + + HCIToRGB(hue/360.0, sat, intensity, &red, &green, &blue); + + r = red; + g = green; + b = blue; + } } else if (m_type == 3) { - qreal red = SCALE_TO_FLOAT(src->red); - qreal green = SCALE_TO_FLOAT(src->green); - qreal blue = SCALE_TO_FLOAT(src->blue); - qreal hue, sat, luma; - RGBToHCY(red, green, blue, &hue, &sat, &luma, lumaR, lumaG, lumaB); - - hue *=360.0; - hue += m_adj_h * 180; - //if (luma+m_adj_v>1.0){hue+=180.0;} - if (hue < 0) hue += 360; - hue = fmod(hue, 360.0); - - sat *= (m_adj_s + 1.0); - //sat = qBound(0.0, sat, 1.0); - - luma += m_adj_v; - - - HCYToRGB(hue/360.0, sat, luma, &red, &green, &blue, lumaR, lumaG, lumaB); - r = red; - g = green; - b = blue; + if (!m_compatibilityMode) { + r = SCALE_TO_FLOAT(src->red); + g = SCALE_TO_FLOAT(src->green); + b = SCALE_TO_FLOAT(src->blue); + HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HCYPolicy(lumaR, lumaG, lumaB)); + } else { + qreal red = SCALE_TO_FLOAT(src->red); + qreal green = SCALE_TO_FLOAT(src->green); + qreal blue = SCALE_TO_FLOAT(src->blue); + qreal hue, sat, luma; + RGBToHCY(red, green, blue, &hue, &sat, &luma, lumaR, lumaG, lumaB); + + hue *= 360.0; + hue += m_adj_h * 180; + hue = normalizeAngleDegrees(hue); + sat *= (m_adj_s + 1.0); + luma += m_adj_v; + + HCYToRGB(hue/360.0, sat, luma, &red, &green, &blue, lumaR, lumaG, lumaB); + r = red; + g = green; + b = blue; + } } else if (m_type == 4) { qreal red = SCALE_TO_FLOAT(src->red); qreal green = SCALE_TO_FLOAT(src->green); qreal blue = SCALE_TO_FLOAT(src->blue); qreal y, cb, cr; RGBToYUV(red, green, blue, &y, &cb, &cr, lumaR, lumaG, lumaB); cb *= (m_adj_h + 1.0); - //cb = qBound(0.0, cb, 1.0); - cr *= (m_adj_s + 1.0); - //cr = qBound(0.0, cr, 1.0); - y += (m_adj_v); - YUVToRGB(y, cb, cr, &red, &green, &blue, lumaR, lumaG, lumaB); r = red; g = green; b = blue; } else { Q_ASSERT_X(false, "", "invalid type"); } } clamp< _channel_type_ >(&r, &g, &b); dst->red = SCALE_FROM_FLOAT(r); dst->green = SCALE_FROM_FLOAT(g); dst->blue = SCALE_FROM_FLOAT(b); dst->alpha = src->alpha; --nPixels; ++src; ++dst; } /*} else if (m_model="LABA"){ const LABPixel* src = reinterpret_cast(srcU8); LABPixel* dst = reinterpret_cast(dstU8); qreal lightness = SCALE_TO_FLOAT(src->L); qreal a = SCALE_TO_FLOAT(src->a); qreal b = SCALE_TO_FLOAT(src->b); qreal L, C, H; while (nPixels > 0) { if (m_type = 4) { a *= (m_adj_h + 1.0); a = qBound(0.0, a, 1.0); b *= (m_adj_s + 1.0); b = qBound(0.0, b, 1.0); if (m_adj_v < 0) lightness *= (m_adj_v + 1.0); else lightness += (m_adj_v * (1.0 - lightness)); } else {//lch LABToLCH(lightness, a, b, &L, &C, &H); H *=360; H += m_adj_h * 180; if (H > 360) h -= 360; if (H < 0) h += 360; C += m_adj_s; C = qBound(0.0,C,1.0); L += m_adj_v; L = qBound(0.0,L,1.0); LCHToLAB(L, C, H/360.0, &lightness, &a, &b); } clamp< _channel_type_ >(&lightness, &a, &b); dst->L = SCALE_FROM_FLOAT(lightness); dst->a = SCALE_FROM_FLOAT(a); dst->b = SCALE_FROM_FLOAT(b); dst->alpha = src->alpha; --nPixels; ++src; ++dst; } }*/ } QList parameters() const override { QList list; - list << "h" << "s" << "v" << "type" << "colorize" << "lumaRed" << "lumaGreen"<< "lumaBlue"; + list << "h" << "s" << "v" << "type" << "colorize" << "lumaRed" << "lumaGreen"<< "lumaBlue" << "compatibilityMode"; return list; } int parameterId(const QString& name) const override { if (name == "h") { return 0; } else if (name == "s") { return 1; } else if (name == "v") { return 2; } else if (name == "type") { return 3; } else if (name == "colorize") { return 4; } else if (name == "lumaRed") { return 5; } else if (name == "lumaGreen") { return 6; } else if (name == "lumaBlue") { return 7; + } else if (name == "compatibilityMode") { + return 8; } return -1; } /** * name - "h", "s" or "v" * (h)ue in range <-1.0, 1.0> ( for user, show as -180, 180 or 0, 360 for colorize) * (s)aturation in range <-1.0, 1.0> ( for user, show -100, 100, or 0, 100 for colorize) * (v)alue in range <-1.0, 1.0> (for user, show -100, 100) * type: 0:HSV, 1:HSL, 2:HSI, 3:HSY, 4:YUV * m_colorize: Use colorize formula instead * luma Red/Green/Blue: Used for luma calculations. */ void setParameter(int id, const QVariant& parameter) override { switch(id) { case 0: m_adj_h = parameter.toDouble(); break; case 1: m_adj_s = parameter.toDouble(); break; case 2: m_adj_v = parameter.toDouble(); break; case 3: m_type = parameter.toInt(); break; case 4: m_colorize = parameter.toBool(); break; case 5: m_lumaRed = parameter.toDouble(); break; case 6: m_lumaGreen = parameter.toDouble(); break; case 7: m_lumaBlue = parameter.toDouble(); break; + case 8: + m_compatibilityMode = parameter.toBool(); + break; default: KIS_ASSERT_RECOVER_NOOP(false && "Unknown parameter ID. Ignored!"); ; } } private: double m_adj_h, m_adj_s, m_adj_v; qreal m_lumaRed, m_lumaGreen, m_lumaBlue; int m_type; bool m_colorize; + bool m_compatibilityMode; }; template class KisHSVCurveAdjustment : public KoColorTransformation { typedef traits RGBTrait; typedef typename RGBTrait::Pixel RGBPixel; public: KisHSVCurveAdjustment() : m_lumaRed(0.0), m_lumaGreen(0.0), m_lumaBlue(0.0) {} QList parameters() const override { QList list; list << "curve" << "channel" << "driverChannel" << "relative" << "lumaRed" << "lumaGreen"<< "lumaBlue"; return list; } int parameterId(const QString& name) const override { if (name == "curve") { return PAR_CURVE; } else if (name == "channel") { return PAR_CHANNEL; } else if (name == "driverChannel") { return PAR_DRIVER_CHANNEL; } else if (name == "relative") { return PAR_RELATIVE; } else if (name == "lumaRed") { return PAR_LUMA_R; } else if (name == "lumaGreen") { return PAR_LUMA_G; } else if (name == "lumaBlue") { return PAR_LUMA_B; } return -1; } /** * curve: adjustment curve as QVector * channel: which channel to adjust. See KisHSVCurve::ColorChannel. * driverChannel: which channel to use as source for adjustments. * relative: * false: use curve for direct lookup. * true: add adjustment to original. In this mode, the curve range is mapped to -1.0 to 1.0 * luma Red/Green/Blue: Used for luma calculations. */ void setParameter(int id, const QVariant& parameter) override { switch(id) { case PAR_CURVE: m_curve = parameter.value>(); break; case PAR_CHANNEL: case PAR_DRIVER_CHANNEL: { int channel = parameter.toInt(); KIS_ASSERT_RECOVER_RETURN(0 <= channel && channel < KisHSVCurve::ChannelCount && "Invalid channel. Ignored!"); if (id == PAR_CHANNEL) { m_channel = channel; } else { m_driverChannel = channel; } } break; case PAR_RELATIVE: m_relative = parameter.toBool(); break; case PAR_LUMA_R: m_lumaRed = parameter.toDouble(); break; case PAR_LUMA_G: m_lumaGreen = parameter.toDouble(); break; case PAR_LUMA_B: m_lumaBlue = parameter.toDouble(); break; default: KIS_ASSERT_RECOVER_NOOP(false && "Unknown parameter ID. Ignored!"); } } const float SCALE_FROM_16BIT = 1.0f / 0xFFFF; void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override { const RGBPixel* src = reinterpret_cast(srcU8); RGBPixel* dst = reinterpret_cast(dstU8); float max = m_curve.size() - 1; int driverChannel = m_relative ? m_driverChannel : m_channel; float component[KisHSVCurve::ChannelCount]; // Aliases for convenience float &h = component[KisHSVCurve::Hue]; float &s = component[KisHSVCurve::Saturation]; float &v = component[KisHSVCurve::Value]; float &r = component[KisHSVCurve::Red]; float &g = component[KisHSVCurve::Green]; float &b = component[KisHSVCurve::Blue]; float &a = component[KisHSVCurve::Alpha]; while (nPixels > 0) { r = SCALE_TO_FLOAT(src->red); g = SCALE_TO_FLOAT(src->green); b = SCALE_TO_FLOAT(src->blue); a = SCALE_TO_FLOAT(src->alpha); RGBToHSV(r, g, b, &h, &s, &v); // Normalize hue to 0.0 to 1.0 range h /= 360.0f; float adjustment = lookupComponent(component[driverChannel], max) * SCALE_FROM_16BIT; if (m_relative) { // Curve uses range 0.0 to 1.0, but for adjustment we need -1.0 to 1.0 adjustment = 2.0f * adjustment - 1.0f; if (m_channel == KisHSVCurve::AllColors) { r += adjustment; g += adjustment; b += adjustment; } else { component[m_channel] += adjustment; } } else { if (m_channel == KisHSVCurve::AllColors) { r = b = g = adjustment; } else { component[m_channel] = adjustment; } } h *= 360.0f; if (h > 360) h -= 360; if (h < 0) h += 360; if (m_channel >= KisHSVCurve::Hue) { HSVToRGB(h, s, v, &r, &g, &b); } clamp< _channel_type_ >(&r, &g, &b); FLOAT_CLAMP(&a); dst->red = SCALE_FROM_FLOAT(r); dst->green = SCALE_FROM_FLOAT(g); dst->blue = SCALE_FROM_FLOAT(b); dst->alpha = SCALE_FROM_FLOAT(a); --nPixels; ++src; ++dst; } } float lookupComponent(float x, float max) const { // No curve for this component? Pass through unmodified if (max < 2) return x; if (x < 0) return m_curve[0]; float lookup = x * max; float base = floor(lookup); float offset = lookup - base; if (base >= max) { base = max - 1.0f; offset = 1.0f; } int index = (int)base; return (1.0f - offset) * m_curve[index] + offset * m_curve[index + 1]; } private: enum ParameterID { PAR_CURVE, PAR_CHANNEL, PAR_DRIVER_CHANNEL, PAR_RELATIVE, PAR_LUMA_R, PAR_LUMA_G, PAR_LUMA_B, }; QVector m_curve; int m_channel = 0; int m_driverChannel = 0; bool m_relative = false; /* Note: the filter currently only supports HSV, so these are * unused, but will be needed once HSL, etc. */ qreal m_lumaRed, m_lumaGreen, m_lumaBlue; }; KisHSVAdjustmentFactory::KisHSVAdjustmentFactory() : KoColorTransformationFactory("hsv_adjustment") { } QList< QPair< KoID, KoID > > KisHSVAdjustmentFactory::supportedModels() const { QList< QPair< KoID, KoID > > l; l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID)); return l; } KoColorTransformation* KisHSVAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash parameters) const { KoColorTransformation * adj; if (colorSpace->colorModelId() != RGBAColorModelID) { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation"; return 0; } if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) { adj = new KisHSVAdjustment< quint8, KoBgrTraits < quint8 > >(); } else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) { adj = new KisHSVAdjustment< quint16, KoBgrTraits < quint16 > >(); } #ifdef HAVE_OPENEXR else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) { adj = new KisHSVAdjustment< half, KoRgbTraits < half > >(); } #endif else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) { adj = new KisHSVAdjustment< float, KoRgbTraits < float > >(); } else { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation"; return 0; } adj->setParameters(parameters); return adj; } KisHSVCurveAdjustmentFactory::KisHSVCurveAdjustmentFactory() : KoColorTransformationFactory("hsv_curve_adjustment") { } QList< QPair< KoID, KoID > > KisHSVCurveAdjustmentFactory::supportedModels() const { QList< QPair< KoID, KoID > > l; l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID)); l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID)); return l; } KoColorTransformation* KisHSVCurveAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash parameters) const { KoColorTransformation * adj; if (colorSpace->colorModelId() != RGBAColorModelID) { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVCurveAdjustmentFactory::createTransformation"; return 0; } if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) { adj = new KisHSVCurveAdjustment< quint8, KoBgrTraits < quint8 > >(); } else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) { adj = new KisHSVCurveAdjustment< quint16, KoBgrTraits < quint16 > >(); } #ifdef HAVE_OPENEXR else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) { adj = new KisHSVCurveAdjustment< half, KoRgbTraits < half > >(); } #endif else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) { adj = new KisHSVCurveAdjustment< float, KoRgbTraits < float > >(); } else { dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVCurveAdjustmentFactory::createTransformation"; return 0; } adj->setParameters(parameters); return adj; } diff --git a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp index 4cc5d12ac2..c16bf8f0f7 100644 --- a/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp +++ b/plugins/filters/colorsfilters/kis_hsv_adjustment_filter.cpp @@ -1,208 +1,221 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2 * of the License. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_hsv_adjustment_filter.h" #include #include #include #include #include #include namespace { struct SliderConfig { QString m_text; int m_minimum; int m_maximum; inline void apply(QSpinBox* spinBox, QSlider* slider, QLabel* label) const { label->setText(m_text); slider->setMinimum(m_minimum); slider->setMaximum(m_maximum); spinBox->setMinimum(m_minimum); spinBox->setMaximum(m_maximum); int sliderValue = slider->value(); if (sliderValue < m_minimum || sliderValue > m_maximum) { slider->setValue((m_minimum + m_maximum) / 2); } } inline double normalize(int value) const { return (double)value / (double)m_maximum; } inline void resetSlider( QSlider* slider) const { slider->setValue(0); } }; struct WidgetSlidersConfig { SliderConfig m_sliders[3]; }; #define PERCENT_FIELD_REL(x) {x, -100, 100} #define PERCENT_FIELD_ABS(x) {x, 0, 100} #define DEGREES_FIELD_REL(x) {x, -180, 180} #define DEGREES_FIELD_ABS(x) {x, 0, 360} #define HSX_CONFIGS(x) { \ { {DEGREES_FIELD_REL(i18n("Hue:")), PERCENT_FIELD_REL(i18n("Saturation:")), PERCENT_FIELD_REL(x)} }, \ { {DEGREES_FIELD_ABS(i18n("Hue:")), PERCENT_FIELD_ABS(i18n("Saturation:")), PERCENT_FIELD_REL(x)} } \ } const WidgetSlidersConfig WIDGET_CONFIGS[][2] = { // Hue/Saturation/Value HSX_CONFIGS(i18n("Value:")), // Hue/Saturation/Lightness HSX_CONFIGS(i18n("Lightness:")), // Hue/Saturation/Intensity HSX_CONFIGS(i18n("Intensity:")), // Hue/Saturation/Luminosity HSX_CONFIGS(i18n("Luma:")), // Blue Chroma/Red Chroma/Luma {{ {PERCENT_FIELD_REL(i18n("Yellow-Blue:")), PERCENT_FIELD_REL(i18n("Green-Red:")), PERCENT_FIELD_REL(i18n("Luma:"))} }, { {PERCENT_FIELD_ABS(i18n("Yellow-Blue:")), PERCENT_FIELD_ABS(i18n("Green-Red:")), PERCENT_FIELD_REL(i18n("Luma:"))} }} }; inline const WidgetSlidersConfig& getCurrentWidgetConfig(int type, bool colorize) { return WIDGET_CONFIGS[type][colorize ? 1 : 0]; } } KisHSVAdjustmentFilter::KisHSVAdjustmentFilter() : KisColorTransformationFilter(id(), FiltersCategoryAdjustId, i18n("&HSV Adjustment...")) { setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U)); setSupportsPainting(true); } KisConfigWidget * KisHSVAdjustmentFilter::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const { Q_UNUSED(dev); return new KisHSVConfigWidget(parent); } KoColorTransformation* KisHSVAdjustmentFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { QHash params; if (config) { int type = config->getInt("type", 1); bool colorize = config->getBool("colorize", false); + bool compatibilityMode = config->getBool("compatibilityMode", true); const WidgetSlidersConfig& widgetConfig = getCurrentWidgetConfig(type, colorize); params["h"] = widgetConfig.m_sliders[0].normalize(config->getInt("h", 0)); params["s"] = widgetConfig.m_sliders[1].normalize(config->getInt("s", 0)); params["v"] = widgetConfig.m_sliders[2].normalize(config->getInt("v", 0)); params["type"] = type; params["colorize"] = colorize; params["lumaRed"] = cs->lumaCoefficients()[0]; params["lumaGreen"] = cs->lumaCoefficients()[1]; params["lumaBlue"] = cs->lumaCoefficients()[2]; + params["compatibilityMode"] = compatibilityMode; } return cs->createColorTransformation("hsv_adjustment", params); } KisFilterConfigurationSP KisHSVAdjustmentFilter::factoryConfiguration() const { KisColorTransformationConfigurationSP config = new KisColorTransformationConfiguration(id().id(), 1); config->setProperty("h", 0); config->setProperty("s", 0); config->setProperty("v", 0); config->setProperty("type", 1); config->setProperty("colorize", false); + config->setProperty("compatibilityMode", false); return config; } KisHSVConfigWidget::KisHSVConfigWidget(QWidget * parent, Qt::WindowFlags f) : KisConfigWidget(parent, f) { m_page = new Ui_WdgHSVAdjustment(); m_page->setupUi(this); connect(m_page->cmbType, SIGNAL(activated(int)), this, SLOT(configureSliderLimitsAndLabels())); connect(m_page->chkColorize, SIGNAL(toggled(bool)), this, SLOT(configureSliderLimitsAndLabels())); + connect(m_page->chkCompatibilityMode, SIGNAL(toggled(bool)), SIGNAL(sigConfigurationItemChanged())); + connect(m_page->reset,SIGNAL(clicked(bool)),this,SLOT(resetFilter())); // connect horizontal sliders connect(m_page->hueSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->saturationSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->valueSlider, SIGNAL(valueChanged(int)), SIGNAL(sigConfigurationItemChanged())); connect(m_page->hueSpinBox, SIGNAL(valueChanged(int)), m_page->hueSlider, SLOT(setValue(int))); connect(m_page->saturationSpinBox, SIGNAL(valueChanged(int)), m_page->saturationSlider, SLOT(setValue(int))); connect(m_page->valueSpinBox, SIGNAL(valueChanged(int)), m_page->valueSlider, SLOT(setValue(int))); connect(m_page->hueSlider, SIGNAL(valueChanged(int)), m_page->hueSpinBox, SLOT(setValue(int))); connect(m_page->saturationSlider, SIGNAL(valueChanged(int)), m_page->saturationSpinBox, SLOT(setValue(int))); connect(m_page->valueSlider, SIGNAL(valueChanged(int)), m_page->valueSpinBox, SLOT(setValue(int))); } KisHSVConfigWidget::~KisHSVConfigWidget() { delete m_page; } KisPropertiesConfigurationSP KisHSVConfigWidget::configuration() const { KisColorTransformationConfigurationSP c = new KisColorTransformationConfiguration(KisHSVAdjustmentFilter::id().id(), 0); c->setProperty("h", m_page->hueSlider->value()); c->setProperty("s", m_page->saturationSlider->value()); c->setProperty("v", m_page->valueSlider->value()); c->setProperty("type", m_page->cmbType->currentIndex()); c->setProperty("colorize", m_page->chkColorize->isChecked()); + c->setProperty("compatibilityMode", m_page->chkCompatibilityMode->isChecked()); return c; } void KisHSVConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { m_page->cmbType->setCurrentIndex(config->getInt("type", 1)); m_page->chkColorize->setChecked(config->getBool("colorize", false)); m_page->hueSlider->setValue(config->getInt("h", 0)); m_page->saturationSlider->setValue(config->getInt("s", 0)); m_page->valueSlider->setValue(config->getInt("v", 0)); + m_page->chkCompatibilityMode->setChecked(config->getInt("compatibilityMode", true)); configureSliderLimitsAndLabels(); } void KisHSVConfigWidget::configureSliderLimitsAndLabels() { const WidgetSlidersConfig& widget = getCurrentWidgetConfig(m_page->cmbType->currentIndex(), m_page->chkColorize->isChecked()); widget.m_sliders[0].apply(m_page->hueSpinBox, m_page->hueSlider, m_page->label); widget.m_sliders[1].apply(m_page->saturationSpinBox, m_page->saturationSlider, m_page->label_2); widget.m_sliders[2].apply(m_page->valueSpinBox, m_page->valueSlider, m_page->label_3); + const bool compatibilityEnabled = + !m_page->chkColorize->isChecked() && + m_page->cmbType->currentIndex() >= 0 && m_page->cmbType->currentIndex() <= 3; + + m_page->chkCompatibilityMode->setEnabled(compatibilityEnabled); + emit sigConfigurationItemChanged(); } void KisHSVConfigWidget::resetFilter() { const WidgetSlidersConfig& widget = getCurrentWidgetConfig(m_page->cmbType->currentIndex(), m_page->chkColorize->isChecked()); widget.m_sliders[0].resetSlider(m_page->hueSlider); widget.m_sliders[1].resetSlider(m_page->saturationSlider); widget.m_sliders[2].resetSlider(m_page->valueSlider); } diff --git a/plugins/filters/colorsfilters/wdg_hsv_adjustment.ui b/plugins/filters/colorsfilters/wdg_hsv_adjustment.ui index d88926d9ba..2b17c3ccc2 100644 --- a/plugins/filters/colorsfilters/wdg_hsv_adjustment.ui +++ b/plugins/filters/colorsfilters/wdg_hsv_adjustment.ui @@ -1,254 +1,261 @@ WdgHSVAdjustment 0 0 412 188 0 0 0 0 5 Qt::Vertical 20 0 &Type: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter cmbType &Colorize 0 Hue/Saturation/Value Hue/Saturation/Lightness Hue/Saturation/Intensity Hue/Saturation/Luma Blue Chroma/Red Chroma/Luma -180 180 Qt::Horizontal -100 100 &Value: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter valueSlider -100 100 0 true Qt::Horizontal false false QSlider::NoTicks 0 0 &Saturation: false Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter saturationSlider 0 0 &Hue: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter hueSlider -100 100 Qt::Horizontal -100 100 -180 180 Reset + + + + Compatibility Mode + + + KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
cmbType chkColorize