KisGradientSlider.cpp 10.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * This file is part of Krita
 *
 * Copyright (c) 2006 Frederic Coiffier <fcoiffie@gmail.com>
 *
 *  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.
 */
20

21
// Local includes.
22
#include "KisGradientSlider.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
23

24 25 26 27 28 29
// C++ includes.

#include <cmath>
#include <cstdlib>

// Qt includes.
30
#include <kis_debug.h>
31
#include <QtGlobal>
32 33 34
#include <QPainter>
#include <QPoint>
#include <QPen>
35
#include <QMouseEvent>
36 37
#include <QBrush>
#include <QLinearGradient>
38

39 40 41
#define MARGIN 5
#define HANDLE_SIZE 10

42
KisGradientSlider::KisGradientSlider(QWidget *parent)
43 44 45 46 47 48 49 50 51 52 53
    : QWidget(parent)
    , m_leftmost(0)
    , m_rightmost(0)
    , m_scalingFactor(0)
    , m_blackCursor(0)
    , m_whiteCursor(0)
    , m_gammaCursor(0)
    , m_black(0)
    , m_white(255)
    , m_gamma(1.0)
    , m_gammaEnabled(false)
54
    , m_whiteEnabled(true)
55
    , m_feedback(false)
56
    , m_inverted(false)
57
{
58
    m_grabCursor = None;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
59

60
    setMouseTracking(true);
61
    setFocusPolicy(Qt::StrongFocus);
62 63
}

64
KisGradientSlider::~KisGradientSlider()
65 66 67
{
}

68 69 70 71 72 73 74 75 76 77
int KisGradientSlider::black() const
{
    return m_black;
}

int KisGradientSlider::white() const
{
    return m_white;
}

78
void KisGradientSlider::paintEvent(QPaintEvent *e)
79
{
80 81
    QWidget::paintEvent(e);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
82 83
    int x = 0;
    int y = 0;
84
    int wWidth = width() - (2 * MARGIN);
85
    int wHeight = height();
86

87
    const int gradientHeight = qRound((double)wHeight / 7.0 * 2);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
88

Boudewijn Rempt's avatar
Boudewijn Rempt committed
89
    QPainter p1(this);
90 91
    p1.fillRect(rect(), palette().background());
    p1.setPen(Qt::black);
92
    p1.drawRect(MARGIN, MARGIN, wWidth, height() - 2 * MARGIN - HANDLE_SIZE);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
93 94

    // Draw first gradient
95 96 97
    QLinearGradient grayGradient(MARGIN, y, wWidth, gradientHeight);
    grayGradient.setColorAt(0, m_inverted ? Qt::white : Qt::black);
    grayGradient.setColorAt(1, m_inverted ? Qt::black : Qt::white);
98
    p1.fillRect(MARGIN, 0, wWidth, gradientHeight, QBrush(grayGradient));
99

Boudewijn Rempt's avatar
Boudewijn Rempt committed
100
    // Draw second gradient
101
    y = gradientHeight;
102 103
    p1.fillRect(MARGIN, y, wWidth, gradientHeight, Qt::white);

104
    if (m_blackCursor > 0 && !m_inverted) {
105
        p1.fillRect(MARGIN, y, m_blackCursor, gradientHeight, Qt::black);
106 107
    } else if (m_blackCursor < wWidth && m_inverted) {
        p1.fillRect(MARGIN + m_blackCursor, y, wWidth - m_blackCursor, gradientHeight, Qt::black);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
108
    }
109

110 111 112 113 114 115

    int left = qMin(m_blackCursor, m_whiteCursor);
    int right = qMax(m_blackCursor, m_whiteCursor);
    for (x = left; x <= right; ++x) {
        double inten = (double)(x - m_blackCursor) /
                       (double)(m_whiteCursor - m_blackCursor);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
116 117 118
        inten = pow(inten, (1.0 / m_gamma));
        int gray = (int)(255 * inten);
        p1.setPen(QColor(gray, gray, gray));
119
        p1.drawLine(x + MARGIN, y, x + MARGIN, y + gradientHeight - 1);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
120
    }
121

Boudewijn Rempt's avatar
Boudewijn Rempt committed
122
    // Draw cursors
123
    y += gradientHeight;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
124
    QPoint a[3];
125
    p1.setPen(Qt::darkGray);
126
    p1.setRenderHint(QPainter::Antialiasing, true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
127

128 129
    const int cursorHalfBase = (int)(gradientHeight / 1.5);

130 131 132
    a[0] = QPoint(m_blackCursor + MARGIN, y);
    a[1] = QPoint(m_blackCursor + MARGIN + cursorHalfBase, wHeight - 1);
    a[2] = QPoint(m_blackCursor + MARGIN - cursorHalfBase, wHeight - 1);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
133 134 135
    p1.setBrush(Qt::black);
    p1.drawPolygon(a, 3);

136
    p1.setPen(Qt::black);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
137
    if (m_gammaEnabled) {
138 139 140
        a[0] = QPoint(m_gammaCursor + MARGIN, y);
        a[1] = QPoint(m_gammaCursor + MARGIN + cursorHalfBase, wHeight - 1);
        a[2] = QPoint(m_gammaCursor + MARGIN - cursorHalfBase, wHeight - 1);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
141
        p1.setBrush(Qt::gray);
142
        p1.drawPolygon(a, 3);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
143
    }
144

145
    if (m_whiteEnabled) {
146 147 148
        a[0] = QPoint(m_whiteCursor + MARGIN, y);
        a[1] = QPoint(m_whiteCursor + MARGIN + cursorHalfBase, wHeight - 1);
        a[2] = QPoint(m_whiteCursor + MARGIN - cursorHalfBase, wHeight - 1);
149 150 151
        p1.setBrush(Qt::white);
        p1.drawPolygon(a, 3);
    }
152
}
153

154
void KisGradientSlider::resizeEvent(QResizeEvent *)
155
{
156
    m_scalingFactor = (double)(width() - 2 * MARGIN) / 255;
157 158
    calculateCursorPositions();
    update();
159 160
}

161
void KisGradientSlider::mousePressEvent(QMouseEvent *e)
162 163 164
{
    eCursor closest_cursor;
    int distance;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
165

166 167
    if (e->button() != Qt::LeftButton)
        return;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
168

169
    unsigned int x = e->pos().x();
170
    int xMinusMargin = x - MARGIN;
171

172
    distance = width() + 1; // just a big number
173

174 175
    if (abs((int)(xMinusMargin - m_blackCursor)) < distance) {
        distance = abs((int)(xMinusMargin - m_blackCursor));
176 177 178
        closest_cursor = BlackCursor;
    }

179 180
    if (abs((int)(xMinusMargin - m_whiteCursor)) < distance) {
        distance = abs((int)(xMinusMargin - m_whiteCursor));
181 182 183
        closest_cursor = WhiteCursor;
    }

184
    if (m_gammaEnabled) {
185
        int gammaDistance = (int)xMinusMargin - m_gammaCursor;
186 187

        if (abs(gammaDistance) < distance) {
188
            distance = abs((int)xMinusMargin - m_gammaCursor);
189 190
            closest_cursor = GammaCursor;
        } else if (abs(gammaDistance) == distance) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
191
            if ((closest_cursor == BlackCursor) && (gammaDistance > 0)) {
192 193
                distance = abs(gammaDistance);
                closest_cursor = GammaCursor;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
194
            } else if ((closest_cursor == WhiteCursor) && (gammaDistance < 0)) {
195 196 197 198
                distance = abs(gammaDistance);
                closest_cursor = GammaCursor;
            }
        }
199 200
    }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
201
    if (distance > 20) {
202
        m_grabCursor = None;
203 204 205 206 207 208
        return;
    }

    // Determine cursor values and the leftmost and rightmost points.

    switch (closest_cursor) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
209
    case BlackCursor:
210
        m_blackCursor = xMinusMargin;
211
        m_grabCursor = closest_cursor;
212 213 214 215 216 217 218
        if (m_inverted) {
            m_leftmost = m_whiteCursor + 1;
            m_rightmost = width() - 2 * MARGIN - 1;
        } else {
            m_leftmost = 0;
            m_rightmost = m_whiteCursor - 1;
        }
219 220
        if (m_gammaEnabled)
            m_gammaCursor = calculateGammaCursor();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
221 222
        break;
    case WhiteCursor:
223
        m_whiteCursor = xMinusMargin;
224
        m_grabCursor = closest_cursor;
225 226 227 228 229 230 231
        if (m_inverted) {
            m_leftmost = 0;
            m_rightmost = m_blackCursor - 1;
        } else {
            m_leftmost = m_blackCursor + 1;
            m_rightmost = width() - 2 * MARGIN - 1;
        }
232 233
        if (m_gammaEnabled)
            m_gammaCursor = calculateGammaCursor();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
234 235
        break;
    case GammaCursor:
236 237
        m_gammaCursor = x;
        m_grabCursor = closest_cursor;
238 239
        m_leftmost = qMin(m_blackCursor, m_whiteCursor);
        m_rightmost = qMax(m_blackCursor, m_whiteCursor);
240 241
    {
        double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0;
242
        double mid = (double)m_blackCursor + delta + MARGIN;
243
        double tmp = (xMinusMargin - mid) / delta;
244 245
        m_gamma = 1.0 / pow(10, tmp);
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
246
        break;
247
    default:
Boudewijn Rempt's avatar
Boudewijn Rempt committed
248
        break;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
249
    }
250
    update();
251 252
}

253
void KisGradientSlider::mouseReleaseEvent(QMouseEvent * e)
254 255 256 257
{
    if (e->button() != Qt::LeftButton)
        return;

258
    update();
259

260
    switch (m_grabCursor) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
261
    case BlackCursor:
262
        m_black = qRound(m_blackCursor / m_scalingFactor);
263 264
        m_feedback = true;
        emit sigModifiedBlack(m_black);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
265 266
        break;
    case WhiteCursor:
267
        m_white = qRound(m_whiteCursor / m_scalingFactor);
268 269
        m_feedback = true;
        emit sigModifiedWhite(m_white);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
270 271
        break;
    case GammaCursor:
272 273 274
        emit sigModifiedGamma(m_gamma);
        break;
    default:
Boudewijn Rempt's avatar
Boudewijn Rempt committed
275
        break;
276
    }
277 278 279

    m_grabCursor = None;
    m_feedback = false;
280 281
}

282
void KisGradientSlider::mouseMoveEvent(QMouseEvent * e)
283
{
284
    int x = e->pos().x();
285
    int xMinusMargin = x - MARGIN;
286
    if (m_grabCursor != None) { // Else, drag the selected point
287 288
        if (xMinusMargin <= m_leftmost)
            xMinusMargin = m_leftmost;
289

290 291
        if (xMinusMargin >= m_rightmost)
            xMinusMargin = m_rightmost;
292

293
        switch (m_grabCursor) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
294
        case BlackCursor:
295 296
            if (m_blackCursor != xMinusMargin) {
                m_blackCursor = xMinusMargin;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
297
                if (m_gammaEnabled) {
298
                    m_gammaCursor = calculateGammaCursor();
299
                }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
300 301 302
            }
            break;
        case WhiteCursor:
303 304
            if (m_whiteCursor != xMinusMargin) {
                m_whiteCursor = xMinusMargin;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
305
                if (m_gammaEnabled) {
306
                    m_gammaCursor = calculateGammaCursor();
307
                }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
308 309 310
            }
            break;
        case GammaCursor:
311 312
            if (m_gammaCursor != xMinusMargin) {
                m_gammaCursor = xMinusMargin;
313 314
                double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0;
                double mid = (double)m_blackCursor + delta;
315
                double tmp = (xMinusMargin - mid) / delta;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
316 317 318
                m_gamma = 1.0 / pow(10, tmp);
            }
            break;
319 320
        default:
            break;
321 322 323
        }
    }

324
    update();
325 326
}

327
void KisGradientSlider::calculateCursorPositions()
328
{
329
    m_blackCursor = qRound(m_black * m_scalingFactor);
330
    m_whiteCursor = qRound(m_white * m_scalingFactor);
331

332 333 334
    m_gammaCursor = calculateGammaCursor();
}

335
unsigned int KisGradientSlider::calculateGammaCursor()
336 337
{
    double delta = (double)(m_whiteCursor - m_blackCursor) / 2.0;
338 339
    double mid = (double)m_blackCursor + delta;
    double tmp = log10(1.0 / m_gamma);
340
    return (unsigned int)qRound(mid + delta * tmp);
341 342 343
}


344
void KisGradientSlider::enableGamma(bool b)
345 346
{
    m_gammaEnabled = b;
347
    update();
348 349
}

350
double KisGradientSlider::getGamma(void)
351 352 353 354
{
    return m_gamma;
}

355 356 357 358 359 360 361

void KisGradientSlider::enableWhite(bool b)
{
    m_whiteEnabled = b;
    update();
}

362 363 364 365 366 367
void KisGradientSlider::setInverted(bool b)
{
    m_inverted = b;
    update();
}

368
void KisGradientSlider::slotModifyBlack(int v)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
369
{
370 371 372 373 374 375 376 377 378
    if ((m_inverted && (v < m_white || v > width())) ||
        (!m_inverted && (v < 0 || v > m_white)) ||
        m_feedback)
        return;

    m_black = v;
    m_blackCursor = qRound(m_black * m_scalingFactor);
    m_gammaCursor = calculateGammaCursor();
    update();
379
}
380

381
void KisGradientSlider::slotModifyWhite(int v)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
382
{
383 384 385 386 387 388 389 390
    if ((m_inverted && (v < 0 || v > m_white)) ||
        (!m_inverted && (v < m_black && v > width())) ||
        m_feedback)
        return;
    m_white = v;
    m_whiteCursor = qRound(m_white * m_scalingFactor);
    m_gammaCursor = calculateGammaCursor();
    update();
391
}
392

393
void KisGradientSlider::slotModifyGamma(double v)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
394 395
{
    if (m_gamma != v) {
396
        emit sigModifiedGamma(v);
397
    }
398
    m_gamma = v;
399
    m_gammaCursor = calculateGammaCursor();
400
    update();
401 402
}