1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2014-09-18
7  * Description : slideshow image widget
8  *
9  * Copyright (C) 2014-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2019-2020 by Minh Nghia Duong <minhnghiaduong997 at gmail dot com>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option)
16  * any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * ============================================================ */
24 
25 #include "slideimage.h"
26 
27 // Qt includes
28 
29 #include <QApplication>
30 #include <QPainter>
31 #include <QScreen>
32 #include <QWindow>
33 
34 // Local includes
35 
36 #include "digikam_debug.h"
37 #include "dimg.h"
38 #include "previewloadthread.h"
39 
40 using namespace Digikam;
41 
42 namespace DigikamGenericSlideShowPlugin
43 {
44 
45 class Q_DECL_HIDDEN SlideImage::Private
46 {
47 
48 public:
49 
Private()50     explicit Private()
51       : previewThread       (nullptr),
52         previewPreloadThread(nullptr)
53     {
54     }
55 
56     PreviewSettings     previewSettings;
57 
58     QPixmap             pixmap;
59 
60     QUrl                currentImage;
61 
62     DImg                preview;
63     PreviewLoadThread*  previewThread;
64     PreviewLoadThread*  previewPreloadThread;
65 };
66 
SlideImage(QWidget * const parent)67 SlideImage::SlideImage(QWidget* const parent)
68     : QWidget(parent),
69       d      (new Private)
70 {
71     setAttribute(Qt::WA_DeleteOnClose);
72     setAttribute(Qt::WA_OpaquePaintEvent);
73     setWindowFlags(Qt::FramelessWindowHint);
74     setMouseTracking(true);
75 
76     d->previewThread        = new PreviewLoadThread();
77     d->previewPreloadThread = new PreviewLoadThread();
78 
79     connect(d->previewThread, SIGNAL(signalImageLoaded(LoadingDescription,DImg)),
80             this, SLOT(slotGotImagePreview(LoadingDescription,DImg)));
81 }
82 
~SlideImage()83 SlideImage::~SlideImage()
84 {
85     delete d->previewThread;
86     delete d->previewPreloadThread;
87     delete d;
88 }
89 
setPreviewSettings(const PreviewSettings & settings)90 void SlideImage::setPreviewSettings(const PreviewSettings& settings)
91 {
92     d->previewSettings = settings;
93 }
94 
setLoadUrl(const QUrl & url)95 void SlideImage::setLoadUrl(const QUrl& url)
96 {
97     d->currentImage = url;
98 
99     // calculate preview size which is used for fast previews
100 
101     QScreen* screen = qApp->primaryScreen();
102 
103     if (QWidget* const widget = nativeParentWidget())
104     {
105         if (QWindow* const window = widget->windowHandle())
106         {
107             screen = window->screen();
108         }
109     }
110 
111     QSize desktopSize = screen->geometry().size();
112     int deskSize      = qMax(640, qMax(desktopSize.height(), desktopSize.width()));
113     d->previewThread->load(url.toLocalFile(), d->previewSettings, deskSize);
114 }
115 
setPreloadUrl(const QUrl & url)116 void SlideImage::setPreloadUrl(const QUrl& url)
117 {
118     // calculate preview size which is used for fast previews
119 
120     QScreen* screen = qApp->primaryScreen();
121 
122     if (QWidget* const widget = nativeParentWidget())
123     {
124         if (QWindow* const window = widget->windowHandle())
125         {
126             screen = window->screen();
127         }
128     }
129 
130     QSize desktopSize = screen->geometry().size();
131     int deskSize      = qMax(640, qMax(desktopSize.height(), desktopSize.width()));
132     d->previewPreloadThread->load(url.toLocalFile(), d->previewSettings, deskSize);
133 }
134 
paintEvent(QPaintEvent *)135 void SlideImage::paintEvent(QPaintEvent*)
136 {
137     QPainter p(this);
138     p.drawPixmap(0, 0, width(), height(), d->pixmap,
139                  0, 0, d->pixmap.width(), d->pixmap.height());
140     p.end();
141 }
142 
slotGotImagePreview(const LoadingDescription & desc,const DImg & preview)143 void SlideImage::slotGotImagePreview(const LoadingDescription& desc, const DImg& preview)
144 {
145     if ((desc.filePath != d->currentImage.toLocalFile()) || desc.isThumbnail())
146     {
147         return;
148     }
149 
150     d->preview.reset();
151 
152     if (!DImg::isAnimatedImage(desc.filePath))      // Special case for animated images as GIF or NMG
153     {
154         d->preview = preview;
155     }
156 
157     if (!d->preview.isNull())
158     {
159         updatePixmap();
160         update();
161 
162         emit signalImageLoaded(true);
163 
164         return;
165     }
166 
167     emit signalImageLoaded(false);
168 }
169 
updatePixmap()170 void SlideImage::updatePixmap()
171 {
172     /**
173      * For high resolution ("retina") displays, Mac OS X / Qt
174      * report only half of the physical resolution in terms of
175      * pixels, i.e. every logical pixels corresponds to 2x2
176      * physical pixels. However, UI elements and fonts are
177      * nevertheless rendered at full resolution, and pixmaps
178      * as well, provided their resolution is high enough (that
179      * is, higher than the reported, logical resolution).
180      *
181      * To work around this, we render the photos not a logical
182      * resolution, but with the photo's full resolution, but
183      * at the screen's aspect ratio. When we later draw this
184      * high resolution bitmap, it is up to Qt to scale the
185      * photo to the true physical resolution.  The ratio
186      * computed below is the ratio between the photo and
187      * screen resolutions, or equivalently the factor by which
188      * we need to increase the pixel size of the rendered
189      * pixmap.
190      */
191 
192     double ratio   = qApp->devicePixelRatio();
193 
194     QSize fullSize = QSizeF(ratio*width(), ratio*height()).toSize();
195     d->pixmap      = QPixmap(fullSize);
196     d->pixmap.fill(Qt::black);
197     QPainter p(&(d->pixmap));
198 
199     QPixmap pix(d->preview.smoothScale(d->pixmap.width(), d->pixmap.height(), Qt::KeepAspectRatio).convertToPixmap());
200     p.drawPixmap((d->pixmap.width()  - pix.width())  / 2,
201                  (d->pixmap.height() - pix.height()) / 2, pix,
202                  0, 0, pix.width(), pix.height());
203 }
204 
205 } // namespace DigikamGenericSlideShowPlugin
206