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 "KoShapeManager.h"
10
11#include "kis_algebra_2d.h"
12#include "KisDocument.h"
14#include "kis_layer_utils.h"
15
17 struct Buffer
18 {
20 QPointF position;
21 QImage image;
22
23 QRectF bounds() const
24 {
25 return QRectF(position, image.size() / image.devicePixelRatio());
26 }
27 };
28
30
35
37 : q(q)
38 {}
39
40 void updateBufferByImageCoordinates(const QRectF &dirtyImageRect)
41 {
42 QRectF dirtyWidgetRect = q->view()->viewConverter()->imageToWidget(dirtyImageRect);
43 updateBuffer(dirtyWidgetRect, dirtyImageRect);
44 }
45
46 void updateBufferByWidgetCoordinates(const QRectF &dirtyWidgetRect)
47 {
48 QRectF dirtyImageRect = q->view()->viewConverter()->widgetToImage(dirtyWidgetRect);
49 updateBuffer(dirtyWidgetRect, dirtyImageRect);
50 }
51
52private:
53 void updateBuffer(QRectF widgetRect, QRectF imageRect)
54 {
55 KisCoordinatesConverter *viewConverter = q->view()->viewConverter();
56 QTransform transform = viewConverter->imageToWidgetTransform();
57
58 qreal devicePixelRatioF = q->view()->devicePixelRatioF();
59 if (buffer.image.isNull() || !buffer.bounds().contains(widgetRect)) {
60 const QRectF boundingImageRect = layer->boundingImageRect();
61 const QRectF boundingWidgetRect = q->view()->viewConverter()->imageToWidget(boundingImageRect);
62 widgetRect = boundingWidgetRect.intersected(q->view()->rect());
63
64 if (widgetRect.isNull()) return;
65
66 buffer.position = widgetRect.topLeft();
67 // to ensure that buffer is big enough for all the pixels on high dpi displays
68 // BUG 411118
69 buffer.image = QImage((widgetRect.size()*devicePixelRatioF).toSize(), QImage::Format_ARGB32);
70 buffer.image.setDevicePixelRatio(devicePixelRatioF);
71
72 imageRect = q->view()->viewConverter()->widgetToImage(widgetRect);
73
74 }
75
76 QPainter gc(&buffer.image);
77
78 gc.translate(-buffer.position);
79 gc.setTransform(transform, true);
80
81 gc.save();
82 gc.setCompositionMode(QPainter::CompositionMode_Source);
83 gc.fillRect(imageRect, Qt::transparent);
84 gc.restore();
85
86 // to ensure that clipping rect is also big enough for all the pixels
87 // BUG 411118
88 gc.setClipRect(QRectF(imageRect.topLeft(), imageRect.size()*devicePixelRatioF));
90 }
91};
92
94 : KisCanvasDecoration("referenceImagesDecoration", parent)
95 , d(new Private(this))
96{
97 connect(document->image().data(), SIGNAL(sigNodeAddedAsync(KisNodeSP, KisNodeAdditionFlags)), this, SLOT(slotNodeAdded(KisNodeSP, KisNodeAdditionFlags)));
98 connect(document->image().data(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), this, SLOT(slotNodeRemoved(KisNodeSP)));
99 connect(document->image().data(), SIGNAL(sigLayersChangedAsync()), this, SLOT(slotLayersChanged()));
101
102 auto referenceImageLayer = document->referenceImagesLayer();
103 if (referenceImageLayer) {
104 setReferenceImageLayer(referenceImageLayer, /* updateCanvas = */ viewReady);
105 }
106}
107
110
112{
113 KUndo2Command *cmd = KisReferenceImagesLayer::addReferenceImages(view()->document(), {referenceImage});
114 view()->canvasBase()->addCommand(cmd);
115}
116
118{
119 return view()->document()->referenceImagesLayer() != nullptr;
120}
121
122void KisReferenceImagesDecoration::drawDecoration(QPainter &gc, const QRectF &/*updateRect*/, const KisCoordinatesConverter *converter, KisCanvas2 */*canvas*/)
123{
124 // TODO: can we use partial updates here?
125
126 KisSharedPtr<KisReferenceImagesLayer> layer = d->layer.toStrongRef();
127
128 if (!layer.isNull()) {
129 QSizeF viewSize = view()->size();
130
131 QTransform transform = converter->imageToWidgetTransform();
132 if (d->previousViewSize != viewSize || !KisAlgebra2D::fuzzyMatrixCompare(transform, d->previousTransform, 1e-4)) {
133 d->previousViewSize = viewSize;
134 d->previousTransform = transform;
135 d->buffer.image = QImage();
136 d->updateBufferByWidgetCoordinates(QRectF(QPointF(0,0), viewSize));
137 }
138
139 if (!d->buffer.image.isNull()) {
140 gc.drawImage(d->buffer.position, d->buffer.image);
141 }
142 }
143}
144
149
150void KisReferenceImagesDecoration::slotNodeAdded(KisNodeSP node, KisNodeAdditionFlags flags)
151{
152 Q_UNUSED(flags)
153
154 KisReferenceImagesLayer *referenceImagesLayer =
155 dynamic_cast<KisReferenceImagesLayer*>(node.data());
156
157 if (referenceImagesLayer) {
158 setReferenceImageLayer(referenceImagesLayer, /* updateCanvas = */ true);
159 }
160}
161
163{
164 KisReferenceImagesLayer *referenceImagesLayer =
165 dynamic_cast<KisReferenceImagesLayer*>(node.data());
166
167 if (referenceImagesLayer && referenceImagesLayer == d->layer) {
168 setReferenceImageLayer(0, true);
169 }
170}
171
173{
174 KisImageSP image = view()->image();
175
176 KisReferenceImagesLayer *referenceImagesLayer =
177 KisLayerUtils::findNodeByType<KisReferenceImagesLayer>(image->root());
178
179 setReferenceImageLayer(referenceImagesLayer, true);
180}
181
183{
184 d->updateBufferByImageCoordinates(dirtyRect);
185
186 QRectF documentRect = view()->viewConverter()->imageToDocument(dirtyRect);
187 view()->canvasBase()->updateCanvasDecorations(documentRect);
188}
189
191{
192 if (d->layer != layer.data()) {
193 KisSharedPtr<KisReferenceImagesLayer> oldLayer = d->layer.toStrongRef();
194 if (oldLayer) {
195 oldLayer->disconnect(this);
196 }
197
198 d->layer = layer;
199
200 if (layer) {
201 connect(layer.data(), SIGNAL(sigUpdateCanvas(QRectF)),
202 this, SLOT(slotReferenceImagesChanged(QRectF)));
203
204 const QRectF dirtyRect = layer->boundingImageRect();
205
206 // If the view is not ready yet (because this is being constructed
207 // from view.d's ctor and thus view.d is not available now),
208 // do not update canvas because it will lead to a crash.
209 if (updateCanvas && !dirtyRect.isEmpty()) { // in case the reference layer is just being loaded from the .kra file
211 }
212 }
213 }
214}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
QPointer< KisView > view() const
void sigReferenceImagesLayerChanged(KisSharedPtr< KisReferenceImagesLayer > layer)
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
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)