File indexing completed on 2024-05-12 16:01:54
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Boudewijn Rempt <boud@valdyas.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "KisReferenceImagesDecoration.h" 0008 0009 #include "KoShapeManager.h" 0010 0011 #include "kis_algebra_2d.h" 0012 #include "KisDocument.h" 0013 #include "KisReferenceImagesLayer.h" 0014 #include "kis_layer_utils.h" 0015 0016 struct KisReferenceImagesDecoration::Private { 0017 struct Buffer 0018 { 0019 /// Top left corner of the buffer relative to the viewport 0020 QPointF position; 0021 QImage image; 0022 0023 QRectF bounds() const 0024 { 0025 return QRectF(position, image.size() / image.devicePixelRatio()); 0026 } 0027 }; 0028 0029 KisReferenceImagesDecoration *q; 0030 0031 KisWeakSharedPtr<KisReferenceImagesLayer> layer; 0032 Buffer buffer; 0033 QTransform previousTransform; 0034 QSizeF previousViewSize; 0035 0036 explicit Private(KisReferenceImagesDecoration *q) 0037 : q(q) 0038 {} 0039 0040 void updateBufferByImageCoordinates(const QRectF &dirtyImageRect) 0041 { 0042 QRectF dirtyWidgetRect = q->view()->viewConverter()->imageToWidget(dirtyImageRect); 0043 updateBuffer(dirtyWidgetRect, dirtyImageRect); 0044 } 0045 0046 void updateBufferByWidgetCoordinates(const QRectF &dirtyWidgetRect) 0047 { 0048 QRectF dirtyImageRect = q->view()->viewConverter()->widgetToImage(dirtyWidgetRect); 0049 updateBuffer(dirtyWidgetRect, dirtyImageRect); 0050 } 0051 0052 private: 0053 void updateBuffer(QRectF widgetRect, QRectF imageRect) 0054 { 0055 KisCoordinatesConverter *viewConverter = q->view()->viewConverter(); 0056 QTransform transform = viewConverter->imageToWidgetTransform(); 0057 0058 qreal devicePixelRatioF = q->view()->devicePixelRatioF(); 0059 if (buffer.image.isNull() || !buffer.bounds().contains(widgetRect)) { 0060 const QRectF boundingImageRect = layer->boundingImageRect(); 0061 const QRectF boundingWidgetRect = q->view()->viewConverter()->imageToWidget(boundingImageRect); 0062 widgetRect = boundingWidgetRect.intersected(q->view()->rect()); 0063 0064 if (widgetRect.isNull()) return; 0065 0066 buffer.position = widgetRect.topLeft(); 0067 // to ensure that buffer is big enough for all the pixels on high dpi displays 0068 // BUG 411118 0069 buffer.image = QImage((widgetRect.size()*devicePixelRatioF).toSize(), QImage::Format_ARGB32); 0070 buffer.image.setDevicePixelRatio(devicePixelRatioF); 0071 0072 imageRect = q->view()->viewConverter()->widgetToImage(widgetRect); 0073 0074 } 0075 0076 QPainter gc(&buffer.image); 0077 0078 gc.translate(-buffer.position); 0079 gc.setTransform(transform, true); 0080 0081 gc.save(); 0082 gc.setCompositionMode(QPainter::CompositionMode_Source); 0083 gc.fillRect(imageRect, Qt::transparent); 0084 gc.restore(); 0085 0086 // to ensure that clipping rect is also big enough for all the pixels 0087 // BUG 411118 0088 gc.setClipRect(QRectF(imageRect.topLeft(), imageRect.size()*devicePixelRatioF)); 0089 layer->paintReferences(gc); 0090 } 0091 }; 0092 0093 KisReferenceImagesDecoration::KisReferenceImagesDecoration(QPointer<KisView> parent, KisDocument *document, bool viewReady) 0094 : KisCanvasDecoration("referenceImagesDecoration", parent) 0095 , d(new Private(this)) 0096 { 0097 connect(document->image().data(), SIGNAL(sigNodeAddedAsync(KisNodeSP)), this, SLOT(slotNodeAdded(KisNodeSP))); 0098 connect(document->image().data(), SIGNAL(sigRemoveNodeAsync(KisNodeSP)), this, SLOT(slotNodeRemoved(KisNodeSP))); 0099 connect(document->image().data(), SIGNAL(sigLayersChangedAsync()), this, SLOT(slotLayersChanged())); 0100 connect(document, &KisDocument::sigReferenceImagesLayerChanged, this, &KisReferenceImagesDecoration::slotNodeAdded); 0101 0102 auto referenceImageLayer = document->referenceImagesLayer(); 0103 if (referenceImageLayer) { 0104 setReferenceImageLayer(referenceImageLayer, /* updateCanvas = */ viewReady); 0105 } 0106 } 0107 0108 KisReferenceImagesDecoration::~KisReferenceImagesDecoration() 0109 {} 0110 0111 void KisReferenceImagesDecoration::addReferenceImage(KisReferenceImage *referenceImage) 0112 { 0113 KUndo2Command *cmd = KisReferenceImagesLayer::addReferenceImages(view()->document(), {referenceImage}); 0114 view()->canvasBase()->addCommand(cmd); 0115 } 0116 0117 bool KisReferenceImagesDecoration::documentHasReferenceImages() const 0118 { 0119 return view()->document()->referenceImagesLayer() != nullptr; 0120 } 0121 0122 void KisReferenceImagesDecoration::drawDecoration(QPainter &gc, const QRectF &/*updateRect*/, const KisCoordinatesConverter *converter, KisCanvas2 */*canvas*/) 0123 { 0124 // TODO: can we use partial updates here? 0125 0126 KisSharedPtr<KisReferenceImagesLayer> layer = d->layer.toStrongRef(); 0127 0128 if (!layer.isNull()) { 0129 QSizeF viewSize = view()->size(); 0130 0131 QTransform transform = converter->imageToWidgetTransform(); 0132 if (d->previousViewSize != viewSize || !KisAlgebra2D::fuzzyMatrixCompare(transform, d->previousTransform, 1e-4)) { 0133 d->previousViewSize = viewSize; 0134 d->previousTransform = transform; 0135 d->buffer.image = QImage(); 0136 d->updateBufferByWidgetCoordinates(QRectF(QPointF(0,0), viewSize)); 0137 } 0138 0139 if (!d->buffer.image.isNull()) { 0140 gc.drawImage(d->buffer.position, d->buffer.image); 0141 } 0142 } 0143 } 0144 0145 void KisReferenceImagesDecoration::slotNodeAdded(KisNodeSP node) 0146 { 0147 KisReferenceImagesLayer *referenceImagesLayer = 0148 dynamic_cast<KisReferenceImagesLayer*>(node.data()); 0149 0150 if (referenceImagesLayer) { 0151 setReferenceImageLayer(referenceImagesLayer, /* updateCanvas = */ true); 0152 } 0153 } 0154 0155 void KisReferenceImagesDecoration::slotNodeRemoved(KisNodeSP node) 0156 { 0157 KisReferenceImagesLayer *referenceImagesLayer = 0158 dynamic_cast<KisReferenceImagesLayer*>(node.data()); 0159 0160 if (referenceImagesLayer && referenceImagesLayer == d->layer) { 0161 setReferenceImageLayer(0, true); 0162 } 0163 } 0164 0165 void KisReferenceImagesDecoration::slotLayersChanged() 0166 { 0167 KisImageSP image = view()->image(); 0168 0169 KisReferenceImagesLayer *referenceImagesLayer = 0170 KisLayerUtils::findNodeByType<KisReferenceImagesLayer>(image->root()); 0171 0172 setReferenceImageLayer(referenceImagesLayer, true); 0173 } 0174 0175 void KisReferenceImagesDecoration::slotReferenceImagesChanged(const QRectF &dirtyRect) 0176 { 0177 d->updateBufferByImageCoordinates(dirtyRect); 0178 0179 QRectF documentRect = view()->viewConverter()->imageToDocument(dirtyRect); 0180 view()->canvasBase()->updateCanvasDecorations(documentRect); 0181 } 0182 0183 void KisReferenceImagesDecoration::setReferenceImageLayer(KisSharedPtr<KisReferenceImagesLayer> layer, bool updateCanvas) 0184 { 0185 if (d->layer != layer.data()) { 0186 KisSharedPtr<KisReferenceImagesLayer> oldLayer = d->layer.toStrongRef(); 0187 if (oldLayer) { 0188 oldLayer->disconnect(this); 0189 } 0190 0191 d->layer = layer; 0192 0193 if (layer) { 0194 connect(layer.data(), SIGNAL(sigUpdateCanvas(QRectF)), 0195 this, SLOT(slotReferenceImagesChanged(QRectF))); 0196 0197 const QRectF dirtyRect = layer->boundingImageRect(); 0198 0199 // If the view is not ready yet (because this is being constructed 0200 // from view.d's ctor and thus view.d is not available now), 0201 // do not update canvas because it will lead to a crash. 0202 if (updateCanvas && !dirtyRect.isEmpty()) { // in case the reference layer is just being loaded from the .kra file 0203 slotReferenceImagesChanged(dirtyRect); 0204 } 0205 } 0206 } 0207 }