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