File indexing completed on 2024-05-19 04:29:24

0001 /*
0002  *  SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2011 Sven Langkamp <sven.langkamp@gmail.com>
0004  *  SPDX-FileCopyrightText: 2015 Moritz Molch <kde@moritzmolch.de>
0005  *
0006  *  SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #include "kis_color_input.h"
0010 
0011 #include <KoConfig.h>
0012 #ifdef HAVE_OPENEXR
0013 #include <half.h>
0014 #endif
0015 
0016 #include <cmath>
0017 
0018 #include <kis_debug.h>
0019 
0020 #include <QHBoxLayout>
0021 #include <QLabel>
0022 #include <QLineEdit>
0023 #include <QSpinBox>
0024 #include <QDoubleSpinBox>
0025 
0026 #include <klocalizedstring.h>
0027 
0028 #include <KoChannelInfo.h>
0029 #include <KoColor.h>
0030 #include <KoColorSlider.h>
0031 #include <KoColorSpace.h>
0032 #include <KisHsvColorSlider.h>
0033 #include <KoColorConversions.h>
0034 #include <KisSpinBoxI18nHelper.h>
0035 
0036 #include "kis_double_parse_spin_box.h"
0037 #include "kis_int_parse_spin_box.h"
0038 #include "kis_signals_blocker.h"
0039 
0040 KisColorInput::KisColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
0041     QWidget(parent), m_channelInfo(channelInfo), m_color(color), m_displayRenderer(displayRenderer),
0042     m_usePercentage(usePercentage)
0043 {
0044 }
0045 
0046 void KisColorInput::init()
0047 {
0048     QHBoxLayout* m_layout = new QHBoxLayout(this);
0049     m_layout->setContentsMargins(0,0,0,0);
0050     m_layout->setSpacing(1);
0051 
0052     QLabel* m_label = new QLabel(i18n("%1:", m_channelInfo->name()), this);
0053     m_layout->addWidget(m_label);
0054 
0055     m_colorSlider = new KoColorSlider(Qt::Horizontal, this, m_displayRenderer);
0056     m_colorSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0057     m_layout->addWidget(m_colorSlider);
0058 
0059     QWidget* m_input = createInput();
0060     m_layout->addWidget(m_input);
0061 }
0062 
0063 KisIntegerColorInput::KisIntegerColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
0064     KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage)
0065 {
0066     init();
0067 }
0068 
0069 void KisIntegerColorInput::setValue(int v)
0070 {
0071     quint8* data = m_color->data() + m_channelInfo->pos();
0072     switch (m_channelInfo->channelValueType()) {
0073     case KoChannelInfo::UINT8:
0074         *(reinterpret_cast<quint8*>(data)) = v;
0075         break;
0076     case KoChannelInfo::UINT16:
0077         *(reinterpret_cast<quint16*>(data)) = v;
0078         break;
0079     case KoChannelInfo::UINT32:
0080         *(reinterpret_cast<quint32*>(data)) = v;
0081         break;
0082     default:
0083         Q_ASSERT(false);
0084     }
0085     emit(updated());
0086 }
0087 
0088 void KisIntegerColorInput::update()
0089 {
0090     KoColor min = *m_color;
0091     KoColor max = *m_color;
0092     quint8* data = m_color->data() + m_channelInfo->pos();
0093     quint8* dataMin = min.data() + m_channelInfo->pos();
0094     quint8* dataMax = max.data() + m_channelInfo->pos();
0095     m_intNumInput->blockSignals(true);
0096     m_colorSlider->blockSignals(true);
0097     switch (m_channelInfo->channelValueType()) {
0098     case KoChannelInfo::UINT8:
0099         if (m_usePercentage) {
0100             m_intNumInput->setMaximum(100);
0101             m_intNumInput->setValue(round(*(reinterpret_cast<quint8*>(data))*1.0 / 255.0 * 100.0));
0102         } else {
0103             m_intNumInput->setMaximum(0xFF);
0104             m_intNumInput->setValue(*(reinterpret_cast<quint8*>(data)));
0105         }
0106         m_colorSlider->setValue(*(reinterpret_cast<quint8*>(data)));
0107         *(reinterpret_cast<quint8*>(dataMin)) = 0x0;
0108         *(reinterpret_cast<quint8*>(dataMax)) = 0xFF;
0109         break;
0110     case KoChannelInfo::UINT16:
0111         if (m_usePercentage) {
0112             m_intNumInput->setMaximum(100);
0113             m_intNumInput->setValue(round(*(reinterpret_cast<quint16*>(data))*1.0 / 65535.0 * 100.0));
0114         } else {
0115             m_intNumInput->setMaximum(0xFFFF);
0116             m_intNumInput->setValue(*(reinterpret_cast<quint16*>(data)));
0117         }
0118         m_colorSlider->setValue(*(reinterpret_cast<quint16*>(data)));
0119         *(reinterpret_cast<quint16*>(dataMin)) = 0x0;
0120         *(reinterpret_cast<quint16*>(dataMax)) = 0xFFFF;
0121         break;
0122     case KoChannelInfo::UINT32:
0123         if (m_usePercentage) {
0124             m_intNumInput->setMaximum(100);
0125             m_intNumInput->setValue(round(*(reinterpret_cast<quint32*>(data))*1.0 / 4294967295.0 * 100.0));
0126         } else {
0127             m_intNumInput->setMaximum(0xFFFF);
0128             m_intNumInput->setValue(*(reinterpret_cast<quint32*>(data)));
0129         }
0130         m_colorSlider->setValue(*(reinterpret_cast<quint32*>(data)));
0131         *(reinterpret_cast<quint32*>(dataMin)) = 0x0;
0132         *(reinterpret_cast<quint32*>(dataMax)) = 0xFFFFFFFF;
0133         break;
0134     default:
0135         Q_ASSERT(false);
0136     }
0137     m_colorSlider->setColors(min, max);
0138     m_intNumInput->blockSignals(false);
0139     m_colorSlider->blockSignals(false);
0140 }
0141 
0142 QWidget* KisIntegerColorInput::createInput()
0143 {
0144     m_intNumInput = new KisIntParseSpinBox(this);
0145     m_intNumInput->setMinimum(0);
0146     m_colorSlider->setMinimum(0);
0147 
0148     if (m_usePercentage) {
0149         KisSpinBoxI18nHelper::setText(m_intNumInput, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
0150     }
0151 
0152     switch (m_channelInfo->channelValueType()) {
0153     case KoChannelInfo::UINT8:
0154         if (m_usePercentage) {
0155             m_intNumInput->setMaximum(100);
0156         } else {
0157             m_intNumInput->setMaximum(0xFF);
0158         }
0159         m_colorSlider->setMaximum(0xFF);
0160         break;
0161     case KoChannelInfo::UINT16:
0162         if (m_usePercentage) {
0163             m_intNumInput->setMaximum(100);
0164         } else {
0165             m_intNumInput->setMaximum(0xFFFF);
0166         }
0167         m_colorSlider->setMaximum(0xFFFF);
0168         break;
0169     case KoChannelInfo::UINT32:
0170         if (m_usePercentage) {
0171             m_intNumInput->setMaximum(100);
0172         } else {
0173             m_intNumInput->setMaximum(0xFFFFFFFF);
0174         }
0175         m_colorSlider->setMaximum(0xFFFFFFFF);
0176         break;
0177     default:
0178         Q_ASSERT(false);
0179     }
0180     connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(onColorSliderChanged(int)));
0181     connect(m_intNumInput, SIGNAL(valueChanged(int)), this, SLOT(onNumInputChanged(int)));
0182     return m_intNumInput;
0183 }
0184 
0185 void KisIntegerColorInput::setPercentageWise(bool val)
0186 {
0187     m_usePercentage = val;
0188 
0189     if (m_usePercentage) {
0190         KisSpinBoxI18nHelper::setText(m_intNumInput, i18nc("{n} is the number value, % is the percent sign", "{n}%"));
0191     } else {
0192         m_intNumInput->setPrefix("");
0193         m_intNumInput->setSuffix("");
0194     }
0195 }
0196 
0197 void KisIntegerColorInput::onColorSliderChanged(int val)
0198 {
0199     m_intNumInput->blockSignals(true);
0200     if (m_usePercentage) {
0201         switch (m_channelInfo->channelValueType()) {
0202         case KoChannelInfo::UINT8:
0203             m_intNumInput->setValue(round((val*1.0) / 255.0 * 100.0));
0204             break;
0205         case KoChannelInfo::UINT16:
0206             m_intNumInput->setValue(round((val*1.0) / 65535.0 * 100.0));
0207             break;
0208         case KoChannelInfo::UINT32:
0209             m_intNumInput->setValue(round((val*1.0) / 4294967295.0 * 100.0));
0210             break;
0211         default:
0212             Q_ASSERT(false);
0213         }
0214     } else {
0215         m_intNumInput->setValue(val);
0216     }
0217     m_intNumInput->blockSignals(false);
0218     setValue(val);
0219 }
0220 
0221 void KisIntegerColorInput::onNumInputChanged(int val)
0222 {
0223     m_colorSlider->blockSignals(true);
0224     if (m_usePercentage) {
0225         switch (m_channelInfo->channelValueType()) {
0226         case KoChannelInfo::UINT8:
0227             m_colorSlider->setValue((val*1.0)/100.0 * 255.0);
0228             m_colorSlider->blockSignals(false);
0229             setValue((val*1.0)/100.0 * 255.0);
0230             break;
0231         case KoChannelInfo::UINT16:
0232             m_colorSlider->setValue((val*1.0)/100.0 * 65535.0);
0233             m_colorSlider->blockSignals(false);
0234             setValue((val*1.0)/100.0 * 65535.0);
0235             break;
0236         case KoChannelInfo::UINT32:
0237             m_colorSlider->setValue((val*1.0)/100.0 * 4294967295.0);
0238             m_colorSlider->blockSignals(false);
0239             setValue((val*1.0)/100.0 * 4294967295.0);
0240             break;
0241         default:
0242             Q_ASSERT(false);
0243         }
0244     } else {
0245         m_colorSlider->setValue(val);
0246         m_colorSlider->blockSignals(false);
0247         setValue(val);
0248     }
0249 }
0250 
0251 KisFloatColorInput::KisFloatColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
0252     KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage)
0253 {
0254     init();
0255 }
0256 
0257 void KisFloatColorInput::setValue(double v)
0258 {
0259     quint8* data = m_color->data() + m_channelInfo->pos();
0260     switch (m_channelInfo->channelValueType()) {
0261 #ifdef HAVE_OPENEXR
0262     case KoChannelInfo::FLOAT16:
0263         *(reinterpret_cast<half*>(data)) = v;
0264         break;
0265 #endif
0266     case KoChannelInfo::FLOAT32:
0267         *(reinterpret_cast<float*>(data)) = v;
0268         break;
0269     default:
0270         Q_ASSERT(false);
0271     }
0272     emit(updated());
0273 }
0274 
0275 QWidget* KisFloatColorInput::createInput()
0276 {
0277     m_dblNumInput = new KisDoubleParseSpinBox(this);
0278     m_dblNumInput->setMinimum(0);
0279     m_dblNumInput->setMaximum(1.0);
0280     connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int)));
0281     connect(m_dblNumInput, SIGNAL(valueChanged(double)), this, SLOT(setValue(double)));
0282     m_dblNumInput->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
0283     m_dblNumInput->setMinimumWidth(60);
0284     m_dblNumInput->setMaximumWidth(60);
0285     
0286     quint8* data = m_color->data() + m_channelInfo->pos();
0287     qreal value = 1.0;
0288 
0289     switch (m_channelInfo->channelValueType()) {
0290 #ifdef HAVE_OPENEXR
0291     case KoChannelInfo::FLOAT16:
0292         value = *(reinterpret_cast<half*>(data));
0293         break;
0294 #endif
0295     case KoChannelInfo::FLOAT32:
0296         value = *(reinterpret_cast<float*>(data));
0297         break;
0298     default:
0299         Q_ASSERT(false);
0300     }
0301     m_dblNumInput->setValue(value);
0302 
0303     return m_dblNumInput;
0304 }
0305 
0306 void KisFloatColorInput::sliderChanged(int i)
0307 {
0308     const qreal floatRange = m_maxValue - m_minValue;
0309     m_dblNumInput->setValue(m_minValue + (i / 255.0) * floatRange);
0310 }
0311 
0312 void KisFloatColorInput::update()
0313 {
0314     KoColor min = *m_color;
0315     KoColor max = *m_color;
0316     quint8* data = m_color->data() + m_channelInfo->pos();
0317     quint8* dataMin = min.data() + m_channelInfo->pos();
0318     quint8* dataMax = max.data() + m_channelInfo->pos();
0319 
0320     qreal value = 1.0;
0321     m_minValue = m_displayRenderer->minVisibleFloatValue(m_channelInfo);
0322     m_maxValue = m_displayRenderer->maxVisibleFloatValue(m_channelInfo);
0323     m_dblNumInput->blockSignals(true);
0324     m_colorSlider->blockSignals(true);
0325 
0326     switch (m_channelInfo->channelValueType()) {
0327 #ifdef HAVE_OPENEXR
0328     case KoChannelInfo::FLOAT16:
0329         value = *(reinterpret_cast<half*>(data));
0330         m_minValue = qMin(value, m_minValue);
0331         m_maxValue = qMax(value, m_maxValue);
0332         *(reinterpret_cast<half*>(dataMin)) = m_minValue;
0333         *(reinterpret_cast<half*>(dataMax)) = m_maxValue;
0334         break;
0335 #endif
0336     case KoChannelInfo::FLOAT32:
0337         value = *(reinterpret_cast<float*>(data));
0338         m_minValue = qMin(value, m_minValue);
0339         m_maxValue = qMax(value, m_maxValue);
0340         *(reinterpret_cast<float*>(dataMin)) = m_minValue;
0341         *(reinterpret_cast<float*>(dataMax)) = m_maxValue;
0342         break;
0343     default:
0344         Q_ASSERT(false);
0345     }
0346 
0347     m_dblNumInput->setMinimum(m_minValue);
0348     m_dblNumInput->setMaximum(m_maxValue);
0349 
0350     // ensure at least 3 significant digits are always shown
0351     int newPrecision = 2 + qMax(qreal(0.0), std::ceil(-std::log10(m_maxValue)));
0352     if (newPrecision != m_dblNumInput->decimals()) {
0353         m_dblNumInput->setDecimals(newPrecision);
0354         m_dblNumInput->updateGeometry();
0355     }
0356     m_dblNumInput->setValue(value);
0357 
0358     m_colorSlider->setColors(min, max);
0359 
0360     const qreal floatRange = m_maxValue - m_minValue;
0361     m_colorSlider->setValue((value - m_minValue) / floatRange * 255);
0362     m_dblNumInput->blockSignals(false);
0363     m_colorSlider->blockSignals(false);
0364 }
0365 
0366 KisHexColorInput::KisHexColorInput(QWidget* parent, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage, bool usePreview) :
0367     KisColorInput(parent, 0, color, displayRenderer, usePercentage)
0368 {
0369     QHBoxLayout* m_layout = new QHBoxLayout(this);
0370     m_layout->setContentsMargins(0,0,0,0);
0371     m_layout->setSpacing(1);
0372 
0373     QLabel* m_label = new QLabel(i18n("Color name:"), this);
0374     m_label->setMinimumWidth(50);
0375     m_layout->addWidget(m_label);
0376 
0377     QWidget* m_input = createInput();
0378     m_input->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
0379 
0380     if(usePreview) {
0381         m_colorPreview = new QLabel("");
0382         m_colorPreview->setMinimumWidth(30);
0383         m_layout->addWidget(m_colorPreview);
0384     }
0385 
0386     m_layout->addWidget(m_input);
0387 }
0388 
0389 void KisHexColorInput::setValue()
0390 {
0391     QString valueString = m_hexInput->text();
0392     valueString.remove(QChar('#'));
0393 
0394     QList<KoChannelInfo*> channels = m_color->colorSpace()->channels();
0395     channels = KoChannelInfo::displayOrderSorted(channels);
0396     Q_FOREACH (KoChannelInfo* channel, channels) {
0397         if (channel->channelType() == KoChannelInfo::COLOR) {
0398             Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8);
0399             quint8* data = m_color->data() + channel->pos();
0400 
0401             int value = valueString.left(2).toInt(0, 16);
0402             *(reinterpret_cast<quint8*>(data)) = value;
0403             valueString.remove(0, 2);
0404         }
0405     }
0406     emit(updated());
0407 }
0408 
0409 void KisHexColorInput::update()
0410 {
0411     QString hexString("#");
0412 
0413     QList<KoChannelInfo*> channels = m_color->colorSpace()->channels();
0414     channels = KoChannelInfo::displayOrderSorted(channels);
0415     Q_FOREACH (KoChannelInfo* channel, channels) {
0416         if (channel->channelType() == KoChannelInfo::COLOR) {
0417             Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8);
0418             quint8* data = m_color->data() + channel->pos();
0419             hexString.append(QString("%1").arg(*(reinterpret_cast<quint8*>(data)), 2, 16, QChar('0')));
0420         }
0421     }
0422     m_hexInput->setText(hexString);
0423     if( m_colorPreview) {
0424         m_colorPreview->setStyleSheet(QString("background-color: %1").arg(m_displayRenderer->toQColor(*m_color).name()));
0425     }
0426 }
0427 
0428 QWidget* KisHexColorInput::createInput()
0429 {
0430     m_hexInput = new QLineEdit(this);
0431     m_hexInput->setAlignment(Qt::AlignRight);
0432 
0433     int digits = 2*m_color->colorSpace()->colorChannelCount();
0434     QString pattern = QString("#?[a-fA-F0-9]{%1,%2}").arg(digits).arg(digits);
0435     m_hexInput->setValidator(new QRegExpValidator(QRegExp(pattern), this));
0436     connect(m_hexInput, SIGNAL(editingFinished()), this, SLOT(setValue()));
0437     return m_hexInput;
0438 }
0439 
0440 
0441 KisHsvColorInput::KisHsvColorInput(QWidget *parent, KoColor *color)
0442     : QWidget(parent)
0443     , m_color(color)
0444     , m_hSlider(nullptr)
0445     , m_sSlider(nullptr)
0446     , m_xSlider(nullptr)
0447     , m_hInput(nullptr)
0448     , m_sInput(nullptr)
0449     , m_xInput(nullptr)
0450     , m_h(0)
0451     , m_s(0)
0452     , m_x(0)
0453     , m_mixMode(KisHsvColorSlider::MIX_MODE::HSV)
0454 {
0455 
0456     QLabel *labels[3];
0457     KisHsvColorSlider *sliders[3];
0458     KisDoubleParseSpinBox *inputs[3];
0459     const char *labelNames[3] = { "H:", "S:", "V:" };
0460     qreal maxValues[3] = { 360, 100, 100 };
0461     int labelWidth = 0;
0462 
0463     QVBoxLayout *mainLayout = new QVBoxLayout(this);
0464     mainLayout->setContentsMargins(0,0,0,0);
0465 
0466     for (int i = 0; i < 3; i++) {
0467         // Slider layout
0468         QHBoxLayout *sliderLayout = new QHBoxLayout();
0469         sliderLayout->setContentsMargins(0,0,0,0);
0470         sliderLayout->setSpacing(1);
0471 
0472         // Label
0473         QLabel *label = new QLabel(i18n(labelNames[i]), this);
0474         sliderLayout->addWidget(label);
0475 
0476         // Slider itself
0477         KisHsvColorSlider *slider = new KisHsvColorSlider(Qt::Horizontal, this);
0478         slider->setMixMode(m_mixMode);
0479         slider->setMinimum(0);
0480         slider->setMaximum(maxValues[i]);
0481         slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0482         sliderLayout->addWidget(slider);
0483 
0484         // Input box
0485         KisDoubleParseSpinBox *input = new KisDoubleParseSpinBox(this);
0486         input->setMinimum(0);
0487         input->setMaximum(maxValues[i]);
0488 
0489         input->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
0490         input->setMinimumWidth(60);
0491         input->setMaximumWidth(60);
0492 
0493         slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0494         sliderLayout->addWidget(input);
0495 
0496         mainLayout->addLayout(sliderLayout);
0497 
0498         // Record max label width
0499         labelWidth = qMax(labelWidth, label->sizeHint().width());
0500 
0501         sliders[i] = slider;
0502         inputs[i] = input;
0503         labels[i] = label;
0504     }
0505 
0506     // Align the labels
0507     for (int i = 0; i < 3; i++) {
0508         labels[i]->setMinimumWidth(labelWidth);
0509     }
0510 
0511     // Connect slots
0512     connect(sliders[0], SIGNAL(valueChanged(int)), this, SLOT(hueSliderChanged(int)));
0513     connect(inputs[0], SIGNAL(valueChanged(double)), this, SLOT(setHue(double)));
0514     connect(sliders[1], SIGNAL(valueChanged(int)), this, SLOT(saturationSliderChanged(int)));
0515     connect(inputs[1], SIGNAL(valueChanged(double)), this, SLOT(setSaturation(double)));
0516     connect(sliders[2], SIGNAL(valueChanged(int)), this, SLOT(valueSliderChanged(int)));
0517     connect(inputs[2], SIGNAL(valueChanged(double)), this, SLOT(setValue(double)));
0518 
0519     m_hSlider = sliders[0];
0520     m_hSlider->setMixMode(KisHsvColorSlider::MIX_MODE::HSV);
0521     m_sSlider = sliders[1];
0522     m_xSlider = sliders[2];
0523 
0524     m_hInput = inputs[0];
0525     m_sInput = inputs[1];
0526     m_xInput = inputs[2];
0527 
0528     // Set initial values
0529     QColor c = m_color->toQColor();
0530     getHsxF(c, &m_h, &m_s, &m_x);
0531 
0532     m_hInput->setValue(m_h);
0533     m_sInput->setValue(m_s);
0534     m_xInput->setValue(m_x);
0535 
0536     // Update sliders
0537     QColor minC, maxC;
0538     minC.setHsvF(0, 1, 1);
0539     maxC.setHsvF(1, 1, 1);
0540     m_hSlider->setColors(minC, maxC);
0541     m_hSlider->setCircularHue(true);
0542 
0543     recolorSliders();
0544 }
0545 
0546 void KisHsvColorInput::setMixMode(KisHsvColorSlider::MIX_MODE mixMode) {
0547     QColor c = m_color->toQColor();
0548     m_mixMode = mixMode;
0549     getHsxF(c, &m_h, &m_s, &m_x);
0550 
0551     m_sSlider->setMixMode(m_mixMode);
0552     m_xSlider->setMixMode(m_mixMode);
0553 
0554     sendUpdate();
0555 }
0556 
0557 void KisHsvColorInput::sendUpdate()
0558 {
0559     {
0560         KisSignalsBlocker blocker(
0561             m_hSlider, m_sSlider, m_xSlider,
0562             m_hInput, m_sInput, m_xInput
0563         );
0564         m_hSlider->setValue(m_h * 360);
0565         m_sSlider->setValue(m_s * 100);
0566         m_xSlider->setValue(m_x * 100);
0567 
0568         m_hInput->setValue(m_h * 360);
0569         m_sInput->setValue(m_s * 100);
0570         m_xInput->setValue(m_x * 100);
0571     }
0572 
0573     recolorSliders();
0574 
0575     QColor c;
0576     fillColor(c);
0577 
0578     m_color->fromQColor(c);
0579     emit(updated());
0580 }
0581 
0582 void KisHsvColorInput::setHue(double x)
0583 {
0584     if (x < 0) {
0585         x = 0;
0586     }
0587 
0588     if (x > 360) {
0589         x = 360;
0590     }
0591 
0592     m_h = x / 360;
0593     sendUpdate();
0594 }
0595 
0596 void KisHsvColorInput::setSaturation(double x)
0597 {
0598     if (x < 0) {
0599         x = 0;
0600     }
0601 
0602     if (x > 100) {
0603         x = 100;
0604     }
0605 
0606     m_s = x / 100;
0607     sendUpdate();
0608 }
0609 
0610 void KisHsvColorInput::setValue(double x)
0611 {
0612     if (x < 0) {
0613         x = 0;
0614     }
0615 
0616     if (x > 100) {
0617         x = 100;
0618     }
0619 
0620     m_x = x / 100;
0621     sendUpdate();
0622 }
0623 
0624 void KisHsvColorInput::hueSliderChanged(int i)
0625 {
0626     m_hInput->setValue(i);
0627 }
0628 
0629 void KisHsvColorInput::saturationSliderChanged(int i)
0630 {
0631     m_sInput->setValue(i);
0632 }
0633 
0634 void KisHsvColorInput::valueSliderChanged(int i)
0635 {
0636     m_xInput->setValue(i);
0637 }
0638 
0639 void KisHsvColorInput::recolorSliders() {
0640     // Update sliders
0641     QColor minC, maxC;
0642 
0643     minC.setHsvF(m_h, 0, m_x);
0644     maxC.setHsvF(m_h, 1, m_x);
0645     m_sSlider->setColors(minC, maxC);
0646 
0647     minC.setHsvF(m_h, m_s, 0);
0648     maxC.setHsvF(m_h, m_s, 1);
0649     m_xSlider->setColors(minC, maxC);
0650 }
0651 
0652 void KisHsvColorInput::update()
0653 {
0654     KisSignalsBlocker blocker(
0655         m_hInput, m_sInput, m_xInput,
0656         m_hSlider, m_sSlider, m_xSlider
0657     );
0658 
0659     // Check if it is the same color we have
0660     QColor current;
0661 
0662     fillColor(current);
0663 
0664     QColor theirs = m_color->toQColor();
0665 
0666     // Truncate to integer for this check
0667     if (!(current.red() == theirs.red() && current.green() == theirs.green() && current.blue() == theirs.blue())) {
0668         // Apply the update
0669         qreal theirH;
0670         getHsxF(theirs, &theirH, &m_s, &m_x);
0671 
0672         // Don't jump the Hue slider around to 0 if it is currently on 360
0673         const qreal EPSILON = 1e-6;
0674         if (!((1.0 - m_h) < EPSILON && (theirH - 0.0) < EPSILON)) {
0675             m_h = theirH;
0676         }
0677 
0678         m_hInput->setValue(m_h * 360);
0679         m_sInput->setValue(m_s * 100);
0680         m_xInput->setValue(m_x * 100);
0681 
0682         recolorSliders();
0683 
0684         // Update slider positions
0685         m_hSlider->setValue(m_h * 360);
0686         m_sSlider->setValue(m_s * 100);
0687         m_xSlider->setValue(m_x * 100);
0688     }
0689 }
0690 
0691 void KisHsvColorInput::fillColor(QColor& c) {
0692     fillColor(c, m_h, m_s, m_x);
0693 }
0694 
0695 void KisHsvColorInput::fillColor(QColor& c, const qreal& h, const qreal& s, const qreal& x)
0696 {
0697     switch (m_mixMode) {
0698     case KisHsvColorSlider::MIX_MODE::HSL:
0699         c.setHslF(h, s, x);
0700         break;
0701 
0702     case KisHsvColorSlider::MIX_MODE::HSY: {
0703         qreal r, g, b;
0704         HSYToRGB(h, s, x, &r, &g, &b);
0705 
0706         // Clamp
0707         r = qBound(0.0, r, 1.0);
0708         g = qBound(0.0, g, 1.0);
0709         b = qBound(0.0, b, 1.0);
0710 
0711         c.setRgbF(r, g, b);
0712         break;
0713     }
0714 
0715     case KisHsvColorSlider::MIX_MODE::HSI: {
0716         qreal r, g, b;
0717         HSIToRGB(h, s, x, &r, &g, &b);
0718         c.setRgbF(r, g, b);
0719         break;
0720     }
0721 
0722     default: // fallthrough
0723     case KisHsvColorSlider::MIX_MODE::HSV:
0724         c.setHsvF(h, s, x);
0725         break;
0726     }
0727 }
0728 
0729 void KisHsvColorInput::getHsxF(const QColor& color, qreal* h, qreal* s, qreal* x)
0730 {
0731     qreal tempH;
0732     switch (m_mixMode) {
0733     case KisHsvColorSlider::MIX_MODE::HSL:
0734         color.getHslF(&tempH, s, x);
0735         break;
0736 
0737     case KisHsvColorSlider::MIX_MODE::HSY: {
0738         RGBToHSY(color.redF(), color.greenF(), color.blueF(), &tempH, s, x);
0739         break;
0740     }
0741 
0742     case KisHsvColorSlider::MIX_MODE::HSI: {
0743         RGBToHSI(color.redF(), color.greenF(), color.blueF(), &tempH, s, x);
0744         break;
0745     }
0746 
0747     default: // fallthrough
0748     case KisHsvColorSlider::MIX_MODE::HSV:
0749         color.getHsvF(&tempH, s, x);
0750         break;
0751     }
0752 
0753     if (tempH >= 0.0 && tempH <= 1.0) {
0754         *h = tempH;
0755     }
0756 }