File indexing completed on 2024-12-22 04:12:34
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Jouni Pentikäinen <joupent@gmail.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <KoShapeCreateCommand.h> 0008 #include <KoShapeDeleteCommand.h> 0009 #include <KoKeepShapesSelectedCommand.h> 0010 #include <KoSelection.h> 0011 #include <kis_node_visitor.h> 0012 #include <kis_processing_visitor.h> 0013 #include <kis_shape_layer_canvas.h> 0014 0015 #include "kis_default_bounds.h" 0016 #include "KisReferenceImagesLayer.h" 0017 #include "KisReferenceImage.h" 0018 #include "KisDocument.h" 0019 #include <KoViewConverter.h> 0020 0021 struct AddReferenceImagesCommand : KoShapeCreateCommand 0022 { 0023 AddReferenceImagesCommand(KisDocument *document, KisSharedPtr<KisReferenceImagesLayer> layer, const QList<KoShape*> referenceImages, KUndo2Command *parent = nullptr) 0024 : KoShapeCreateCommand(layer->shapeController(), referenceImages, layer.data(), parent, kundo2_i18n("Add reference image")) 0025 , m_document(document) 0026 , m_layer(layer) 0027 {} 0028 0029 void redo() override { 0030 auto layer = m_document->referenceImagesLayer(); 0031 KIS_SAFE_ASSERT_RECOVER_NOOP(!layer || layer == m_layer); 0032 0033 if (!layer) { 0034 m_document->setReferenceImagesLayer(m_layer, true); 0035 } 0036 0037 KoShapeCreateCommand::redo(); 0038 } 0039 0040 void undo() override { 0041 KoShapeCreateCommand::undo(); 0042 0043 if (m_layer->shapeCount() == 0) { 0044 m_document->setReferenceImagesLayer(nullptr, true); 0045 } 0046 } 0047 0048 private: 0049 KisDocument *m_document; 0050 KisSharedPtr<KisReferenceImagesLayer> m_layer; 0051 }; 0052 0053 struct RemoveReferenceImagesCommand : KoShapeDeleteCommand 0054 { 0055 RemoveReferenceImagesCommand(KisDocument *document, KisSharedPtr<KisReferenceImagesLayer> layer, QList<KoShape *> referenceImages, KUndo2Command *parent = nullptr) 0056 : KoShapeDeleteCommand(layer->shapeController(), referenceImages, parent) 0057 , m_document(document) 0058 , m_layer(layer) 0059 {} 0060 0061 0062 void redo() override { 0063 KoShapeDeleteCommand::redo(); 0064 0065 if (m_layer->shapeCount() == 0) { 0066 m_document->setReferenceImagesLayer(nullptr, true); 0067 } 0068 } 0069 0070 void undo() override { 0071 auto layer = m_document->referenceImagesLayer(); 0072 KIS_SAFE_ASSERT_RECOVER_NOOP(!layer || layer == m_layer); 0073 0074 if (!layer) { 0075 m_document->setReferenceImagesLayer(m_layer, true); 0076 } 0077 0078 KoShapeDeleteCommand::undo(); 0079 } 0080 0081 private: 0082 KisDocument *m_document; 0083 KisSharedPtr<KisReferenceImagesLayer> m_layer; 0084 }; 0085 0086 class ReferenceImagesCanvas : public KisShapeLayerCanvasBase 0087 { 0088 Q_OBJECT 0089 public: 0090 ReferenceImagesCanvas(const KoColorSpace *cs, KisDefaultBoundsBaseSP defaultBounds, KisReferenceImagesLayer *parent) 0091 : KisShapeLayerCanvasBase(parent) 0092 , m_layer(parent) 0093 , m_fallbackProjection(new KisPaintDevice(parent, cs, defaultBounds)) 0094 , m_compressor(KisThreadSafeSignalCompressor(25, KisSignalCompressor::FIRST_ACTIVE)) 0095 { 0096 connect(&m_compressor, SIGNAL(timeout()), this, SLOT(slotAsyncRepaint())); 0097 } 0098 0099 ReferenceImagesCanvas(const ReferenceImagesCanvas &rhs, KisReferenceImagesLayer *parent) 0100 : KisShapeLayerCanvasBase(rhs, parent) 0101 , m_layer(parent) 0102 , m_fallbackProjection(new KisPaintDevice(*rhs.m_fallbackProjection)) 0103 , m_compressor(KisThreadSafeSignalCompressor(25, KisSignalCompressor::FIRST_ACTIVE)) 0104 { 0105 connect(&m_compressor, SIGNAL(timeout()), this, SLOT(slotAsyncRepaint())); 0106 } 0107 0108 void updateCanvas(const QRectF &rect) override 0109 { 0110 if (!m_layer->image() || m_isDestroying) { 0111 return; 0112 } 0113 0114 m_dirtyRect |= rect; 0115 0116 m_compressor.start(); 0117 m_hasUpdateInCompressor = true; 0118 } 0119 0120 void forceRepaint() override 0121 { 0122 m_layer->signalUpdate(m_layer->boundingImageRect()); 0123 } 0124 0125 bool hasPendingUpdates() const override 0126 { 0127 return m_hasUpdateInCompressor; 0128 } 0129 0130 void rerenderAfterBeingInvisible() override {} 0131 void resetCache() override {} 0132 0133 KisPaintDeviceSP projection() const override { 0134 return m_fallbackProjection; 0135 } 0136 private Q_SLOTS: 0137 void slotAsyncRepaint() { 0138 QRectF r = viewConverter()->documentToView(m_dirtyRect); 0139 m_layer->signalUpdate(r); 0140 m_dirtyRect = QRectF(); 0141 m_hasUpdateInCompressor = false; 0142 } 0143 0144 private: 0145 KisReferenceImagesLayer *m_layer; 0146 KisPaintDeviceSP m_fallbackProjection; 0147 KisThreadSafeSignalCompressor m_compressor; 0148 QRectF m_dirtyRect; 0149 volatile bool m_hasUpdateInCompressor = false; 0150 }; 0151 0152 KisReferenceImagesLayer::KisReferenceImagesLayer(KoShapeControllerBase* shapeController, KisImageWSP image) 0153 : KisShapeLayer(shapeController, image, i18n("Reference images"), OPACITY_OPAQUE_U8, 0154 [&] () { return new ReferenceImagesCanvas(image->colorSpace(), new KisDefaultBounds(image), this); }) 0155 {} 0156 0157 KisReferenceImagesLayer::KisReferenceImagesLayer(const KisReferenceImagesLayer &rhs) 0158 : KisShapeLayer(rhs, rhs.shapeController(), 0159 [&] () { 0160 const ReferenceImagesCanvas* referenceImagesCanvas = dynamic_cast<const ReferenceImagesCanvas*>(rhs.canvas()); 0161 KIS_ASSERT(referenceImagesCanvas); 0162 return new ReferenceImagesCanvas(*referenceImagesCanvas, this); }) 0163 {} 0164 0165 KUndo2Command * KisReferenceImagesLayer::addReferenceImages(KisDocument *document, const QList<KoShape*> referenceImages) 0166 { 0167 KisSharedPtr<KisReferenceImagesLayer> layer = document->referenceImagesLayer(); 0168 if (!layer) { 0169 layer = new KisReferenceImagesLayer(document->shapeController(), document->image()); 0170 } 0171 0172 KUndo2Command *parentCommand = new KUndo2Command(); 0173 0174 new KoKeepShapesSelectedCommand(layer->shapeManager()->selection()->selectedShapes(), {}, layer->selectedShapesProxy(), KisCommandUtils::FlipFlopCommand::State::INITIALIZING, parentCommand); 0175 AddReferenceImagesCommand *cmd = new AddReferenceImagesCommand(document, layer, referenceImages, parentCommand); 0176 parentCommand->setText(cmd->text()); 0177 new KoKeepShapesSelectedCommand({}, referenceImages, layer->selectedShapesProxy(), KisCommandUtils::FlipFlopCommand::State::FINALIZING, parentCommand); 0178 0179 return parentCommand; 0180 } 0181 0182 KUndo2Command * KisReferenceImagesLayer::removeReferenceImages(KisDocument *document, QList<KoShape*> referenceImages) 0183 { 0184 return new RemoveReferenceImagesCommand(document, this, referenceImages); 0185 } 0186 0187 QVector<KisReferenceImage*> KisReferenceImagesLayer::referenceImages() const 0188 { 0189 QVector<KisReferenceImage*> references; 0190 0191 Q_FOREACH(auto shape, shapes()) { 0192 KisReferenceImage *referenceImage = dynamic_cast<KisReferenceImage*>(shape); 0193 if (referenceImage) { 0194 references.append(referenceImage); 0195 } 0196 } 0197 return references; 0198 } 0199 0200 void KisReferenceImagesLayer::paintReferences(QPainter &painter) { 0201 painter.setTransform(converter()->documentToView(), true); 0202 shapeManager()->paint(painter); 0203 } 0204 0205 bool KisReferenceImagesLayer::allowAsChild(KisNodeSP) const 0206 { 0207 return false; 0208 } 0209 0210 bool KisReferenceImagesLayer::accept(KisNodeVisitor &visitor) 0211 { 0212 return visitor.visit(this); 0213 } 0214 0215 void KisReferenceImagesLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) 0216 { 0217 visitor.visit(this, undoAdapter); 0218 } 0219 0220 bool KisReferenceImagesLayer::isFakeNode() const 0221 { 0222 return true; 0223 } 0224 0225 KUndo2Command *KisReferenceImagesLayer::setProfile(const KoColorProfile *profile) 0226 { 0227 // references should not be converted with the image 0228 Q_UNUSED(profile); 0229 return 0; 0230 } 0231 0232 KUndo2Command *KisReferenceImagesLayer::convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) 0233 { 0234 // references should not be converted with the image 0235 Q_UNUSED(dstColorSpace); 0236 Q_UNUSED(renderingIntent); 0237 Q_UNUSED(conversionFlags); 0238 return 0; 0239 } 0240 0241 void KisReferenceImagesLayer::signalUpdate(const QRectF &rect) 0242 { 0243 emit sigUpdateCanvas(rect); 0244 } 0245 0246 QRectF KisReferenceImagesLayer::boundingImageRect() const 0247 { 0248 return converter()->documentToView(boundingRect()); 0249 } 0250 0251 QColor KisReferenceImagesLayer::getPixel(QPointF position) const 0252 { 0253 const QPointF docPoint = converter()->viewToDocument(position); 0254 0255 KoShape *shape = shapeManager()->shapeAt(docPoint); 0256 0257 if (shape) { 0258 auto *reference = dynamic_cast<KisReferenceImage*>(shape); 0259 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, QColor()); 0260 0261 return reference->getPixel(docPoint); 0262 } 0263 0264 return QColor(); 0265 } 0266 0267 #include "KisReferenceImagesLayer.moc"