kis_color_input.cpp 14.4 KB
Newer Older
1 2
/*
 *  Copyright (c) 2008 Cyrille Berger <cberger@cberger.net>
Sven Langkamp's avatar
Sven Langkamp committed
3
 *  Copyright (c) 2011 Sven Langkamp <sven.langkamp@gmail.com>
Moritz Molch's avatar
Moritz Molch committed
4
 *  Copyright (c) 2015 Moritz Molch <kde@moritzmolch.de>
5
 *
6
 *  This library is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU Lesser General Public License as published by
8 9
 *  the Free Software Foundation; version 2 of the License, or
 *  (at your option) any later version.
10
 *
11
 *  This library is distributed in the hope that it will be useful,
12 13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU Lesser General Public License for more details.
15 16 17 18 19 20
 *
 *  You should have received a copy of the GNU Lesser 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.
 */

Boudewijn Rempt's avatar
Boudewijn Rempt committed
21 22
#include "kis_color_input.h"

23
#include <KoConfig.h>
24 25 26 27
#ifdef HAVE_OPENEXR
#include <half.h>
#endif

28 29
#include <cmath>

30 31
#include <kis_debug.h>

32 33
#include <QHBoxLayout>
#include <QLabel>
Sven Langkamp's avatar
Sven Langkamp committed
34
#include <QLineEdit>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
35 36
#include <QSpinBox>
#include <QDoubleSpinBox>
37

38
#include <klocalizedstring.h>
39 40 41

#include <KoChannelInfo.h>
#include <KoColor.h>
42
#include <KoColorSlider.h>
Sven Langkamp's avatar
Sven Langkamp committed
43
#include <KoColorSpace.h>
44

45 46
#include "kis_double_parse_spin_box.h"
#include "kis_int_parse_spin_box.h"
47

48 49 50
KisColorInput::KisColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
    QWidget(parent), m_channelInfo(channelInfo), m_color(color), m_displayRenderer(displayRenderer),
    m_usePercentage(usePercentage)
51 52 53 54 55
{
}

void KisColorInput::init()
{
56
    QHBoxLayout* m_layout = new QHBoxLayout(this);
Moritz Molch's avatar
Moritz Molch committed
57 58 59
    m_layout->setContentsMargins(0,0,0,0);
    m_layout->setSpacing(1);

60
    QLabel* m_label = new QLabel(i18n("%1:", m_channelInfo->name()), this);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
61
    m_layout->addWidget(m_label);
62

63
    m_colorSlider = new KoColorSlider(Qt::Horizontal, this, m_displayRenderer);
64
    m_colorSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
65
    m_layout->addWidget(m_colorSlider);
66

67
    QWidget* m_input = createInput();
Moritz Molch's avatar
Moritz Molch committed
68
    m_colorSlider->setFixedHeight(m_input->sizeHint().height());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
69
    m_layout->addWidget(m_input);
70 71
}

72 73
KisIntegerColorInput::KisIntegerColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
    KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage)
74 75 76 77 78 79 80
{
    init();
}

void KisIntegerColorInput::setValue(int v)
{
    quint8* data = m_color->data() + m_channelInfo->pos();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
81 82 83 84 85 86 87 88 89 90 91 92
    switch (m_channelInfo->channelValueType()) {
    case KoChannelInfo::UINT8:
        *(reinterpret_cast<quint8*>(data)) = v;
        break;
    case KoChannelInfo::UINT16:
        *(reinterpret_cast<quint16*>(data)) = v;
        break;
    case KoChannelInfo::UINT32:
        *(reinterpret_cast<quint32*>(data)) = v;
        break;
    default:
        Q_ASSERT(false);
93 94 95 96 97 98
    }
    emit(updated());
}

void KisIntegerColorInput::update()
{
99 100
    KoColor min = *m_color;
    KoColor max = *m_color;
101
    quint8* data = m_color->data() + m_channelInfo->pos();
102 103
    quint8* dataMin = min.data() + m_channelInfo->pos();
    quint8* dataMax = max.data() + m_channelInfo->pos();
104 105
    m_intNumInput->blockSignals(true);
    m_colorSlider->blockSignals(true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
106 107
    switch (m_channelInfo->channelValueType()) {
    case KoChannelInfo::UINT8:
108 109 110 111 112 113 114
        if (m_usePercentage) {
            m_intNumInput->setMaximum(100);
            m_intNumInput->setValue(round(*(reinterpret_cast<quint8*>(data))*1.0 / 255.0 * 100.0));
        } else {
            m_intNumInput->setMaximum(0xFF);
            m_intNumInput->setValue(*(reinterpret_cast<quint8*>(data)));
        }
115
        m_colorSlider->setValue(*(reinterpret_cast<quint8*>(data)));
116 117
        *(reinterpret_cast<quint8*>(dataMin)) = 0x0;
        *(reinterpret_cast<quint8*>(dataMax)) = 0xFF;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
118 119
        break;
    case KoChannelInfo::UINT16:
120 121 122 123 124 125 126
        if (m_usePercentage) {
            m_intNumInput->setMaximum(100);
            m_intNumInput->setValue(round(*(reinterpret_cast<quint16*>(data))*1.0 / 65535.0 * 100.0));
        } else {
            m_intNumInput->setMaximum(0xFFFF);
            m_intNumInput->setValue(*(reinterpret_cast<quint16*>(data)));
        }
127
        m_colorSlider->setValue(*(reinterpret_cast<quint16*>(data)));
128 129
        *(reinterpret_cast<quint16*>(dataMin)) = 0x0;
        *(reinterpret_cast<quint16*>(dataMax)) = 0xFFFF;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
130 131
        break;
    case KoChannelInfo::UINT32:
132 133 134 135 136 137 138
        if (m_usePercentage) {
            m_intNumInput->setMaximum(100);
            m_intNumInput->setValue(round(*(reinterpret_cast<quint32*>(data))*1.0 / 4294967295.0 * 100.0));
        } else {
            m_intNumInput->setMaximum(0xFFFF);
            m_intNumInput->setValue(*(reinterpret_cast<quint32*>(data)));
        }
139
        m_colorSlider->setValue(*(reinterpret_cast<quint32*>(data)));
140 141
        *(reinterpret_cast<quint32*>(dataMin)) = 0x0;
        *(reinterpret_cast<quint32*>(dataMax)) = 0xFFFFFFFF;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
142 143 144
        break;
    default:
        Q_ASSERT(false);
145
    }
146
    m_colorSlider->setColors(min, max);
147 148
    m_intNumInput->blockSignals(false);
    m_colorSlider->blockSignals(false);
149 150
}

151
QWidget* KisIntegerColorInput::createInput()
152
{
153
    m_intNumInput = new KisIntParseSpinBox(this);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
154
    m_intNumInput->setMinimum(0);
155
    m_colorSlider->setMinimum(0);
156 157

    if (m_usePercentage) {
158
        m_intNumInput->setSuffix(i18n("%"));
159 160 161 162
    } else {
        m_intNumInput->setSuffix("");
    }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
163 164
    switch (m_channelInfo->channelValueType()) {
    case KoChannelInfo::UINT8:
165 166 167 168 169
        if (m_usePercentage) {
            m_intNumInput->setMaximum(100);
        } else {
            m_intNumInput->setMaximum(0xFF);
        }
170
        m_colorSlider->setMaximum(0xFF);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
171 172
        break;
    case KoChannelInfo::UINT16:
173 174 175 176 177
        if (m_usePercentage) {
            m_intNumInput->setMaximum(100);
        } else {
            m_intNumInput->setMaximum(0xFFFF);
        }
178
        m_colorSlider->setMaximum(0xFFFF);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
179 180
        break;
    case KoChannelInfo::UINT32:
181 182 183 184 185
        if (m_usePercentage) {
            m_intNumInput->setMaximum(100);
        } else {
            m_intNumInput->setMaximum(0xFFFFFFFF);
        }
186
        m_colorSlider->setMaximum(0xFFFFFFFF);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
187 188 189
        break;
    default:
        Q_ASSERT(false);
190
    }
191 192
    connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(onColorSliderChanged(int)));
    connect(m_intNumInput, SIGNAL(valueChanged(int)), this, SLOT(onNumInputChanged(int)));
193 194 195
    return m_intNumInput;
}

196 197 198 199 200
void KisIntegerColorInput::setPercentageWise(bool val)
{
    m_usePercentage = val;

    if (m_usePercentage) {
201
        m_intNumInput->setSuffix(i18n("%"));
202 203 204 205 206
    } else {
        m_intNumInput->setSuffix("");
    }
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
void KisIntegerColorInput::onColorSliderChanged(int val)
{
    m_intNumInput->blockSignals(true);
    if (m_usePercentage) {
        switch (m_channelInfo->channelValueType()) {
        case KoChannelInfo::UINT8:
            m_intNumInput->setValue(round((val*1.0) / 255.0 * 100.0));
            break;
        case KoChannelInfo::UINT16:
            m_intNumInput->setValue(round((val*1.0) / 65535.0 * 100.0));
            break;
        case KoChannelInfo::UINT32:
            m_intNumInput->setValue(round((val*1.0) / 4294967295.0 * 100.0));
            break;
        default:
            Q_ASSERT(false);
        }
    } else {
        m_intNumInput->setValue(val);
    }
    m_intNumInput->blockSignals(false);
    setValue(val);
}

void KisIntegerColorInput::onNumInputChanged(int val)
{
    m_colorSlider->blockSignals(true);
    if (m_usePercentage) {
        switch (m_channelInfo->channelValueType()) {
        case KoChannelInfo::UINT8:
            m_colorSlider->setValue((val*1.0)/100.0 * 255.0);
            m_colorSlider->blockSignals(false);
            setValue((val*1.0)/100.0 * 255.0);
            break;
        case KoChannelInfo::UINT16:
            m_colorSlider->setValue((val*1.0)/100.0 * 65535.0);
            m_colorSlider->blockSignals(false);
            setValue((val*1.0)/100.0 * 65535.0);
            break;
        case KoChannelInfo::UINT32:
            m_colorSlider->setValue((val*1.0)/100.0 * 4294967295.0);
            m_colorSlider->blockSignals(false);
            setValue((val*1.0)/100.0 * 4294967295.0);
            break;
        default:
            Q_ASSERT(false);
        }
    } else {
        m_colorSlider->setValue(val);
        m_colorSlider->blockSignals(false);
        setValue(val);
    }
}
260

261 262
KisFloatColorInput::KisFloatColorInput(QWidget* parent, const KoChannelInfo* channelInfo, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
    KisColorInput(parent, channelInfo, color, displayRenderer, usePercentage)
263 264 265 266 267 268 269
{
    init();
}

void KisFloatColorInput::setValue(double v)
{
    quint8* data = m_color->data() + m_channelInfo->pos();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
270
    switch (m_channelInfo->channelValueType()) {
271
#ifdef HAVE_OPENEXR
Boudewijn Rempt's avatar
Boudewijn Rempt committed
272 273 274
    case KoChannelInfo::FLOAT16:
        *(reinterpret_cast<half*>(data)) = v;
        break;
275
#endif
Boudewijn Rempt's avatar
Boudewijn Rempt committed
276
    case KoChannelInfo::FLOAT32:
277
        *(reinterpret_cast<float*>(data)) = v;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
278 279 280
        break;
    default:
        Q_ASSERT(false);
281 282 283 284
    }
    emit(updated());
}

285
QWidget* KisFloatColorInput::createInput()
286
{
287
    m_dblNumInput = new KisDoubleParseSpinBox(this);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
288 289
    m_dblNumInput->setMinimum(0);
    m_dblNumInput->setMaximum(1.0);
290
    connect(m_colorSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int)));
291 292 293 294
    connect(m_dblNumInput, SIGNAL(valueChanged(double)), this, SLOT(setValue(double)));
    m_dblNumInput->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
    m_dblNumInput->setMinimumWidth(60);
    m_dblNumInput->setMaximumWidth(60);
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    
    quint8* data = m_color->data() + m_channelInfo->pos();
    qreal value = 1.0;

    switch (m_channelInfo->channelValueType()) {
#ifdef HAVE_OPENEXR
    case KoChannelInfo::FLOAT16:
        value = *(reinterpret_cast<half*>(data));
        break;
#endif
    case KoChannelInfo::FLOAT32:
        value = *(reinterpret_cast<float*>(data));
        break;
    default:
        Q_ASSERT(false);
    }
    m_dblNumInput->setValue(value);

313 314 315
    return m_dblNumInput;
}

316 317
void KisFloatColorInput::sliderChanged(int i)
{
318 319
    const qreal floatRange = m_maxValue - m_minValue;
    m_dblNumInput->setValue(m_minValue + (i / 255.0) * floatRange);
320 321
}

322 323
void KisFloatColorInput::update()
{
324 325
    KoColor min = *m_color;
    KoColor max = *m_color;
326
    quint8* data = m_color->data() + m_channelInfo->pos();
327 328
    quint8* dataMin = min.data() + m_channelInfo->pos();
    quint8* dataMax = max.data() + m_channelInfo->pos();
329 330

    qreal value = 1.0;
331 332
    m_minValue = m_displayRenderer->minVisibleFloatValue(m_channelInfo);
    m_maxValue = m_displayRenderer->maxVisibleFloatValue(m_channelInfo);
333
    m_dblNumInput->blockSignals(true);
334
    m_colorSlider->blockSignals(true);
335

Boudewijn Rempt's avatar
Boudewijn Rempt committed
336
    switch (m_channelInfo->channelValueType()) {
337
#ifdef HAVE_OPENEXR
Boudewijn Rempt's avatar
Boudewijn Rempt committed
338
    case KoChannelInfo::FLOAT16:
339
        value = *(reinterpret_cast<half*>(data));
340 341 342 343
        m_minValue = qMin(value, m_minValue);
        m_maxValue = qMax(value, m_maxValue);
        *(reinterpret_cast<half*>(dataMin)) = m_minValue;
        *(reinterpret_cast<half*>(dataMax)) = m_maxValue;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
344
        break;
345
#endif
Boudewijn Rempt's avatar
Boudewijn Rempt committed
346
    case KoChannelInfo::FLOAT32:
347
        value = *(reinterpret_cast<float*>(data));
348 349 350 351
        m_minValue = qMin(value, m_minValue);
        m_maxValue = qMax(value, m_maxValue);
        *(reinterpret_cast<float*>(dataMin)) = m_minValue;
        *(reinterpret_cast<float*>(dataMax)) = m_maxValue;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
352 353 354
        break;
    default:
        Q_ASSERT(false);
355
    }
356 357 358 359 360

    m_dblNumInput->setMinimum(m_minValue);
    m_dblNumInput->setMaximum(m_maxValue);

    // ensure at least 3 significant digits are always shown
361 362 363 364 365
    int newPrecision = 2 + qMax(qreal(0.0), std::ceil(-std::log10(m_maxValue)));
    if (newPrecision != m_dblNumInput->decimals()) {
        m_dblNumInput->setDecimals(newPrecision);
        m_dblNumInput->updateGeometry();
    }
366
    m_dblNumInput->setValue(value);
367

368
    m_colorSlider->setColors(min, max);
369

370 371
    const qreal floatRange = m_maxValue - m_minValue;
    m_colorSlider->setValue((value - m_minValue) / floatRange * 255);
372
    m_dblNumInput->blockSignals(false);
373
    m_colorSlider->blockSignals(false);
374 375
}

376 377
KisHexColorInput::KisHexColorInput(QWidget* parent, KoColor* color, KoColorDisplayRendererInterface *displayRenderer, bool usePercentage) :
    KisColorInput(parent, 0, color, displayRenderer, usePercentage)
Sven Langkamp's avatar
Sven Langkamp committed
378 379
{
    QHBoxLayout* m_layout = new QHBoxLayout(this);
Moritz Molch's avatar
Moritz Molch committed
380 381 382
    m_layout->setContentsMargins(0,0,0,0);
    m_layout->setSpacing(1);

Sven Langkamp's avatar
Sven Langkamp committed
383 384 385 386 387
    QLabel* m_label = new QLabel(i18n("Color name:"), this);
    m_label->setMinimumWidth(50);
    m_layout->addWidget(m_label);

    QWidget* m_input = createInput();
388
    m_input->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
Sven Langkamp's avatar
Sven Langkamp committed
389 390 391 392 393 394 395 396 397
    m_layout->addWidget(m_input);
}

void KisHexColorInput::setValue()
{
    QString valueString = m_hexInput->text();
    valueString.remove(QChar('#'));

    QList<KoChannelInfo*> channels = m_color->colorSpace()->channels();
398
    channels = KoChannelInfo::displayOrderSorted(channels);
399
    Q_FOREACH (KoChannelInfo* channel, channels) {
Sven Langkamp's avatar
Sven Langkamp committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        if (channel->channelType() == KoChannelInfo::COLOR) {
            Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8);
            quint8* data = m_color->data() + channel->pos();

            int value = valueString.left(2).toInt(0, 16);
            *(reinterpret_cast<quint8*>(data)) = value;
            valueString.remove(0, 2);
        }
    }
    emit(updated());
}

void KisHexColorInput::update()
{
    QString hexString("#");

    QList<KoChannelInfo*> channels = m_color->colorSpace()->channels();
417
    channels = KoChannelInfo::displayOrderSorted(channels);
418
    Q_FOREACH (KoChannelInfo* channel, channels) {
Sven Langkamp's avatar
Sven Langkamp committed
419 420 421 422 423 424 425 426 427 428 429 430
        if (channel->channelType() == KoChannelInfo::COLOR) {
            Q_ASSERT(channel->channelValueType() == KoChannelInfo::UINT8);
            quint8* data = m_color->data() + channel->pos();
            hexString.append(QString("%1").arg(*(reinterpret_cast<quint8*>(data)), 2, 16, QChar('0')));
        }
    }
    m_hexInput->setText(hexString);
}

QWidget* KisHexColorInput::createInput()
{
    m_hexInput = new QLineEdit(this);
431
    m_hexInput->setAlignment(Qt::AlignRight);
Sven Langkamp's avatar
Sven Langkamp committed
432 433 434 435 436 437 438 439

    int digits = 2*m_color->colorSpace()->colorChannelCount();
    QString pattern = QString("#?[a-fA-F0-9]{%1,%2}").arg(digits).arg(digits);
    m_hexInput->setValidator(new QRegExpValidator(QRegExp(pattern), this));
    connect(m_hexInput, SIGNAL(editingFinished()), this, SLOT(setValue()));
    return m_hexInput;
}