1 /*
2  * Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
3  * Copyright (C) 2020 L. E. Segovia <amy@amyspark.me>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 #include "kis_spinbox_color_selector.h"
20 #include <QFormLayout>
21 #include <QLabel>
22 #include "kis_double_parse_spin_box.h"
23 #include "kis_int_parse_spin_box.h"
24 #include "kis_signal_compressor.h"
25 
26 #include <KoConfig.h>
27 #ifdef HAVE_OPENEXR
28 #include <half.h>
29 #endif
30 #include <KoChannelInfo.h>
31 #include <KoColorModelStandardIds.h>
32 #include <KoColorSpaceTraits.h>
33 #include <KoColorSpaceMaths.h>
34 #include <KoColorSpaceRegistry.h>
35 
36 struct KisSpinboxColorSelector::Private
37 {
38     QList <QLabel*> labels;
39     QList <KisIntParseSpinBox*> spinBoxList;
40     QList <KisDoubleParseSpinBox*> doubleSpinBoxList;
41     KoColor color;
42     const KoColorSpace *cs {0};
43     bool chooseAlpha {false};
44     QFormLayout *layout {0};
45 };
46 
KisSpinboxColorSelector(QWidget * parent)47 KisSpinboxColorSelector::KisSpinboxColorSelector(QWidget *parent)
48     : QWidget(parent)
49     , m_d(new Private)
50 {
51     this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
52     m_d->layout = new QFormLayout(this);
53 }
54 
~KisSpinboxColorSelector()55 KisSpinboxColorSelector::~KisSpinboxColorSelector()
56 {
57 
58 }
59 
slotSetColor(KoColor color)60 void KisSpinboxColorSelector::slotSetColor(KoColor color)
61 {
62     m_d->color = color;
63     slotSetColorSpace(m_d->color.colorSpace());
64     updateSpinboxesWithNewValues();
65 }
66 
slotSetColorSpace(const KoColorSpace * cs)67 void KisSpinboxColorSelector::slotSetColorSpace(const KoColorSpace *cs)
68 {
69     if (cs == m_d->cs) {
70         return;
71     }
72 
73     m_d->cs = cs;
74     //remake spinboxes
75     delete m_d->layout;
76     m_d->layout = new QFormLayout(this);
77 
78     Q_FOREACH(QObject *o, m_d->labels) {
79         o->deleteLater();
80     }
81     Q_FOREACH(QObject *o, m_d->spinBoxList) {
82         o->deleteLater();
83     }
84     Q_FOREACH(QObject *o, m_d->doubleSpinBoxList) {
85         o->deleteLater();
86     }
87 
88     m_d->labels.clear();
89     m_d->spinBoxList.clear();
90     m_d->doubleSpinBoxList.clear();
91 
92     QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_d->cs->channels());
93     Q_FOREACH (KoChannelInfo* channel, channels) {
94         QString inputLabel = channel->name() + ":";
95         QLabel *inlb = new QLabel(this);
96         m_d->labels << inlb;
97         inlb->setText(inputLabel);
98         switch (channel->channelValueType()) {
99         case KoChannelInfo::UINT8: {
100             KisIntParseSpinBox *input = new KisIntParseSpinBox(this);
101             input->setMinimum(0);
102             input->setMaximum(0xFF);
103             m_d->spinBoxList.append(input);
104             connect(input, SIGNAL(valueChanged(int)), this,  SLOT(slotUpdateFromSpinBoxes()));
105             if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
106                 inlb->setVisible(false);
107                 input->setVisible(false);
108                 input->blockSignals(true);
109             }
110             else {
111                 m_d->layout->addRow(inlb, input);
112             }
113         }
114             break;
115         case KoChannelInfo::UINT16: {
116             KisIntParseSpinBox *input = new KisIntParseSpinBox(this);
117             input->setMinimum(0);
118             input->setMaximum(0xFFFF);
119             m_d->spinBoxList.append(input);
120             connect(input, SIGNAL(valueChanged(int)), this,  SLOT(slotUpdateFromSpinBoxes()));
121             if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
122                 inlb->setVisible(false);
123                 input->setVisible(false);
124                 input->blockSignals(true);
125             }
126             else {
127                 m_d->layout->addRow(inlb,input);
128             }
129         }
130             break;
131         case KoChannelInfo::UINT32: {
132             KisIntParseSpinBox *input = new KisIntParseSpinBox(this);
133             input->setMinimum(0);
134             input->setMaximum(0xFFFFFFFF);
135             m_d->spinBoxList.append(input);
136             connect(input, SIGNAL(valueChanged(int)), this,  SLOT(slotUpdateFromSpinBoxes()));
137             if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
138                 inlb->setVisible(false);
139                 input->setVisible(false);
140                 input->blockSignals(true);
141             }
142             else {
143                 m_d->layout->addRow(inlb,input);
144             }
145         }
146             break;
147 #ifdef HAVE_OPENEXR
148         case KoChannelInfo::FLOAT16: {
149             half m_uiMin, m_uiMax;
150             if (cs->colorModelId() == LABAColorModelID || cs->colorModelId() == CMYKAColorModelID) {
151                 m_uiMin = channel->getUIMin();
152                 m_uiMax = channel->getUIMax();
153             } else {
154                 m_uiMin = 0;
155                 m_uiMax = KoColorSpaceMathsTraits<half>::max;
156             }
157 
158             KisDoubleParseSpinBox *input = new KisDoubleParseSpinBox(this);
159             input->setMinimum(m_uiMin);
160             input->setMaximum(m_uiMax);
161             input->setSingleStep(0.1);
162             m_d->doubleSpinBoxList.append(input);
163             connect(input, SIGNAL(valueChanged(double)), this,  SLOT(slotUpdateFromSpinBoxes()));
164             if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
165                 inlb->setVisible(false);
166                 input->setVisible(false);
167                 input->blockSignals(true);
168             }
169             else {
170                 m_d->layout->addRow(inlb,input);
171             }
172         }
173             break;
174 #endif
175         case KoChannelInfo::FLOAT32: {
176             float m_uiMin, m_uiMax;
177             if (cs->colorModelId() == LABAColorModelID || cs->colorModelId() == CMYKAColorModelID) {
178                 m_uiMin = channel->getUIMin();
179                 m_uiMax = channel->getUIMax();
180             } else {
181                 m_uiMin = 0;
182                 m_uiMax = KoColorSpaceMathsTraits<float>::max;
183             }
184 
185             KisDoubleParseSpinBox *input = new KisDoubleParseSpinBox(this);
186             input->setMinimum(m_uiMin);
187             input->setMaximum(m_uiMax);
188             input->setSingleStep(0.1);
189             m_d->doubleSpinBoxList.append(input);
190             connect(input, SIGNAL(valueChanged(double)), this,  SLOT(slotUpdateFromSpinBoxes()));
191             if (channel->channelType() == KoChannelInfo::ALPHA && m_d->chooseAlpha == false) {
192                 inlb->setVisible(false);
193                 input->setVisible(false);
194                 input->blockSignals(true);
195             }
196             else {
197                 m_d->layout->addRow(inlb,input);
198             }
199         }
200             break;
201         default:
202             Q_ASSERT(false);
203         }
204 
205     }
206     this->setLayout(m_d->layout);
207 }
208 
createColorFromSpinboxValues()209 void KisSpinboxColorSelector::createColorFromSpinboxValues()
210 {
211     KoColor newColor(m_d->cs);
212     int channelcount = m_d->cs->channelCount();
213     QVector <float> channelValues(channelcount);
214     channelValues.fill(1.0);
215     QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_d->cs->channels());
216 
217     for (int i = 0; i < (int)qAbs(m_d->cs->colorChannelCount()); i++) {
218         int channelposition = KoChannelInfo::displayPositionToChannelIndex(i, m_d->cs->channels());
219 
220         if (channels.at(i)->channelValueType()==KoChannelInfo::UINT8 && m_d->spinBoxList.at(i)){
221 
222             int value = m_d->spinBoxList.at(i)->value();
223             channelValues[channelposition] = KoColorSpaceMaths<quint8,float>::scaleToA(value);
224 
225         } else if (channels.at(i)->channelValueType()==KoChannelInfo::UINT16 && m_d->spinBoxList.at(i)){
226 
227             channelValues[channelposition] = KoColorSpaceMaths<quint16,float>::scaleToA(m_d->spinBoxList.at(i)->value());
228 
229         } else if ((channels.at(i)->channelValueType()==KoChannelInfo::FLOAT16 ||
230                     channels.at(i)->channelValueType()==KoChannelInfo::FLOAT32 ||
231                     channels.at(i)->channelValueType()==KoChannelInfo::FLOAT64) && m_d->doubleSpinBoxList.at(i)) {
232 
233             channelValues[channelposition] = m_d->doubleSpinBoxList.at(i)->value();
234 
235         }
236     }
237 
238     m_d->cs->fromNormalisedChannelsValue(newColor.data(), channelValues);
239     newColor.setOpacity(m_d->color.opacityU8());
240 
241     m_d->color = newColor;
242 }
243 
slotUpdateFromSpinBoxes()244 void KisSpinboxColorSelector::slotUpdateFromSpinBoxes()
245 {
246     createColorFromSpinboxValues();
247     emit sigNewColor(m_d->color);
248 }
249 
updateSpinboxesWithNewValues()250 void KisSpinboxColorSelector::updateSpinboxesWithNewValues()
251 {
252     int channelcount = m_d->cs->channelCount();
253     QVector <float> channelValues(channelcount);
254     channelValues.fill(1.0);
255     m_d->cs->normalisedChannelsValue(m_d->color.data(), channelValues);
256     QList<KoChannelInfo *> channels = KoChannelInfo::displayOrderSorted(m_d->cs->channels());
257 
258     int i;
259     /*while (QLayoutItem *item = this->layout()->takeAt(0))
260     {
261         item->widget()->blockSignals(true);
262     }*/
263     for (i=0; i<m_d->spinBoxList.size(); i++) {
264         m_d->spinBoxList.at(i)->blockSignals(true);
265     }
266     for (i=0; i<m_d->doubleSpinBoxList.size(); i++) {
267         m_d->doubleSpinBoxList.at(i)->blockSignals(true);
268     }
269 
270     for (i = 0; i < (int)qAbs(m_d->cs->colorChannelCount()); i++) {
271         int channelposition = KoChannelInfo::displayPositionToChannelIndex(i, m_d->cs->channels());
272         if (channels.at(i)->channelValueType() == KoChannelInfo::UINT8 && m_d->spinBoxList.at(i)) {
273             int value = KoColorSpaceMaths<float, quint8>::scaleToA(channelValues[channelposition]);
274             m_d->spinBoxList.at(i)->setValue(value);
275         } else if (channels.at(i)->channelValueType() == KoChannelInfo::UINT16 && m_d->spinBoxList.at(i)) {
276             m_d->spinBoxList.at(i)->setValue(KoColorSpaceMaths<float, quint16>::scaleToA(channelValues[channelposition]));
277         } else if ((channels.at(i)->channelValueType()==KoChannelInfo::FLOAT16 ||
278                     channels.at(i)->channelValueType()==KoChannelInfo::FLOAT32 ||
279                     channels.at(i)->channelValueType()==KoChannelInfo::FLOAT64) && m_d->doubleSpinBoxList.at(i)) {
280             float value = channels.at(i)->getUIMin() + channelValues[channelposition] * channels.at(i)->getUIUnitValue();
281             m_d->doubleSpinBoxList.at(i)->setValue(value);
282         }
283     }
284 
285     for (i=0; i<m_d->spinBoxList.size(); i++) {
286         m_d->spinBoxList.at(i)->blockSignals(false);
287     }
288     for (i=0; i<m_d->doubleSpinBoxList.size(); i++) {
289         m_d->doubleSpinBoxList.at(i)->blockSignals(false);
290     }
291     /*while (QLayoutItem *item = this->layout()->takeAt(0))
292     {
293         item->widget()->blockSignals(false);
294     }*/
295 }
296 
297 
298