1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2004-12-01
7 * Description : a widget to draw histogram curves
8 *
9 * Copyright (C) 2004-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "curveswidget.h"
25
26 // C++ includes
27
28 #include <cmath>
29 #include <cstdlib>
30
31 // Qt includes
32
33 #include <QPixmap>
34 #include <QPainter>
35 #include <QPoint>
36 #include <QPen>
37 #include <QEvent>
38 #include <QTimer>
39 #include <QRect>
40 #include <QColor>
41 #include <QFont>
42 #include <QFontMetrics>
43 #include <QPaintEvent>
44 #include <QMouseEvent>
45 #include <QPainterPath>
46
47 // KDE includes
48
49 #include <kconfiggroup.h>
50 #include <klocalizedstring.h>
51
52 // Local includes
53
54 #include "dlayoutbox.h"
55 #include "dimg.h"
56 #include "imagehistogram.h"
57 #include "imagecurves.h"
58 #include "digikam_globals.h"
59 #include "digikam_debug.h"
60 #include "histogrampainter.h"
61 #include "dworkingpixmap.h"
62
63 namespace Digikam
64 {
65
66 class Q_DECL_HIDDEN CurvesWidget::Private
67 {
68 public:
69
70 enum RepaintType
71 {
72 HistogramDataLoading = 0, ///< Image Data loading in progress.
73 HistogramNone, ///< No current histogram values calculation.
74 HistogramStarted, ///< Histogram values calculation started.
75 HistogramCompleted, ///< Histogram values calculation completed.
76 HistogramFailed ///< Histogram values calculation failed.
77 };
78
79 public:
80
Private(CurvesWidget * const q)81 explicit Private(CurvesWidget* const q)
82 : readOnlyMode(false),
83 guideVisible(false),
84 clearFlag(HistogramNone),
85 leftMost(0),
86 rightMost(0),
87 grabPoint(-1),
88 last(0),
89 xMouseOver(-1),
90 yMouseOver(-1),
91 progressCount(0),
92 channelType(LuminosityChannel),
93 scaleType(LinScaleHistogram),
94 imageHistogram(nullptr),
95 progressTimer(nullptr),
96 progressPix(nullptr),
97 curves(nullptr),
98 histogramPainter(nullptr),
99 q(q)
100 {
101 }
102
103 bool readOnlyMode;
104 bool guideVisible;
105
106 int clearFlag; // Clear drawing zone with message.
107 int leftMost;
108 int rightMost;
109 int grabPoint;
110 int last;
111 int xMouseOver;
112 int yMouseOver;
113 int progressCount; // Position of animation during loading/calculation.
114 ChannelType channelType; // Channel type to draw
115 HistogramScale scaleType; // Scale to use for drawing
116 ImageHistogram* imageHistogram; // Full image
117
118 QTimer* progressTimer;
119
120 DWorkingPixmap* progressPix;
121
122 DColor colorGuide;
123
124 ImageCurves* curves; // Curves data instance.
125
126 HistogramPainter* histogramPainter;
127
128 private:
129
130 CurvesWidget* q;
131
132 public:
133
134 // --- misc methods ---
135
getDelta() const136 int getDelta() const
137 {
138 // TODO magic number, what is this?
139 return imageHistogram->getHistogramSegments() / 16;
140 }
141
getHistogramCoordinates(const QPoint & mousePosition,int & x,int & y,int & closestPoint)142 void getHistogramCoordinates(const QPoint& mousePosition, int& x, int& y, int& closestPoint)
143 {
144 x = CLAMP((int)(mousePosition.x() *
145 ((float)(imageHistogram->getMaxSegmentIndex()) / (float)q->width())),
146 0, imageHistogram->getMaxSegmentIndex());
147 y = CLAMP((int)(mousePosition.y() *
148 ((float)(imageHistogram->getMaxSegmentIndex()) / (float)q->height())),
149 0, imageHistogram->getMaxSegmentIndex());
150
151 int distance = NUM_SEGMENTS_16BIT;
152
153 closestPoint = 0;
154
155 for (int i = 0 ; i < ImageCurves::NUM_POINTS ; ++i)
156 {
157 int xcurvepoint = curves->getCurvePointX(channelType, i);
158
159 if (xcurvepoint != -1)
160 {
161 if (abs(x - xcurvepoint) < distance)
162 {
163 distance = abs(x - xcurvepoint);
164 closestPoint = i;
165 }
166 }
167 }
168
169 // TODO magic number, what is this?
170
171 if (distance > 8)
172 {
173 closestPoint = (x + getDelta() / 2) / getDelta();
174 }
175 }
176
177 // --- rendering ---
178
renderLoadingAnimation()179 void renderLoadingAnimation()
180 {
181
182 QPixmap anim(progressPix->frameAt(progressCount));
183 ++progressCount;
184
185 if (progressCount >= progressPix->frameCount())
186 {
187 progressCount = 0;
188 }
189
190 // ... and we render busy text.
191
192 QPainter p1(q);
193 p1.fillRect(0, 0, q->width(), q->height(), q->palette().color(QPalette::Active, QPalette::Window));
194 p1.setPen(QPen(q->palette().color(QPalette::Active, QPalette::WindowText), 1, Qt::SolidLine));
195 p1.drawRect(0, 0, q->width() - 1, q->height() - 1);
196 p1.drawPixmap(q->width() / 2 - anim.width() / 2, anim.height(), anim);
197 p1.setPen(q->palette().color(QPalette::Active, QPalette::Text));
198
199 if (clearFlag == Private::HistogramDataLoading)
200 {
201 p1.drawText(0, 0, q->width(), q->height(), Qt::AlignCenter,
202 i18n("Loading image..."));
203 }
204 else
205 {
206 p1.drawText(0, 0, q->width(), q->height(), Qt::AlignCenter,
207 i18n("Histogram calculation..."));
208 }
209
210 p1.end();
211 }
212
renderHistogramFailed()213 void renderHistogramFailed()
214 {
215 QPainter p1(q);
216 p1.fillRect(0, 0, q->width(), q->height(), q->palette().color(QPalette::Active, QPalette::Window));
217 p1.setPen(QPen(q->palette().color(QPalette::Active, QPalette::WindowText), 1, Qt::SolidLine));
218 p1.drawRect(0, 0, q->width() - 1, q->height() - 1);
219 p1.setPen(q->palette().color(QPalette::Active, QPalette::Text));
220 p1.drawText(0, 0, q->width(), q->height(), Qt::AlignCenter,
221 i18n("Histogram\ncalculation\nfailed."));
222 p1.end();
223 }
224
renderCurve(QPixmap & pm)225 void renderCurve(QPixmap& pm)
226 {
227 QPainter p1;
228 p1.begin(&pm);
229 initPainterFromWidget(&p1);
230
231 int wWidth = pm.width();
232 int wHeight = pm.height();
233
234 // Drawing curves.
235 QPainterPath curvePath;
236 curvePath.moveTo(0, wHeight);
237
238 for (int x = 0 ; x < wWidth ; ++x)
239 {
240
241 // TODO duplicate code...
242 int i = (x * imageHistogram->getHistogramSegments()) / wWidth;
243
244 int curveVal = curves->getCurveValue(channelType, i);
245 curvePath.lineTo(x, wHeight - ((curveVal * wHeight) / imageHistogram->getHistogramSegments()));
246 }
247
248 curvePath.lineTo(wWidth, wHeight);
249
250 p1.save();
251 p1.setRenderHint(QPainter::Antialiasing);
252 p1.setPen(QPen(q->palette().color(QPalette::Active, QPalette::Link), 2, Qt::SolidLine));
253 p1.drawPath(curvePath);
254 p1.restore();
255
256
257 // Drawing curves points.
258 if (!readOnlyMode && curves->getCurveType(channelType) == ImageCurves::CURVE_SMOOTH)
259 {
260
261 p1.save();
262 p1.setPen(QPen(Qt::red, 3, Qt::SolidLine));
263 p1.setRenderHint(QPainter::Antialiasing);
264
265 for (int p = 0 ; p < ImageCurves::NUM_POINTS ; ++p)
266 {
267 QPoint curvePoint = curves->getCurvePoint(channelType, p);
268
269 if (curvePoint.x() >= 0)
270 {
271 p1.drawEllipse(((curvePoint.x() * wWidth) / imageHistogram->getHistogramSegments()) - 2,
272 wHeight - 2 - ((curvePoint.y() * wHeight) / imageHistogram->getHistogramSegments()),
273 4, 4);
274 }
275 }
276
277 p1.restore();
278 }
279 }
280
renderGrid(QPixmap & pm)281 void renderGrid(QPixmap& pm)
282 {
283 QPainter p1;
284 p1.begin(&pm);
285 initPainterFromWidget(&p1);
286
287 int wWidth = pm.width();
288 int wHeight = pm.height();
289
290 // Drawing black/middle/highlight tone grid separators.
291 p1.setPen(QPen(q->palette().color(QPalette::Active, QPalette::Base), 1, Qt::SolidLine));
292 p1.drawLine(wWidth / 4, 0, wWidth / 4, wHeight);
293 p1.drawLine(wWidth / 2, 0, wWidth / 2, wHeight);
294 p1.drawLine(3 * wWidth / 4, 0, 3 * wWidth / 4, wHeight);
295 p1.drawLine(0, wHeight / 4, wWidth, wHeight / 4);
296 p1.drawLine(0, wHeight / 2, wWidth, wHeight / 2);
297 p1.drawLine(0, 3 * wHeight / 4, wWidth, 3 * wHeight / 4);
298
299 }
300
renderMousePosition(QPixmap & pm)301 void renderMousePosition(QPixmap& pm)
302 {
303 QPainter p1;
304 p1.begin(&pm);
305 initPainterFromWidget(&p1);
306
307 int wWidth = pm.width();
308 int wHeight = pm.height();
309
310 // Drawing X,Y point position dragged by mouse over widget.
311 p1.setPen(QPen(Qt::red, 1, Qt::DotLine));
312
313 if ((xMouseOver != -1) && (yMouseOver != -1))
314 {
315 QString string = i18n("x:%1\ny:%2", xMouseOver, yMouseOver);
316 QFontMetrics fontMt(string);
317 QRect rect = fontMt.boundingRect(0, 0, wWidth, wHeight, 0, string);
318 rect.moveRight(wWidth);
319 rect.moveBottom(wHeight);
320 p1.drawText(rect, Qt::AlignLeft | Qt::AlignTop, string);
321 }
322 }
323
renderFrame(QPixmap & pm)324 void renderFrame(QPixmap& pm)
325 {
326 QPainter p1;
327 p1.begin(&pm);
328 initPainterFromWidget(&p1);
329
330 p1.setPen(QPen(q->palette().color(QPalette::Active,
331 QPalette::WindowText), 1, Qt::SolidLine));
332 p1.drawRect(0, 0, pm.width() - 1, pm.height() - 1);
333 }
334
initPainterFromWidget(QPainter * const p)335 void initPainterFromWidget(QPainter* const p)
336 {
337 if (!p || !q)
338 {
339 return;
340 }
341
342 const QPalette& pal = q->palette();
343 p->setPen(QPen(pal.brush(q->foregroundRole()), 0));
344 p->setBackground(pal.brush(q->backgroundRole()));
345 p->setFont(q->font());
346 }
347
348 // --- patterns for storing / restoring state ---
349
getChannelTypeOption(const QString & prefix,int channel)350 static QString getChannelTypeOption(const QString& prefix, int channel)
351 {
352 return QString(prefix + QString::fromLatin1("Channel%1Type")).arg(channel);
353 }
354
getPointOption(const QString & prefix,int channel,int point)355 static QString getPointOption(const QString& prefix, int channel, int point)
356 {
357 return QString(prefix + QString::fromLatin1("Channel%1Point%2")).arg(channel).arg(point);
358 }
359 };
360
CurvesWidget(int w,int h,QWidget * const parent,bool readOnly)361 CurvesWidget::CurvesWidget(int w, int h, QWidget* const parent, bool readOnly)
362 : QWidget(parent),
363 d(new Private(this))
364 {
365 setAttribute(Qt::WA_DeleteOnClose);
366 setup(w, h, readOnly);
367 }
368
~CurvesWidget()369 CurvesWidget::~CurvesWidget()
370 {
371 d->progressTimer->stop();
372
373 delete d->imageHistogram;
374 delete d->curves;
375 delete d;
376 }
377
setup(int w,int h,bool readOnly)378 void CurvesWidget::setup(int w, int h, bool readOnly)
379 {
380 d->progressPix = new DWorkingPixmap(this);
381 d->readOnlyMode = readOnly;
382 d->curves = new ImageCurves(true);
383 d->histogramPainter = new HistogramPainter(this);
384 d->histogramPainter->setChannelType(LuminosityChannel);
385 d->histogramPainter->setRenderXGrid(false);
386 d->histogramPainter->setHighlightSelection(false);
387 d->histogramPainter->initFrom(this);
388 d->channelType = LuminosityChannel;
389 d->scaleType = LogScaleHistogram;
390 d->imageHistogram = nullptr;
391
392 setMouseTracking(true);
393 setMinimumSize(w, h);
394
395 d->progressTimer = new QTimer(this);
396
397 connect(d->progressTimer, SIGNAL(timeout()),
398 this, SLOT(slotProgressTimerDone()));
399 }
400
saveCurve(KConfigGroup & group,const QString & prefix)401 void CurvesWidget::saveCurve(KConfigGroup& group, const QString& prefix)
402 {
403 qCDebug(DIGIKAM_DIMG_LOG) << "Storing curves";
404
405 for (int channel = 0 ; channel < ImageCurves::NUM_CHANNELS ; ++channel)
406 {
407
408 group.writeEntry(Private::getChannelTypeOption(prefix, channel),
409 (int) curves()->getCurveType(channel));
410
411 for (int point = 0 ; point <= ImageCurves::NUM_POINTS ; ++point)
412 {
413 QPoint p = curves()->getCurvePoint(channel, point);
414
415 if (!isSixteenBits() && (p != ImageCurves::getDisabledValue()))
416 {
417 // Store point as 16 bits depth.
418
419 p.setX(p.x() * ImageCurves::MULTIPLIER_16BIT);
420 p.setY(p.y() * ImageCurves::MULTIPLIER_16BIT);
421 }
422
423 group.writeEntry(Private::getPointOption(prefix, channel, point), p);
424 }
425 }
426 }
427
restoreCurve(KConfigGroup & group,const QString & prefix)428 void CurvesWidget::restoreCurve(KConfigGroup& group, const QString& prefix)
429 {
430 qCDebug(DIGIKAM_DIMG_LOG) << "Restoring curves";
431
432 reset();
433
434 qCDebug(DIGIKAM_DIMG_LOG) << "curves " << curves() << " isSixteenBits = " << isSixteenBits();
435
436 for (int channel = 0 ; channel < ImageCurves::NUM_CHANNELS ; ++channel)
437 {
438
439 curves()->setCurveType(channel, (ImageCurves::CurveType) group.readEntry(
440 Private::getChannelTypeOption(
441 prefix, channel), 0));
442
443 for (int point = 0 ; point <= ImageCurves::NUM_POINTS ; ++point)
444 {
445 QPoint p = group.readEntry(Private::getPointOption(prefix,
446 channel, point),
447 ImageCurves::getDisabledValue());
448
449 // always load a 16 bit curve and stretch it to 8 bit if necessary
450 if (!isSixteenBits() && (p != ImageCurves::getDisabledValue()))
451 {
452 p.setX(p.x() / ImageCurves::MULTIPLIER_16BIT);
453 p.setY(p.y() / ImageCurves::MULTIPLIER_16BIT);
454 }
455
456 curves()->setCurvePoint(channel, point, p);
457 }
458
459 curves()->curvesCalculateCurve(channel);
460 }
461 }
462
updateData(const DImg & img)463 void CurvesWidget::updateData(const DImg& img)
464 {
465 qCDebug(DIGIKAM_DIMG_LOG) << "updating data";
466
467 stopHistogramComputation();
468
469 // Remove old histogram data from memory.
470 delete d->imageHistogram;
471 d->imageHistogram = new ImageHistogram(img);
472
473 connect(d->imageHistogram, SIGNAL(calculationStarted()),
474 this, SLOT(slotCalculationStarted()));
475
476 connect(d->imageHistogram, SIGNAL(calculationFinished(bool)),
477 this, SLOT(slotCalculationFinished(bool)));
478
479 d->imageHistogram->calculateInThread();
480
481 // keep the old curve
482 ImageCurves* const newCurves = new ImageCurves(img.sixteenBit());
483 newCurves->setCurveType(ImageCurves::CURVE_SMOOTH);
484
485 if (d->curves)
486 {
487 newCurves->fillFromOtherCurves(d->curves);
488 delete d->curves;
489 }
490
491 d->curves = newCurves;
492
493 resetUI();
494 }
495
isSixteenBits() const496 bool CurvesWidget::isSixteenBits() const
497 {
498 return curves()->isSixteenBits();
499 }
500
reset()501 void CurvesWidget::reset()
502 {
503 if (d->curves)
504 {
505 d->curves->curvesReset();
506 }
507
508 resetUI();
509 }
510
resetUI()511 void CurvesWidget::resetUI()
512 {
513 d->grabPoint = -1;
514 d->guideVisible = false;
515 update();
516 }
517
curves() const518 ImageCurves* CurvesWidget::curves() const
519 {
520 return d->curves;
521 }
522
setDataLoading()523 void CurvesWidget::setDataLoading()
524 {
525 if (d->clearFlag != Private::HistogramDataLoading)
526 {
527 setCursor(Qt::WaitCursor);
528 d->clearFlag = Private::HistogramDataLoading;
529 d->progressCount = 0;
530 d->progressTimer->start(100);
531 }
532 }
533
setLoadingFailed()534 void CurvesWidget::setLoadingFailed()
535 {
536 d->clearFlag = Private::HistogramFailed;
537 d->progressCount = 0;
538 d->progressTimer->stop();
539 update();
540 setCursor(Qt::ArrowCursor);
541 }
542
setCurveGuide(const DColor & color)543 void CurvesWidget::setCurveGuide(const DColor& color)
544 {
545 d->guideVisible = true;
546 d->colorGuide = color;
547 update();
548 }
549
curveTypeChanged()550 void CurvesWidget::curveTypeChanged()
551 {
552 switch (d->curves->getCurveType(d->channelType))
553 {
554 case ImageCurves::CURVE_SMOOTH:
555
556 // pick representative points from the curve and make them control points
557 int index;
558
559 for (int i = 0 ; i <= 16 ; ++i)
560 {
561 index = CLAMP(i * d->imageHistogram->getHistogramSegments() / 16, 0, d->imageHistogram->getMaxSegmentIndex());
562 d->curves->setCurvePoint(d->channelType, i, QPoint(index, d->curves->getCurveValue(d->channelType, index)));
563 }
564
565 d->curves->curvesCalculateCurve(d->channelType);
566 break;
567
568 case ImageCurves::CURVE_FREE:
569 break;
570 }
571
572 update();
573 emit signalCurvesChanged();
574 }
575
slotCalculationStarted()576 void CurvesWidget::slotCalculationStarted()
577 {
578 setCursor(Qt::WaitCursor);
579 d->clearFlag = Private::HistogramStarted;
580 d->progressTimer->start(200);
581 update();
582 }
583
slotCalculationFinished(bool success)584 void CurvesWidget::slotCalculationFinished(bool success)
585 {
586 if (success)
587 {
588 // Repaint histogram
589
590 d->clearFlag = Private::HistogramCompleted;
591 d->progressTimer->stop();
592 update();
593 setCursor(Qt::ArrowCursor);
594 }
595 else
596 {
597 d->clearFlag = Private::HistogramFailed;
598 d->progressTimer->stop();
599 update();
600 setCursor(Qt::ArrowCursor);
601 emit signalHistogramComputationFailed();
602 }
603 }
604
stopHistogramComputation()605 void CurvesWidget::stopHistogramComputation()
606 {
607 if (d->imageHistogram)
608 {
609 d->imageHistogram->stopCalculation();
610 }
611
612 d->progressTimer->stop();
613 d->progressCount = 0;
614 }
615
slotProgressTimerDone()616 void CurvesWidget::slotProgressTimerDone()
617 {
618 update();
619 d->progressTimer->start(200);
620 }
621
paintEvent(QPaintEvent *)622 void CurvesWidget::paintEvent(QPaintEvent*)
623 {
624 // special cases
625
626 if (d->clearFlag == Private::HistogramDataLoading ||
627 d->clearFlag == Private::HistogramStarted)
628 {
629 d->renderLoadingAnimation();
630 return;
631 }
632 else if (d->clearFlag == Private::HistogramFailed)
633 {
634 d->renderHistogramFailed();
635 return;
636 }
637
638 // normal case, histogram present
639
640 if (!d->imageHistogram)
641 {
642 qCWarning(DIGIKAM_DIMG_LOG) << "Should render a histogram, but did not get one.";
643 return;
644 }
645
646 // render subelements on a pixmap (double buffering)
647
648 QPixmap pm(size());
649
650 d->histogramPainter->setScale(d->scaleType);
651 d->histogramPainter->setHistogram(d->imageHistogram);
652 d->histogramPainter->setChannelType(d->channelType);
653
654 if (d->guideVisible)
655 {
656 d->histogramPainter->enableHistogramGuideByColor(d->colorGuide);
657 }
658 else
659 {
660 d->histogramPainter->disableHistogramGuide();
661 }
662
663 d->histogramPainter->render(pm);
664 d->renderCurve(pm);
665 d->renderGrid(pm);
666 d->renderMousePosition(pm);
667 d->renderFrame(pm);
668
669 // render pixmap on widget
670
671 QPainter p2(this);
672 p2.drawPixmap(0, 0, pm);
673 p2.end();
674 }
675
mousePressEvent(QMouseEvent * e)676 void CurvesWidget::mousePressEvent(QMouseEvent* e)
677 {
678 if (d->readOnlyMode || !d->imageHistogram)
679 {
680 return;
681 }
682
683 if (d->clearFlag == Private::HistogramStarted)
684 {
685 return;
686 }
687
688 int x, y, closest_point;
689 d->getHistogramCoordinates(e->pos(), x, y, closest_point);
690
691 setCursor(Qt::CrossCursor);
692
693 switch (d->curves->getCurveType(d->channelType))
694 {
695 case ImageCurves::CURVE_SMOOTH:
696 {
697 // Determine the leftmost and rightmost points.
698
699 d->leftMost = -1;
700
701 for (int i = closest_point - 1 ; i >= 0 ; --i)
702 {
703 if (d->curves->getCurvePointX(d->channelType, i) != -1)
704 {
705 d->leftMost = d->curves->getCurvePointX(d->channelType, i);
706 break;
707 }
708 }
709
710 d->rightMost = d->imageHistogram->getHistogramSegments();
711
712 for (int i = closest_point + 1 ; i < ImageCurves::NUM_POINTS ; ++i)
713 {
714 if (d->curves->getCurvePointX(d->channelType, i) != -1)
715 {
716 d->rightMost = d->curves->getCurvePointX(d->channelType, i);
717 break;
718 }
719 }
720
721 d->grabPoint = closest_point;
722
723 if (e->button() == Qt::LeftButton)
724 {
725 d->curves->setCurvePoint(d->channelType, d->grabPoint,
726 QPoint(x, d->imageHistogram->getHistogramSegments() - y));
727
728 }
729 else if (e->button() == Qt::RightButton)
730 {
731 int interval = x - d->curves->getCurvePointX(d->channelType, closest_point);
732
733 if (interval <= 3 && interval >= -3)
734 {
735 d->curves->unsetCurvePoint(d->channelType, closest_point);
736 }
737 }
738
739 break;
740 }
741
742 case ImageCurves::CURVE_FREE:
743 {
744 d->curves->setCurveValue(d->channelType, x, d->imageHistogram->getHistogramSegments() - y);
745 d->grabPoint = x;
746 d->last = y;
747 break;
748 }
749 }
750
751 d->curves->curvesCalculateCurve(d->channelType);
752 emit signalCurvesChanged();
753 update();
754 }
755
mouseReleaseEvent(QMouseEvent * e)756 void CurvesWidget::mouseReleaseEvent(QMouseEvent* e)
757 {
758 if (d->readOnlyMode || !d->imageHistogram)
759 {
760 return;
761 }
762
763 if (((e->button() == Qt::LeftButton) &&
764 (e->button() == Qt::RightButton)) ||
765 d->clearFlag == Private::HistogramStarted)
766 {
767 return;
768 }
769
770 setCursor(Qt::ArrowCursor);
771 d->grabPoint = -1;
772 }
773
mouseMoveEvent(QMouseEvent * e)774 void CurvesWidget::mouseMoveEvent(QMouseEvent* e)
775 {
776 if (d->readOnlyMode || !d->imageHistogram)
777 {
778 return;
779 }
780
781 if ((e->buttons() == Qt::RightButton) ||
782 (d->clearFlag == Private::HistogramStarted))
783 {
784 return;
785 }
786
787 int x, y, closest_point;
788 d->getHistogramCoordinates(e->pos(), x, y, closest_point);
789
790 switch (d->curves->getCurveType(d->channelType))
791 {
792 case ImageCurves::CURVE_SMOOTH:
793 {
794 if (d->grabPoint == -1) // If no point is grabbed...
795 {
796 if (d->curves->getCurvePointX(d->channelType, closest_point) != -1)
797 {
798 setCursor(Qt::ArrowCursor);
799 }
800 else
801 {
802 setCursor(Qt::CrossCursor);
803 }
804 }
805 else // Else, drag the grabbed point
806 {
807 setCursor(Qt::CrossCursor);
808
809 d->curves->setCurvePointX(d->channelType, d->grabPoint, -1);
810
811 if (x > d->leftMost && x < d->rightMost)
812 {
813 closest_point = (x + d->getDelta() / 2) / d->getDelta();
814
815 if (d->curves->getCurvePointX(d->channelType, closest_point) == -1)
816 {
817 d->grabPoint = closest_point;
818 }
819
820 d->curves->setCurvePoint(d->channelType, d->grabPoint,
821 QPoint(x, d->imageHistogram->getMaxSegmentIndex() - y));
822 }
823
824 d->curves->curvesCalculateCurve(d->channelType);
825 emit signalCurvesChanged();
826 }
827
828 break;
829 }
830
831 case ImageCurves::CURVE_FREE:
832 {
833 if (d->grabPoint != -1)
834 {
835 int x1, x2, y1, y2;
836
837 if (d->grabPoint > x)
838 {
839 x1 = x;
840 x2 = d->grabPoint;
841 y1 = y;
842 y2 = d->last;
843 }
844 else
845 {
846 x1 = d->grabPoint;
847 x2 = x;
848 y1 = d->last;
849 y2 = y;
850 }
851
852 if (x2 != x1)
853 {
854 for (int i = x1 ; i <= x2 ; ++i)
855 d->curves->setCurveValue(d->channelType, i, d->imageHistogram->getMaxSegmentIndex() -
856 (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1)));
857 }
858 else
859 {
860 d->curves->setCurveValue(d->channelType, x, d->imageHistogram->getMaxSegmentIndex() - y);
861 }
862
863 d->grabPoint = x;
864 d->last = y;
865
866 emit signalCurvesChanged();
867 }
868
869 break;
870 }
871 }
872
873 d->xMouseOver = x;
874 d->yMouseOver = d->imageHistogram->getMaxSegmentIndex() - y;
875 emit signalMouseMoved(d->xMouseOver, d->yMouseOver);
876 update();
877 }
878
leaveEvent(QEvent *)879 void CurvesWidget::leaveEvent(QEvent*)
880 {
881 d->xMouseOver = -1;
882 d->yMouseOver = -1;
883 emit signalMouseMoved(d->xMouseOver, d->yMouseOver);
884 update();
885 }
886
setChannelType(ChannelType channel)887 void CurvesWidget::setChannelType(ChannelType channel)
888 {
889 d->channelType = channel;
890 update();
891 }
892
setScaleType(HistogramScale scale)893 void CurvesWidget::setScaleType(HistogramScale scale)
894 {
895 d->scaleType = scale;
896 update();
897 }
898
899 } // namespace Digikam
900