1 /*
2  * Copyright (C) 2017 Jouni Pentikäinen <joupent@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include <KoShapeCreateCommand.h>
21 #include <KoShapeDeleteCommand.h>
22 #include <KoKeepShapesSelectedCommand.h>
23 #include <KoSelection.h>
24 #include <kis_node_visitor.h>
25 #include <kis_processing_visitor.h>
26 #include <kis_shape_layer_canvas.h>
27 
28 #include "KisReferenceImagesLayer.h"
29 #include "KisReferenceImage.h"
30 #include "KisDocument.h"
31 
32 struct AddReferenceImagesCommand : KoShapeCreateCommand
33 {
AddReferenceImagesCommandAddReferenceImagesCommand34     AddReferenceImagesCommand(KisDocument *document, KisSharedPtr<KisReferenceImagesLayer> layer, const QList<KoShape*> referenceImages, KUndo2Command *parent = nullptr)
35         : KoShapeCreateCommand(layer->shapeController(), referenceImages, layer.data(), parent, kundo2_i18n("Add reference image"))
36         , m_document(document)
37         , m_layer(layer)
38     {}
39 
redoAddReferenceImagesCommand40     void redo() override {
41         auto layer = m_document->referenceImagesLayer();
42         KIS_SAFE_ASSERT_RECOVER_NOOP(!layer || layer == m_layer);
43 
44         if (!layer) {
45             m_document->setReferenceImagesLayer(m_layer, true);
46         }
47 
48         KoShapeCreateCommand::redo();
49     }
50 
undoAddReferenceImagesCommand51     void undo() override {
52         KoShapeCreateCommand::undo();
53 
54         if (m_layer->shapeCount() == 0) {
55             m_document->setReferenceImagesLayer(nullptr, true);
56         }
57     }
58 
59 private:
60     KisDocument *m_document;
61     KisSharedPtr<KisReferenceImagesLayer> m_layer;
62 };
63 
64 struct RemoveReferenceImagesCommand : KoShapeDeleteCommand
65 {
RemoveReferenceImagesCommandRemoveReferenceImagesCommand66     RemoveReferenceImagesCommand(KisDocument *document, KisSharedPtr<KisReferenceImagesLayer> layer, QList<KoShape *> referenceImages, KUndo2Command *parent = nullptr)
67         : KoShapeDeleteCommand(layer->shapeController(), referenceImages, parent)
68         , m_document(document)
69         , m_layer(layer)
70     {}
71 
72 
redoRemoveReferenceImagesCommand73     void redo() override {
74         KoShapeDeleteCommand::redo();
75 
76         if (m_layer->shapeCount() == 0) {
77             m_document->setReferenceImagesLayer(nullptr, true);
78         }
79     }
80 
undoRemoveReferenceImagesCommand81     void undo() override {
82         auto layer = m_document->referenceImagesLayer();
83         KIS_SAFE_ASSERT_RECOVER_NOOP(!layer || layer == m_layer);
84 
85         if (!layer) {
86             m_document->setReferenceImagesLayer(m_layer, true);
87         }
88 
89         KoShapeDeleteCommand::undo();
90     }
91 
92 private:
93     KisDocument *m_document;
94     KisSharedPtr<KisReferenceImagesLayer> m_layer;
95 };
96 
97 class ReferenceImagesCanvas : public KisShapeLayerCanvasBase
98 {
99 public:
ReferenceImagesCanvas(KisReferenceImagesLayer * parent,KisImageWSP image)100     ReferenceImagesCanvas(KisReferenceImagesLayer *parent, KisImageWSP image)
101         : KisShapeLayerCanvasBase(parent, image)
102         , m_layer(parent)
103     {}
104 
updateCanvas(const QRectF & rect)105     void updateCanvas(const QRectF &rect) override
106     {
107         if (!m_layer->image() || m_isDestroying) {
108             return;
109         }
110 
111         QRectF r = m_viewConverter->documentToView(rect);
112         m_layer->signalUpdate(r);
113     }
114 
forceRepaint()115     void forceRepaint() override
116     {
117         m_layer->signalUpdate(m_layer->boundingImageRect());
118     }
119 
hasPendingUpdates() const120     bool hasPendingUpdates() const override
121     {
122         return false;
123     }
124 
rerenderAfterBeingInvisible()125     void rerenderAfterBeingInvisible() override {}
resetCache()126     void resetCache() override {}
setImage(KisImageWSP image)127     void setImage(KisImageWSP image) override
128     {
129         m_viewConverter->setImage(image);
130     }
131 
132 private:
133     KisReferenceImagesLayer *m_layer;
134 };
135 
KisReferenceImagesLayer(KoShapeControllerBase * shapeController,KisImageWSP image)136 KisReferenceImagesLayer::KisReferenceImagesLayer(KoShapeControllerBase* shapeController, KisImageWSP image)
137     : KisShapeLayer(shapeController, image, i18n("Reference images"), OPACITY_OPAQUE_U8, new ReferenceImagesCanvas(this, image))
138 {}
139 
KisReferenceImagesLayer(const KisReferenceImagesLayer & rhs)140 KisReferenceImagesLayer::KisReferenceImagesLayer(const KisReferenceImagesLayer &rhs)
141     : KisShapeLayer(rhs, rhs.shapeController(), new ReferenceImagesCanvas(this, rhs.image()))
142 {}
143 
addReferenceImages(KisDocument * document,const QList<KoShape * > referenceImages)144 KUndo2Command * KisReferenceImagesLayer::addReferenceImages(KisDocument *document, const QList<KoShape*> referenceImages)
145 {
146     KisSharedPtr<KisReferenceImagesLayer> layer = document->referenceImagesLayer();
147     if (!layer) {
148         layer = new KisReferenceImagesLayer(document->shapeController(), document->image());
149     }
150 
151     KUndo2Command *parentCommand = new KUndo2Command();
152 
153     new KoKeepShapesSelectedCommand(layer->shapeManager()->selection()->selectedShapes(), {}, layer->selectedShapesProxy(), KisCommandUtils::FlipFlopCommand::State::INITIALIZING, parentCommand);
154     AddReferenceImagesCommand *cmd = new AddReferenceImagesCommand(document, layer, referenceImages, parentCommand);
155     parentCommand->setText(cmd->text());
156     new KoKeepShapesSelectedCommand({}, referenceImages, layer->selectedShapesProxy(), KisCommandUtils::FlipFlopCommand::State::FINALIZING, parentCommand);
157 
158     return parentCommand;
159 }
160 
removeReferenceImages(KisDocument * document,QList<KoShape * > referenceImages)161 KUndo2Command * KisReferenceImagesLayer::removeReferenceImages(KisDocument *document, QList<KoShape*> referenceImages)
162 {
163     return new RemoveReferenceImagesCommand(document, this, referenceImages);
164 }
165 
referenceImages() const166 QVector<KisReferenceImage*> KisReferenceImagesLayer::referenceImages() const
167 {
168     QVector<KisReferenceImage*> references;
169 
170     Q_FOREACH(auto shape, shapes()) {
171         KisReferenceImage *referenceImage = dynamic_cast<KisReferenceImage*>(shape);
172         if (referenceImage) {
173             references.append(referenceImage);
174         }
175     }
176     return references;
177 }
178 
paintReferences(QPainter & painter)179 void KisReferenceImagesLayer::paintReferences(QPainter &painter) {
180     painter.setTransform(converter()->documentToView(), true);
181     shapeManager()->paint(painter, false);
182 }
183 
allowAsChild(KisNodeSP) const184 bool KisReferenceImagesLayer::allowAsChild(KisNodeSP) const
185 {
186     return false;
187 }
188 
accept(KisNodeVisitor & visitor)189 bool KisReferenceImagesLayer::accept(KisNodeVisitor &visitor)
190 {
191     return visitor.visit(this);
192 }
193 
accept(KisProcessingVisitor & visitor,KisUndoAdapter * undoAdapter)194 void KisReferenceImagesLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
195 {
196     visitor.visit(this, undoAdapter);
197 }
198 
isFakeNode() const199 bool KisReferenceImagesLayer::isFakeNode() const
200 {
201     return true;
202 }
203 
setProfile(const KoColorProfile * profile)204 KUndo2Command *KisReferenceImagesLayer::setProfile(const KoColorProfile *profile)
205 {
206     // references should not be converted with the image
207     Q_UNUSED(profile);
208     return 0;
209 }
210 
convertTo(const KoColorSpace * dstColorSpace,KoColorConversionTransformation::Intent renderingIntent,KoColorConversionTransformation::ConversionFlags conversionFlags)211 KUndo2Command *KisReferenceImagesLayer::convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
212 {
213     // references should not be converted with the image
214     Q_UNUSED(dstColorSpace);
215     Q_UNUSED(renderingIntent);
216     Q_UNUSED(conversionFlags);
217     return 0;
218 }
219 
signalUpdate(const QRectF & rect)220 void KisReferenceImagesLayer::signalUpdate(const QRectF &rect)
221 {
222     emit sigUpdateCanvas(rect);
223 }
224 
boundingImageRect() const225 QRectF KisReferenceImagesLayer::boundingImageRect() const
226 {
227     return converter()->documentToView(boundingRect());
228 }
229 
getPixel(QPointF position) const230 QColor KisReferenceImagesLayer::getPixel(QPointF position) const
231 {
232     const QPointF docPoint = converter()->viewToDocument(position);
233 
234     KoShape *shape = shapeManager()->shapeAt(docPoint);
235 
236     if (shape) {
237         auto *reference = dynamic_cast<KisReferenceImage*>(shape);
238         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(reference, QColor());
239 
240         return reference->getPixel(docPoint);
241     }
242 
243     return QColor();
244 }
245