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