1 // vim: set tabstop=4 shiftwidth=4 expandtab:
2 /*
3 Gwenview: an image viewer
4 Copyright 2011 Aurélien Gâteau <agateau@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
19
20 */
21 // Self
22 #include "rasterimageview.h"
23
24 // Local
25 #include "alphabackgrounditem.h"
26 #include "gwenview_lib_debug.h"
27 #include "rasterimageitem.h"
28 #include <lib/cms/cmsprofile.h>
29 #include <lib/documentview/abstractrasterimageviewtool.h>
30 #include <lib/gvdebug.h>
31 #include <lib/paintutils.h>
32
33 // KF
34
35 // Qt
36 #include <QApplication>
37 #include <QGraphicsSceneMouseEvent>
38 #include <QPainter>
39 #include <QPointer>
40 #include <QTimer>
41
42 #include <QColorSpace>
43 #include <QCryptographicHash>
44
45 namespace Gwenview
46 {
47 /*
48 * We need the tools to be painted on top of the image. However, since we are
49 * using RasterImageItem as a child of this item, the image gets painted on
50 * top of this item. To fix that, this custom item is stacked after the image
51 * item and will paint the tools, which will draw it on top of the image.
52 */
53 class ToolPainter : public QGraphicsItem
54 {
55 public:
ToolPainter(AbstractRasterImageViewTool * tool,QGraphicsItem * parent=nullptr)56 ToolPainter(AbstractRasterImageViewTool *tool, QGraphicsItem *parent = nullptr)
57 : QGraphicsItem(parent)
58 , mTool(tool)
59 {
60 }
61
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)62 void paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/) override
63 {
64 if (mTool) {
65 mTool->paint(painter);
66 }
67 }
68
boundingRect() const69 QRectF boundingRect() const override
70 {
71 return parentItem()->boundingRect();
72 }
73
74 QPointer<AbstractRasterImageViewTool> mTool;
75 };
76
77 struct RasterImageViewPrivate {
RasterImageViewPrivateGwenview::RasterImageViewPrivate78 RasterImageViewPrivate(RasterImageView *qq)
79 : q(qq)
80 {
81 }
82
83 RasterImageView *q;
84
85 RasterImageItem *mImageItem;
86 ToolPainter *mToolItem = nullptr;
87
88 QPointer<AbstractRasterImageViewTool> mTool;
89
startAnimationIfNecessaryGwenview::RasterImageViewPrivate90 void startAnimationIfNecessary()
91 {
92 if (q->document() && q->isVisible()) {
93 q->document()->startAnimation();
94 }
95 }
96
adjustItemPositionGwenview::RasterImageViewPrivate97 void adjustItemPosition()
98 {
99 mImageItem->setPos((q->imageOffset() - q->scrollPos()).toPoint());
100 q->update();
101 }
102 };
103
RasterImageView(QGraphicsItem * parent)104 RasterImageView::RasterImageView(QGraphicsItem *parent)
105 : AbstractImageView(parent)
106 , d(new RasterImageViewPrivate{this})
107 {
108 d->mImageItem = new RasterImageItem{this};
109
110 // Clip this item so we only render the visible part of the image when
111 // zoomed or when viewing a large image.
112 setFlag(QGraphicsItem::ItemClipsChildrenToShape);
113 }
114
~RasterImageView()115 RasterImageView::~RasterImageView()
116 {
117 if (d->mTool) {
118 d->mTool.data()->toolDeactivated();
119 }
120
121 delete d;
122 }
123
setRenderingIntent(const RenderingIntent::Enum & renderingIntent)124 void RasterImageView::setRenderingIntent(const RenderingIntent::Enum &renderingIntent)
125 {
126 d->mImageItem->setRenderingIntent(renderingIntent);
127 }
128
resetMonitorICC()129 void RasterImageView::resetMonitorICC()
130 {
131 update();
132 }
133
loadFromDocument()134 void RasterImageView::loadFromDocument()
135 {
136 Document::Ptr doc = document();
137 if (!doc) {
138 return;
139 }
140
141 connect(doc.data(), &Document::metaInfoLoaded, this, &RasterImageView::slotDocumentMetaInfoLoaded);
142 connect(doc.data(), &Document::isAnimatedUpdated, this, &RasterImageView::slotDocumentIsAnimatedUpdated);
143 connect(doc.data(), &Document::imageRectUpdated, this, [this]() {
144 d->mImageItem->updateCache();
145 });
146
147 const Document::LoadingState state = doc->loadingState();
148 if (state == Document::MetaInfoLoaded || state == Document::Loaded) {
149 slotDocumentMetaInfoLoaded();
150 }
151 }
152
slotDocumentMetaInfoLoaded()153 void RasterImageView::slotDocumentMetaInfoLoaded()
154 {
155 if (document()->size().isValid() && document()->image().format() != QImage::Format_Invalid) {
156 QMetaObject::invokeMethod(this, &RasterImageView::finishSetDocument, Qt::QueuedConnection);
157 } else {
158 // Could not retrieve image size from meta info, we need to load the
159 // full image now.
160 connect(document().data(), &Document::loaded, this, &RasterImageView::finishSetDocument);
161 document()->startLoadingFullImage();
162 }
163 }
164
finishSetDocument()165 void RasterImageView::finishSetDocument()
166 {
167 GV_RETURN_IF_FAIL(document()->size().isValid());
168
169 if (zoomToFit()) {
170 // Force the update otherwise if computeZoomToFit() returns 1, setZoom()
171 // will think zoom has not changed and won't update the image
172 setZoom(computeZoomToFit(), QPointF(-1, -1), ForceUpdate);
173 } else if (zoomToFill()) {
174 setZoom(computeZoomToFill(), QPointF(-1, -1), ForceUpdate);
175 } else {
176 onZoomChanged();
177 }
178
179 applyPendingScrollPos();
180 d->startAnimationIfNecessary();
181 update();
182
183 d->mImageItem->updateCache();
184
185 backgroundItem()->setVisible(true);
186
187 Q_EMIT completed();
188 }
189
slotDocumentIsAnimatedUpdated()190 void RasterImageView::slotDocumentIsAnimatedUpdated()
191 {
192 d->startAnimationIfNecessary();
193 }
194
onZoomChanged()195 void RasterImageView::onZoomChanged()
196 {
197 d->adjustItemPosition();
198 }
199
onImageOffsetChanged()200 void RasterImageView::onImageOffsetChanged()
201 {
202 d->adjustItemPosition();
203 }
204
onScrollPosChanged(const QPointF & oldPos)205 void RasterImageView::onScrollPosChanged(const QPointF &oldPos)
206 {
207 Q_UNUSED(oldPos);
208 d->adjustItemPosition();
209 }
210
setCurrentTool(AbstractRasterImageViewTool * tool)211 void RasterImageView::setCurrentTool(AbstractRasterImageViewTool *tool)
212 {
213 if (d->mTool) {
214 d->mTool.data()->toolDeactivated();
215 d->mTool.data()->deleteLater();
216 delete d->mToolItem;
217 }
218
219 // Go back to default cursor when tool is deactivated. We need to call this here and
220 // not further below in case toolActivated wants to set its own new cursor afterwards.
221 updateCursor();
222
223 d->mTool = tool;
224 if (d->mTool) {
225 d->mTool.data()->toolActivated();
226 d->mToolItem = new ToolPainter{d->mTool, this};
227 }
228 Q_EMIT currentToolChanged(tool);
229 update();
230 }
231
currentTool() const232 AbstractRasterImageViewTool *RasterImageView::currentTool() const
233 {
234 return d->mTool.data();
235 }
236
mousePressEvent(QGraphicsSceneMouseEvent * event)237 void RasterImageView::mousePressEvent(QGraphicsSceneMouseEvent *event)
238 {
239 if (d->mTool) {
240 d->mTool.data()->mousePressEvent(event);
241 if (event->isAccepted()) {
242 return;
243 }
244 }
245 AbstractImageView::mousePressEvent(event);
246 }
247
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)248 void RasterImageView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
249 {
250 if (d->mTool) {
251 d->mTool.data()->mouseDoubleClickEvent(event);
252 if (event->isAccepted()) {
253 return;
254 }
255 }
256 AbstractImageView::mouseDoubleClickEvent(event);
257 }
258
mouseMoveEvent(QGraphicsSceneMouseEvent * event)259 void RasterImageView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
260 {
261 if (d->mTool) {
262 d->mTool.data()->mouseMoveEvent(event);
263 if (event->isAccepted()) {
264 return;
265 }
266 }
267 AbstractImageView::mouseMoveEvent(event);
268 }
269
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)270 void RasterImageView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
271 {
272 if (d->mTool) {
273 d->mTool.data()->mouseReleaseEvent(event);
274 if (event->isAccepted()) {
275 return;
276 }
277 }
278 AbstractImageView::mouseReleaseEvent(event);
279 }
280
wheelEvent(QGraphicsSceneWheelEvent * event)281 void RasterImageView::wheelEvent(QGraphicsSceneWheelEvent *event)
282 {
283 if (d->mTool) {
284 d->mTool.data()->wheelEvent(event);
285 if (event->isAccepted()) {
286 return;
287 }
288 }
289 AbstractImageView::wheelEvent(event);
290 }
291
keyPressEvent(QKeyEvent * event)292 void RasterImageView::keyPressEvent(QKeyEvent *event)
293 {
294 if (d->mTool) {
295 d->mTool.data()->keyPressEvent(event);
296 if (event->isAccepted()) {
297 return;
298 }
299 }
300 AbstractImageView::keyPressEvent(event);
301 }
302
keyReleaseEvent(QKeyEvent * event)303 void RasterImageView::keyReleaseEvent(QKeyEvent *event)
304 {
305 if (d->mTool) {
306 d->mTool.data()->keyReleaseEvent(event);
307 if (event->isAccepted()) {
308 return;
309 }
310 }
311 AbstractImageView::keyReleaseEvent(event);
312 }
313
hoverMoveEvent(QGraphicsSceneHoverEvent * event)314 void RasterImageView::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
315 {
316 if (d->mTool) {
317 d->mTool.data()->hoverMoveEvent(event);
318 if (event->isAccepted()) {
319 return;
320 }
321 }
322 AbstractImageView::hoverMoveEvent(event);
323 }
324
325 } // namespace
326