1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2004-11-16
7  * Description : a widget to display an image with guides
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 "imageguidewidget.h"
25 
26 // Qt includes
27 
28 #include <QRegion>
29 #include <QPainter>
30 #include <QPen>
31 #include <QTimer>
32 #include <QRect>
33 #include <QBrush>
34 #include <QFont>
35 #include <QFontMetrics>
36 
37 // KDE includes
38 
39 #include <klocalizedstring.h>
40 
41 // Local includes
42 
43 #include "dimg.h"
44 #include "digikam_debug.h"
45 #include "previewtoolbar.h"
46 #include "exposurecontainer.h"
47 #include "iccsettingscontainer.h"
48 
49 namespace Digikam
50 {
51 
52 static const KLocalizedString beforeLabel = ki18nc("Preview image (before filter has been applied)", "Before");
53 static const KLocalizedString afterLabel  = ki18nc("Preview image (after filter has been applied)",  "After");
54 
55 class Q_DECL_HIDDEN ImageGuideWidget::Private
56 {
57 public:
58 
Private()59     explicit Private()
60       : sixteenBit(false),
61         focus(false),
62         spotVisible(false),
63         onMouseMovePreviewToggled(true),
64         drawLineBetweenPoints(false),
65         drawingMask(false),
66         enableDrawMask(false),
67         eraseMask(false),
68         timerID(0),
69         guideMode(0),
70         guideSize(0),
71         flicker(0),
72         renderingPreviewMode(PreviewToolBar::NoPreviewMode),
73         penWidth(10),
74         pixmap(nullptr),
75         maskPixmap(nullptr),
76         previewPixmap(nullptr),
77         iface(nullptr)
78     {
79     }
80 
81     bool        sixteenBit;
82     bool        focus;
83     bool        spotVisible;
84     bool        onMouseMovePreviewToggled;
85     bool        drawLineBetweenPoints;
86     bool        drawingMask;
87     bool        enableDrawMask;
88     bool        eraseMask;
89 
90     int         timerID;
91     int         guideMode;
92     int         guideSize;
93     int         flicker;
94     int         renderingPreviewMode;
95     int         penWidth;
96 
97     /// Current spot position in preview coordinates.
98     QPoint      spot;
99     QPolygon    selectedPoints;
100 
101     QRect       rect;
102 
103     QColor      guideColor;
104     QColor      paintColor;
105     QColor      bgColor;
106 
107     QPixmap*    pixmap;
108     QPixmap*    maskPixmap;
109     QPixmap*    previewPixmap;
110 
111     QCursor     maskCursor;
112 
113     QPoint      lastPoint;
114 
115     ImageIface* iface;
116 
117     DImg        preview;
118 };
119 
ImageGuideWidget(QWidget * const parent,bool spotVisible,int guideMode,const QColor & guideColor,int guideSize,bool blink,ImageIface::PreviewType type)120 ImageGuideWidget::ImageGuideWidget(QWidget* const parent,
121                                    bool spotVisible,
122                                    int guideMode,
123                                    const QColor& guideColor,
124                                    int guideSize,
125                                    bool blink, ImageIface::PreviewType type)
126     : QWidget(parent),
127       d(new Private)
128 {
129     int w            = 480;
130     int h            = 320;
131     d->spotVisible   = spotVisible;
132     d->guideMode     = guideMode;
133     d->guideColor    = guideColor;
134     d->guideSize     = guideSize;
135     d->bgColor       = palette().color(QPalette::Base);
136 
137     setMinimumSize(w, h);
138     setMouseTracking(true);
139     setAttribute(Qt::WA_DeleteOnClose);
140 
141     d->iface         = new ImageIface(QSize(w, h));
142     d->iface->setPreviewType(type);
143     d->preview       = d->iface->preview();
144     d->preview.setIccProfile(d->iface->original() ? d->iface->original()->getIccProfile() : IccProfile());
145 
146     d->pixmap        = new QPixmap(w, h);
147     d->rect          = QRect(w / 2 - d->preview.width() / 2, h / 2 - d->preview.height() / 2, d->preview.width(), d->preview.height());
148     d->maskPixmap    = new QPixmap(d->rect.width(), d->rect.height());
149     d->previewPixmap = new QPixmap(d->rect.width(), d->rect.height());
150     d->maskPixmap->fill(QColor(0, 0, 0, 0));
151     d->previewPixmap->fill(QColor(0, 0, 0, 0));
152 
153     d->paintColor.setRgb(255, 255, 255, 255);
154 
155     d->lastPoint     = QPoint(d->rect.x(), d->rect.y());
156 
157     resetSpotPosition();
158     setSpotVisible(d->spotVisible, blink);
159 }
160 
~ImageGuideWidget()161 ImageGuideWidget::~ImageGuideWidget()
162 {
163     delete d->iface;
164 
165     if (d->timerID)
166     {
167         killTimer(d->timerID);
168     }
169 
170     delete d->pixmap;
171     delete d->maskPixmap;
172     delete d->previewPixmap;
173     delete d;
174 }
175 
imageIface() const176 ImageIface* ImageGuideWidget::imageIface() const
177 {
178     return d->iface;
179 }
180 
setBackgroundColor(const QColor & bg)181 void ImageGuideWidget::setBackgroundColor(const QColor& bg)
182 {
183     d->bgColor = bg;
184     updatePreview();
185 }
186 
ICCSettingsChanged()187 void ImageGuideWidget::ICCSettingsChanged()
188 {
189     updatePreview();
190 }
191 
exposureSettingsChanged()192 void ImageGuideWidget::exposureSettingsChanged()
193 {
194     updatePreview();
195 }
196 
resetSpotPosition()197 void ImageGuideWidget::resetSpotPosition()
198 {
199     d->spot.setX(d->preview.width()  / 2);
200     d->spot.setY(d->preview.height() / 2);
201     updatePreview();
202 }
203 
slotPreviewModeChanged(int mode)204 void ImageGuideWidget::slotPreviewModeChanged(int mode)
205 {
206     d->renderingPreviewMode = mode;
207     updatePreview();
208 }
209 
previewMode() const210 int ImageGuideWidget::previewMode() const
211 {
212     return (d->renderingPreviewMode);
213 }
214 
getSpotPosition() const215 QPoint ImageGuideWidget::getSpotPosition() const
216 {
217     return (QPoint((int)((float)d->spot.x() * (float)d->iface->originalSize().width()  / (float)d->preview.width()),
218                    (int)((float)d->spot.y() * (float)d->iface->originalSize().height() / (float)d->preview.height())));
219 }
220 
getSpotColor(int getColorFrom) const221 DColor ImageGuideWidget::getSpotColor(int getColorFrom) const
222 {
223     if      (getColorFrom == OriginalImage)                          // Get point color from full original image
224     {
225         return (d->iface->colorInfoFromOriginal(getSpotPosition()));
226     }
227     else if (getColorFrom == PreviewImage)                      // Get point color from full preview image
228     {
229         return (d->iface->colorInfoFromPreview(d->spot));
230     }
231 
232     // In other cases, get point color from preview target image
233 
234     return (d->iface->colorInfoFromTargetPreview(d->spot));
235 }
236 
setSpotVisible(bool spotVisible,bool blink)237 void ImageGuideWidget::setSpotVisible(bool spotVisible, bool blink)
238 {
239     d->spotVisible = spotVisible;
240 
241     if (blink)
242     {
243         if (d->spotVisible)
244         {
245             d->timerID = startTimer(800);
246         }
247         else
248         {
249             killTimer(d->timerID);
250             d->timerID = 0;
251         }
252     }
253 
254     updatePreview();
255 }
256 
setSpotVisibleNoUpdate(bool spotVisible)257 void ImageGuideWidget::setSpotVisibleNoUpdate(bool spotVisible)
258 {
259     d->spotVisible = spotVisible;
260 }
261 
slotChangeGuideColor(const QColor & color)262 void ImageGuideWidget::slotChangeGuideColor(const QColor& color)
263 {
264     d->guideColor = color;
265     updatePreview();
266 }
267 
slotChangeGuideSize(int size)268 void ImageGuideWidget::slotChangeGuideSize(int size)
269 {
270     d->guideSize = size;
271     updatePreview();
272 }
273 
updatePreview()274 void ImageGuideWidget::updatePreview()
275 {
276     updatePixmap();
277     update();
278 }
279 
updatePixmap()280 void ImageGuideWidget::updatePixmap()
281 {
282     QPainter p(d->pixmap);
283     p.setRenderHint(QPainter::Antialiasing, true);
284     p.setBackgroundMode(Qt::TransparentMode);
285 
286     QString text;
287     p.setPen(QPen(Qt::red, 1));
288 
289     d->pixmap->fill(d->bgColor);
290 
291     if      ((d->renderingPreviewMode == PreviewToolBar::PreviewOriginalImage) ||
292              ((d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver) && !d->onMouseMovePreviewToggled))
293     {
294         p.drawPixmap(d->rect, *d->previewPixmap);
295         drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + 20), beforeLabel.toString());
296     }
297     else if ((d->renderingPreviewMode == PreviewToolBar::PreviewTargetImage) ||
298              (d->renderingPreviewMode == PreviewToolBar::NoPreviewMode)      ||
299              ((d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver) && d->onMouseMovePreviewToggled))
300     {
301         d->iface->paint(d->pixmap, d->rect, &p);
302 
303         if ((d->renderingPreviewMode == PreviewToolBar::PreviewTargetImage) ||
304             (d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver))
305         {
306             drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + 20), afterLabel.toString());
307         }
308     }
309     else if ((d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesVert) ||
310              (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesVertCont))
311     {
312         if (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesVert)
313         {
314             // Drawing original image.
315 
316             p.drawPixmap(d->rect, *d->previewPixmap);
317 
318             // Drawing target image under the original.
319 
320             d->iface->paint(d->pixmap, QRect(d->rect.x() + d->rect.width() / 2, d->rect.y(), d->rect.width() / 2, d->rect.height()), &p);
321         }
322         else
323         {
324             // Drawing target image.
325 
326             d->iface->paint(d->pixmap, d->rect, &p);
327 
328             // Drawing original image under the target.
329 
330             p.drawPixmap(d->rect.x(), d->rect.y(), *d->previewPixmap, 0, 0, d->rect.width() / 2, d->rect.height());
331         }
332 
333         // Drawing information and others stuff.
334 
335         p.fillRect(d->rect.right(), 0, width(), height(), palette().color(QPalette::Window));
336 
337         p.setPen(QPen(Qt::white, 2, Qt::SolidLine));
338         p.drawLine(d->rect.x() + d->rect.width() / 2 - 1, d->rect.y(),
339                    d->rect.x() + d->rect.width() / 2 - 1, d->rect.y() + d->rect.height());
340         p.setPen(QPen(Qt::red, 2, Qt::DotLine));
341         p.drawLine(d->rect.x() + d->rect.width() / 2 - 1, d->rect.y(),
342                    d->rect.x() + d->rect.width() / 2 - 1, d->rect.y() + d->rect.height());
343 
344         drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + 20), beforeLabel.toString());
345         drawText(&p, QPoint(d->rect.x() + d->rect.width() / 2 + 20, d->rect.y() + 20), afterLabel.toString());
346     }
347     else if ((d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesHorz) ||
348              (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesHorzCont))
349     {
350         if (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesHorz)
351         {
352             // Drawing original image.
353 
354             p.drawPixmap(d->rect, *d->previewPixmap);
355 
356             // Drawing target image under the original.
357 
358             d->iface->paint(d->pixmap, QRect(d->rect.x(), d->rect.y() + d->rect.height() / 2, d->rect.width(), d->rect.height() / 2), &p);
359         }
360         else
361         {
362             // Drawing target image.
363 
364             d->iface->paint(d->pixmap, d->rect, &p);
365 
366             // Drawing original image under the target.
367 
368             p.drawPixmap(d->rect.x(), d->rect.y(), *d->previewPixmap,
369                          0, 0, d->rect.width(), d->rect.height() / 2);
370         }
371 
372         p.fillRect(0, d->rect.bottom(), width(), height(), palette().color(QPalette::Window));
373 
374         p.setPen(QPen(Qt::white, 2, Qt::SolidLine));
375         p.drawLine(d->rect.x(),
376                    d->rect.y() + d->rect.height() / 2 - 1,
377                    d->rect.x() + d->rect.width(),
378                    d->rect.y() + d->rect.height() / 2 - 1);
379         p.setPen(QPen(Qt::red, 2, Qt::DotLine));
380         p.drawLine(d->rect.x(),
381                    d->rect.y() + d->rect.height() / 2 - 1,
382                    d->rect.x() + d->rect.width(),
383                    d->rect.y() + d->rect.height() / 2 - 1);
384 
385         drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + 20), beforeLabel.toString());
386         drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + d->rect.height() / 2 + 20), afterLabel.toString());
387     }
388 
389     if (d->spotVisible)
390     {
391         // Adapt spot from image coordinate to widget coordinate.
392 
393         int xspot = d->spot.x() + d->rect.x();
394         int yspot = d->spot.y() + d->rect.y();
395 
396         switch (d->guideMode)
397         {
398             case HVGuideMode:
399             {
400                 p.setPen(QPen(Qt::white, d->guideSize, Qt::SolidLine));
401                 p.drawLine(xspot, d->rect.top() + d->flicker, xspot, d->rect.bottom() - d->flicker);
402                 p.drawLine(d->rect.left() + d->flicker, yspot, d->rect.right() - d->flicker, yspot);
403                 p.setPen(QPen(d->guideColor, d->guideSize, Qt::DotLine));
404                 p.drawLine(xspot, d->rect.top() + d->flicker, xspot, d->rect.bottom() - d->flicker);
405                 p.drawLine(d->rect.left() + d->flicker, yspot, d->rect.right() - d->flicker, yspot);
406                 break;
407             }
408 
409             case PickColorMode:
410             {
411                 p.setPen(QPen(d->guideColor, 1, Qt::SolidLine));
412                 p.drawLine(xspot - 10, yspot - 10, xspot + 10, yspot + 10);
413                 p.drawLine(xspot + 10, yspot - 10, xspot - 10, yspot + 10);
414                 p.setPen(QPen(d->guideColor, 3, Qt::SolidLine));
415                 p.drawEllipse(xspot - 5, yspot - 5, 11, 11);
416 
417                 if (d->flicker % 2 != 0)
418                 {
419                     p.setPen(QPen(Qt::white, 1, Qt::SolidLine));
420                     p.drawEllipse(xspot - 5, yspot - 5, 11, 11);
421                 }
422 
423                 break;
424             }
425         }
426     }
427 
428     // draw additional points added by the image tool
429 
430     if (d->selectedPoints.count() > 0)
431     {
432         QPainter::RenderHints hints = p.renderHints();
433         QColor semiTransGuideColor  = QColor(d->guideColor.red(), d->guideColor.green(), d->guideColor.blue(), 75);
434 
435         QPoint point;
436         int x = 0;
437         int y = 0;
438 
439         for (int i = 0 ; i < d->selectedPoints.count() ; ++i)
440         {
441             point = d->selectedPoints.point(i);
442             point = translatePointPosition(point);
443             x     = point.x();
444             y     = point.y();
445 
446             p.save();
447             p.setRenderHint(QPainter::Antialiasing, true);
448             p.setPen(QPen(d->guideColor, 2, Qt::SolidLine));
449             p.setBrush(QBrush(semiTransGuideColor));
450             p.drawEllipse(point, 6, 6);
451 
452             p.restore();
453             p.setPen(QPen(d->guideColor, 1, Qt::SolidLine));
454             p.setBrush(Qt::NoBrush);
455             p.setRenderHint(QPainter::Antialiasing, false);
456             p.drawPoint(point);
457             p.drawText(QPoint(x + 10, y - 5), QString::number(i + 1));
458 
459             // draw a line between the points
460 
461             if (d->drawLineBetweenPoints &&
462                 ((i + 1) < d->selectedPoints.count()) && !d->selectedPoints.point(i + 1).isNull())
463             {
464                 p.save();
465                 p.setPen(QPen(d->guideColor, d->guideSize, Qt::SolidLine));
466                 QPoint point2 = d->selectedPoints.point(i + 1);
467                 point2        = translatePointPosition(point2);
468                 p.setRenderHint(QPainter::Antialiasing, true);
469                 p.drawLine(point, point2);
470                 p.restore();
471             }
472         }
473 
474         p.setRenderHints(hints);
475     }
476 
477     p.end();
478 }
479 
drawText(QPainter * const p,const QPoint & corner,const QString & text)480 void ImageGuideWidget::drawText(QPainter* const p, const QPoint& corner, const QString& text)
481 {
482     p->save();
483     QFontMetrics fontMt = p->fontMetrics();
484     QRect      fontRect = fontMt.boundingRect(text);
485     QRect textRect;
486     textRect.setTopLeft(corner);
487     textRect.setSize(QSize(fontRect.width() + 5, fontRect.height() + 2));
488 
489     // Draw background
490 
491     p->setPen(Qt::black);
492     QColor semiTransBg = palette().color(QPalette::Window);
493     semiTransBg.setAlpha(190);
494     p->setBrush(semiTransBg);
495 /*
496     p->translate(0.5, 0.5);
497 */
498     p->drawRoundedRect(textRect, 10.0, 10.0);
499 
500     // Draw shadow and text
501 
502     p->setPen(palette().color(QPalette::Window).darker(115));
503     p->drawText(textRect.translated(3, 1), text);
504     p->setPen(palette().color(QPalette::WindowText));
505     p->drawText(textRect.translated(2, 0), text);
506 
507     p->restore();
508 }
509 
paintEvent(QPaintEvent *)510 void ImageGuideWidget::paintEvent(QPaintEvent*)
511 {
512     QPainter p(this);
513     p.drawPixmap(0, 0, *d->pixmap);
514 
515     if (d->enableDrawMask && d->onMouseMovePreviewToggled == false)
516     {
517         p.setOpacity(0.7);
518         p.drawPixmap(d->rect.x(), d->rect.y(), *d->maskPixmap);
519 
520         if ((d->renderingPreviewMode == PreviewToolBar::PreviewOriginalImage) ||
521             ((d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver) && !d->onMouseMovePreviewToggled))
522         {
523             drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + 20), beforeLabel.toString());
524         }
525         else if ((d->renderingPreviewMode == PreviewToolBar::PreviewTargetImage) ||
526                  (d->renderingPreviewMode == PreviewToolBar::NoPreviewMode)      ||
527                  ((d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver) && d->onMouseMovePreviewToggled))
528         {
529             drawText(&p, QPoint(d->rect.x() + 20, d->rect.y() + 20), afterLabel.toString());
530         }
531     }
532 
533     p.end();
534 }
535 
timerEvent(QTimerEvent * e)536 void ImageGuideWidget::timerEvent(QTimerEvent* e)
537 {
538     if (e->timerId() == d->timerID)
539     {
540         if (d->flicker == 5)
541         {
542             d->flicker = 0;
543         }
544         else
545         {
546             d->flicker++;
547         }
548 
549         updatePreview();
550     }
551     else
552     {
553         QWidget::timerEvent(e);
554     }
555 }
556 
resizeEvent(QResizeEvent * e)557 void ImageGuideWidget::resizeEvent(QResizeEvent* e)
558 {
559     blockSignals(true);
560     delete d->pixmap;
561     delete d->previewPixmap;
562 
563     int w             = e->size().width();
564     int h             = e->size().height();
565     int old_w         = d->preview.width();
566     int old_h         = d->preview.height();
567     d->preview        = d->iface->setPreviewSize(QSize(w, h));
568     d->preview.setIccProfile(d->iface->original() ? d->iface->original()->getIccProfile() : IccProfile());
569 
570     d->pixmap         = new QPixmap(w, h);
571     d->previewPixmap  = new QPixmap(w, h);
572     d->rect           = QRect(w / 2 - d->preview.width() / 2, h / 2 - d->preview.height() / 2, d->preview.width(), d->preview.height());
573     *d->maskPixmap    = d->maskPixmap->scaled(d->preview.width(), d->preview.height(), Qt::IgnoreAspectRatio);
574     *d->previewPixmap = d->iface->convertToPixmap(d->preview);
575 
576     d->spot.setX((int)((float)d->spot.x() * ((float)d->preview.width()  / (float)old_w)));
577     d->spot.setY((int)((float)d->spot.y() * ((float)d->preview.height() / (float)old_h)));
578     updatePixmap();
579 
580     blockSignals(false);
581     emit signalResized();
582 }
583 
mousePressEvent(QMouseEvent * e)584 void ImageGuideWidget::mousePressEvent(QMouseEvent* e)
585 {
586     if (e->button() == Qt::LeftButton)
587     {
588         if      (!d->focus && d->rect.contains(e->x(), e->y()) && d->spotVisible)
589         {
590             d->focus = true;
591             d->spot.setX(e->x() - d->rect.x());
592             d->spot.setY(e->y() - d->rect.y());
593         }
594         else if (d->enableDrawMask)
595         {
596             d->lastPoint   = QPoint(e->x() - d->rect.x(), e->y() - d->rect.y());
597             d->drawingMask = true;
598         }
599 
600         updatePreview();
601     }
602 }
603 
mouseReleaseEvent(QMouseEvent * e)604 void ImageGuideWidget::mouseReleaseEvent(QMouseEvent* e)
605 {
606     if (d->rect.contains(e->x(), e->y()))
607     {
608         if (d->focus && d->spotVisible)
609         {
610             d->focus = false;
611             updatePreview();
612             d->spot.setX(e->x() - d->rect.x());
613             d->spot.setY(e->y() - d->rect.y());
614 
615             DColor color;
616 /*
617             QPoint point = getSpotPosition();
618 */
619             if      (d->renderingPreviewMode == PreviewToolBar::PreviewOriginalImage)
620             {
621                 color = getSpotColor(OriginalImage);
622                 emit spotPositionChangedFromOriginal(color, d->spot);
623             }
624             else if ((d->renderingPreviewMode == PreviewToolBar::PreviewTargetImage) ||
625                      (d->renderingPreviewMode == PreviewToolBar::NoPreviewMode))
626             {
627                 color = getSpotColor(TargetPreviewImage);
628                 emit spotPositionChangedFromTarget(color, d->spot);
629             }
630             else if (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesVert)
631             {
632                 if (d->spot.x() > (d->rect.width() / 2))
633                 {
634                     color = getSpotColor(TargetPreviewImage);
635                     emit spotPositionChangedFromTarget(color, QPoint(d->spot.x() - d->rect.width() / 2,
636                                                                      d->spot.y()));
637                 }
638                 else
639                 {
640                     color = getSpotColor(OriginalImage);
641                     emit spotPositionChangedFromOriginal(color, d->spot);
642                 }
643             }
644             else if (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesVertCont)
645             {
646                 if (d->spot.x() > (d->rect.width() / 2))
647                 {
648                     color = getSpotColor(TargetPreviewImage);
649                     emit spotPositionChangedFromTarget(color, d->spot);
650                 }
651                 else
652                 {
653                     color = getSpotColor(OriginalImage);
654                     emit spotPositionChangedFromOriginal(color, d->spot);
655                 }
656             }
657             else if (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesHorz)
658             {
659                 if (d->spot.y() > (d->rect.height() / 2))
660                 {
661                     color = getSpotColor(TargetPreviewImage);
662                     emit spotPositionChangedFromTarget(color, QPoint(d->spot.x(),
663                                                                      d->spot.y() - d->rect.height() / 2));
664                 }
665                 else
666                 {
667                     color = getSpotColor(OriginalImage);
668                     emit spotPositionChangedFromOriginal(color, d->spot);
669                 }
670             }
671             else if (d->renderingPreviewMode == PreviewToolBar::PreviewBothImagesHorzCont)
672             {
673                 if (d->spot.y() > (d->rect.height() / 2))
674                 {
675                     color = getSpotColor(TargetPreviewImage);
676                     emit spotPositionChangedFromTarget(color, d->spot);
677                 }
678                 else
679                 {
680                     color = getSpotColor(OriginalImage);
681                     emit spotPositionChangedFromOriginal(color, d->spot);
682                 }
683             }
684         }
685         else if (e->button() == Qt::LeftButton && d->drawingMask)
686         {
687             d->drawingMask = false;
688             updatePreview();
689         }
690     }
691 }
692 
mouseMoveEvent(QMouseEvent * e)693 void ImageGuideWidget::mouseMoveEvent(QMouseEvent* e)
694 {
695     if (d->rect.contains(e->x(), e->y()))
696     {
697         if      (d->focus && d->spotVisible)
698         {
699             setCursor(Qt::CrossCursor);
700             d->spot.setX(e->x() - d->rect.x());
701             d->spot.setY(e->y() - d->rect.y());
702         }
703         else if (d->enableDrawMask)
704         {
705             setCursor(d->maskCursor);
706 
707             if ((e->buttons() & Qt::LeftButton) && d->drawingMask)
708             {
709                 QPoint currentPos = QPoint(e->x() - d->rect.x(), e->y() - d->rect.y());
710                 drawLineTo(currentPos);
711                 updatePreview();
712             }
713         }
714     }
715     else
716     {
717         unsetCursor();
718     }
719 }
720 
enterEvent(QEvent *)721 void ImageGuideWidget::enterEvent(QEvent*)
722 {
723     if (!d->focus && (d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver))
724     {
725         d->onMouseMovePreviewToggled = false;
726         updatePixmap();
727         repaint();
728     }
729 }
730 
leaveEvent(QEvent *)731 void ImageGuideWidget::leaveEvent(QEvent*)
732 {
733     if (!d->focus && (d->renderingPreviewMode == PreviewToolBar::PreviewToggleOnMouseOver))
734     {
735         d->onMouseMovePreviewToggled = true;
736         updatePixmap();
737         update();
738     }
739 }
740 
setPoints(const QPolygon & p,bool drawLine)741 void ImageGuideWidget::setPoints(const QPolygon& p, bool drawLine)
742 {
743     d->selectedPoints        = p;
744     d->drawLineBetweenPoints = drawLine;
745     updatePreview();
746 }
747 
resetPoints()748 void ImageGuideWidget::resetPoints()
749 {
750     d->selectedPoints.clear();
751 }
752 
drawLineTo(const QPoint & endPoint)753 void ImageGuideWidget::drawLineTo(const QPoint& endPoint)
754 {
755     drawLineTo(d->penWidth, d->eraseMask, d->paintColor, d->lastPoint, endPoint);
756 }
757 
drawLineTo(int width,bool erase,const QColor & color,const QPoint & start,const QPoint & end)758 void ImageGuideWidget::drawLineTo(int width, bool erase, const QColor& color, const QPoint& start, const QPoint& end)
759 {
760 
761     QPainter painter(d->maskPixmap);
762 
763     if (erase)
764     {
765         // drawLine() seems to ignore composition modes, use a tmp pixmap and combine it later on
766 
767         QPixmap tmpMask(d->maskPixmap->width(), d->maskPixmap->height());
768         tmpMask.fill(Qt::transparent);
769         QPainter tmpPainter(&tmpMask);
770 
771         painter.setRenderHint(QPainter::Antialiasing, false);
772         painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
773 
774         QPen eraser;
775         eraser.setColor(Qt::yellow);
776         eraser.setStyle(Qt::SolidLine);
777         eraser.setWidth(width);
778         eraser.setCapStyle(Qt::RoundCap);
779         eraser.setJoinStyle(Qt::RoundJoin);
780 
781         tmpPainter.setPen(eraser);
782         tmpPainter.setBrush(QBrush());
783         tmpPainter.drawLine(start, end);
784         tmpPainter.end();
785 
786         painter.drawPixmap(0, 0, tmpMask);
787     }
788     else
789     {
790         painter.setPen(QPen(color, width, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
791         painter.drawLine(start, end);
792     }
793 
794     int rad      = (width / 2) + 2;
795 
796     update(QRect(start, end).normalized().adjusted(-rad, -rad, +rad, +rad));
797     d->lastPoint = end;
798 
799     painter.end();
800 }
801 
setPaintColor(const QColor & color)802 void ImageGuideWidget::setPaintColor(const QColor& color)
803 {
804     d->paintColor = color;
805 }
806 
setMaskEnabled(bool enabled)807 void ImageGuideWidget::setMaskEnabled(bool enabled)
808 {
809     d->enableDrawMask = enabled;
810     unsetCursor();
811     updatePreview();
812 }
813 
setEraseMode(bool erase)814 void ImageGuideWidget::setEraseMode(bool erase)
815 {
816     d->eraseMask = erase;
817 }
818 
getMask() const819 QImage ImageGuideWidget::getMask() const
820 {
821     QImage mask = d->maskPixmap->toImage();
822     return mask;
823 }
824 
translatePointPosition(const QPoint & point) const825 QPoint ImageGuideWidget::translatePointPosition(const QPoint& point) const
826 {
827     int x = d->rect.x() + 1 + (int)(point.x() * (float)d->preview.width()  /
828                                                 (float)d->iface->originalSize().width());
829     int y = d->rect.y() + 1 + (int)(point.y() * (float)d->preview.height() /
830                                                 (float)d->iface->originalSize().height());
831 
832     return (QPoint(x, y));
833 }
834 
setMaskPenSize(int size)835 void ImageGuideWidget::setMaskPenSize(int size)
836 {
837     d->penWidth = size;
838     updateMaskCursor();
839 }
840 
updateMaskCursor()841 void ImageGuideWidget::updateMaskCursor()
842 {
843     int size = d->penWidth;
844 
845     if (size > 64)
846     {
847         size = 64;
848     }
849 
850     if (size < 3)
851     {
852         size = 3;
853     }
854 
855     QPixmap pix(size, size);
856     pix.fill(Qt::transparent);
857 
858     QPainter p(&pix);
859     p.setRenderHint(QPainter::Antialiasing, true);
860 
861     p.drawEllipse(1, 1, size - 2, size - 2);
862 
863     d->maskCursor = QCursor(pix);
864 }
865 
setSpotPosition(const QPoint & point)866 void ImageGuideWidget::setSpotPosition(const QPoint& point)
867 {
868     d->spot.setX(point.x());
869     d->spot.setY(point.y());
870     updatePreview();
871 }
872 
updateSpotPosition(int x,int y)873 void ImageGuideWidget::updateSpotPosition(int x, int y)
874 {
875     QPoint origin = d->rect.topLeft();
876     x            -= origin.x();
877     y            -= origin.y();
878     d->spot.setX(x);
879     d->spot.setY(y);
880     updatePreview();
881 }
882 
translateItemPosition(const QPoint & point,bool src) const883 QPoint ImageGuideWidget::translateItemPosition(const QPoint& point, bool src) const
884 {
885     int x = (int)(point.x() * (float)d->preview.width()  /
886                               (float) d->iface->originalSize().width());
887     int y = (int)(point.y() * (float)d->preview.height() /
888                               (float) d->iface->originalSize().height());
889 
890     if (!src)
891     {
892         x  = (int)(point.x());
893         y  = (int)(point.y());
894         x -= (int)(d->rect.topLeft().x()) + 1;
895         y -= (int)(d->rect.topLeft().y()) + 1;
896     }
897 
898     return (QPoint(x, y));
899 }
900 
setMaskCursor()901 void ImageGuideWidget::setMaskCursor()
902 {
903     if (d->enableDrawMask)
904     {
905         updateMaskCursor();
906         setCursor(d->maskCursor);
907         updatePreview();
908     }
909 }
910 
911 } // namespace Digikam
912