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