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