Krita Source Code Documentation
Loading...
Searching...
No Matches
KisReferenceImagesDecoration.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
9#include "KisDisplayConfig.h"
10#include "KoShapeManager.h"
11
12#include "kis_algebra_2d.h"
13#include "KisDocument.h"
15#include "kis_layer_utils.h"
17#include <qcolorspace.h>
18
20 struct Buffer
21 {
23 QPointF position;
24 QImage image;
25
26 QRectF bounds() const
27 {
28 return QRectF(position, image.size() / image.devicePixelRatio());
29 }
30 };
31
33
38
40 : q(q)
41 {}
42
43 void updateBufferByImageCoordinates(const QRectF &dirtyImageRect)
44 {
45 QRectF dirtyWidgetRect = q->view()->viewConverter()->imageToWidget(dirtyImageRect);
46 updateBuffer(dirtyWidgetRect, dirtyImageRect);
47 }
48
49 void updateBufferByWidgetCoordinates(const QRectF &dirtyWidgetRect)
50 {
51 QRectF dirtyImageRect = q->view()->viewConverter()->widgetToImage(dirtyWidgetRect);
52 updateBuffer(dirtyWidgetRect, dirtyImageRect);
53 }
54
55private:
56 void updateBuffer(QRectF widgetRect, QRectF imageRect)
57 {
58 KisCoordinatesConverter *viewConverter = q->view()->viewConverter();
59 QTransform transform = viewConverter->imageToWidgetTransform();
60
61 qreal devicePixelRatioF = q->view()->devicePixelRatioF();
62 if (buffer.image.isNull() || !buffer.bounds().contains(widgetRect)) {
63 const QRectF boundingImageRect = layer->boundingImageRect();
64 const QRectF boundingWidgetRect = q->view()->viewConverter()->imageToWidget(boundingImageRect);
65 widgetRect = boundingWidgetRect.intersected(q->view()->rect());
66
67 if (widgetRect.isNull()) return;
68
69 buffer.position = widgetRect.topLeft();
70 // to ensure that buffer is big enough for all the pixels on high dpi displays
71 // BUG 411118
72 QColorSpace space = buffer.image.colorSpace();
73 buffer.image = QImage((widgetRect.size()*devicePixelRatioF).toSize(), QImage::Format_ARGB32);
74 buffer.image.setColorSpace(space);
75 buffer.image.setDevicePixelRatio(devicePixelRatioF);
76
77 imageRect = q->view()->viewConverter()->widgetToImage(widgetRect);
78
79 }
80
81 QPainter gc(&buffer.image);
82
83 gc.translate(-buffer.position);
84 gc.setTransform(transform, true);
85
86 gc.save();
87 gc.setCompositionMode(QPainter::CompositionMode_Source);
88 gc.fillRect(imageRect, Qt::transparent);
89 gc.restore();
90
91 // to ensure that clipping rect is also big enough for all the pixels
92 // BUG 411118
93 gc.setClipRect(QRectF(imageRect.topLeft(), imageRect.size()*devicePixelRatioF));
95 }
96};
97
99 : KisCanvasDecoration("referenceImagesDecoration", parent)
100 , d(new Private(this))
101{
102 connect(document->image().data(), SIGNAL(sigNodeAddedAsync(KisNodeSP, KisNodeAdditionFlags)), this, SLOT(slotNodeAdded(KisNodeSP, KisNodeAdditionFlags)));
103 connect(document->image().data(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), this, SLOT(slotNodeRemoved(KisNodeSP)));
104 connect(document->image().data(), SIGNAL(sigLayersChangedAsync()), this, SLOT(slotLayersChanged()));
106
107 auto referenceImageLayer = document->referenceImagesLayer();
108 if (referenceImageLayer) {
109 setReferenceImageLayer(referenceImageLayer, /* updateCanvas = */ viewReady);
110 }
111}
112
115
117{
118 KUndo2Command *cmd = KisReferenceImagesLayer::addReferenceImages(view()->document(), {referenceImage});
119 view()->canvasBase()->addCommand(cmd);
120}
121
123{
124 return view()->document()->referenceImagesLayer() != nullptr;
125}
126
127void KisReferenceImagesDecoration::drawDecoration(QPainter &gc, const QRectF &/*updateRect*/, const KisCoordinatesConverter *converter, KisCanvas2 *canvas)
128{
129 // TODO: can we use partial updates here?
130
131 KisSharedPtr<KisReferenceImagesLayer> layer = d->layer.toStrongRef();
133
134 if (!layer.isNull()) {
135 QSizeF viewSize = view()->size();
136
137 QTransform transform = converter->imageToWidgetTransform();
138 if (d->previousViewSize != viewSize || !KisAlgebra2D::fuzzyMatrixCompare(transform, d->previousTransform, 1e-4)
139 || d->buffer.image.colorSpace() != canvasSpace) {
140 d->previousViewSize = viewSize;
141 d->previousTransform = transform;
142
143 d->buffer.image = QImage(QSize(1, 1), QImage::Format_ARGB32);
144 d->buffer.image.setColorSpace(canvasSpace);
145
146 d->updateBufferByWidgetCoordinates(QRectF(QPointF(0,0), viewSize));
147 }
148
149 if (!d->buffer.image.isNull()) {
150#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
151 gc.drawImage(d->buffer.position, d->buffer.image);
152#else
153 gc.drawImage(d->buffer.position, canvas->displayRendererInterface()->convertImageToDisplayColorSpace(d->buffer.image));
154#endif
155 }
156 }
157}
158
163
164void KisReferenceImagesDecoration::slotNodeAdded(KisNodeSP node, KisNodeAdditionFlags flags)
165{
166 Q_UNUSED(flags)
167
168 KisReferenceImagesLayer *referenceImagesLayer =
169 dynamic_cast<KisReferenceImagesLayer*>(node.data());
170
171 if (referenceImagesLayer) {
172 setReferenceImageLayer(referenceImagesLayer, /* updateCanvas = */ true);
173 }
174}
175
177{
178 KisReferenceImagesLayer *referenceImagesLayer =
179 dynamic_cast<KisReferenceImagesLayer*>(node.data());
180
181 if (referenceImagesLayer && referenceImagesLayer == d->layer) {
182 setReferenceImageLayer(0, true);
183 }
184}
185
187{
188 KisImageSP image = view()->image();
189
190 KisReferenceImagesLayer *referenceImagesLayer =
191 KisLayerUtils::findNodeByType<KisReferenceImagesLayer>(image->root());
192
193 setReferenceImageLayer(referenceImagesLayer, true);
194}
195
197{
198 d->updateBufferByImageCoordinates(dirtyRect);
199
200 QRectF documentRect = view()->viewConverter()->imageToDocument(dirtyRect);
201 view()->canvasBase()->updateCanvasDecorations(documentRect);
202}
203
205{
206 if (d->layer != layer.data()) {
207 KisSharedPtr<KisReferenceImagesLayer> oldLayer = d->layer.toStrongRef();
208 if (oldLayer) {
209 oldLayer->disconnect(this);
210 }
211
212 d->layer = layer;
213
214 if (layer) {
215 connect(layer.data(), SIGNAL(sigUpdateCanvas(QRectF)),
216 this, SLOT(slotReferenceImagesChanged(QRectF)));
217
218 const QRectF dirtyRect = layer->boundingImageRect();
219
220 // If the view is not ready yet (because this is being constructed
221 // from view.d's ctor and thus view.d is not available now),
222 // do not update canvas because it will lead to a crash.
223 if (updateCanvas && !dirtyRect.isEmpty()) { // in case the reference layer is just being loaded from the .kra file
225 }
226 }
227 }
228}
KoColorDisplayRendererInterface * displayRendererInterface() const override
displayRendererInterface The display renderer interface has a number of color conversion functions wh...
KisDisplayColorConverter displayColorConverter
QPointer< KisView > view() const
KisMultiSurfaceDisplayConfig multiSurfaceDisplayConfig() const
void sigReferenceImagesLayerChanged(KisSharedPtr< KisReferenceImagesLayer > layer)
const KoColorProfile * canvasProfile
The KisReferenceImage class represents a single reference image.
The KisReferenceImagesDecoration class draws the reference images on the canvas. The document stores ...
void setReferenceImageLayer(KisSharedPtr< KisReferenceImagesLayer > layer, bool updateCanvas)
KisReferenceImagesDecoration(QPointer< KisView > parent, KisDocument *document, bool viewReady=true)
void addReferenceImage(KisReferenceImage *referenceImage)
const QScopedPointer< Private > d
void slotReferenceImagesChanged(const QRectF &dirtyRect)
void drawDecoration(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, KisCanvas2 *canvas) override
void paintReferences(QPainter &painter)
static KUndo2Command * addReferenceImages(KisDocument *document, QList< KoShape * > referenceImages)
bool isNull() const
virtual QImage convertImageToDisplayColorSpace(const QImage source) const =0
convertImageToDisplayColorSpace
bool fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta)
QPointF position
Top left corner of the buffer relative to the viewport.
void updateBufferByWidgetCoordinates(const QRectF &dirtyWidgetRect)
KisWeakSharedPtr< KisReferenceImagesLayer > layer
void updateBufferByImageCoordinates(const QRectF &dirtyImageRect)
void updateBuffer(QRectF widgetRect, QRectF imageRect)
static KoColorSpaceRegistry * instance()
QColorSpace QColorSpaceForProfile(const KoColorProfile *profile) const
QColorSpaceForProfile Generate a QColorSpace for the given KoColorProfile. Will return sRGB when the ...