1 /*
2  *  Copyright (c) 2018 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program 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
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_shape_layer_test.h"
20 
21 #include <QTest>
22 
23 #include "kis_global.h"
24 
25 #include "kis_shape_layer.h"
26 #include <KoPathShape.h>
27 #include <KoColorBackground.h>
28 #include "testutil.h"
29 
30 #include <KisPart.h>
31 #include <KisDocument.h>
32 
33 #include <kis_meta_data_store.h>
34 #include <kis_meta_data_merge_strategy_registry.h>
35 
36 #include "kis_filter_strategy.h"
37 
38 #include "kis_layer_utils.h"
39 
40 #include <sdk/tests/testutil.h>
41 
testMergeDownImpl(bool useImageTransformations)42 void testMergeDownImpl(bool useImageTransformations)
43 {
44     const QString testName = useImageTransformations ? "scale_and_merge_down" : "merge_down";
45 
46     using namespace TestUtil;
47 
48     ReferenceImageChecker chk(testName, "shape_layer_test", ReferenceImageChecker::InternalStorage);
49     chk.setMaxFailingPixels(10);
50 
51     QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
52 
53     const QRect refRect(0,0,64,64);
54     MaskParent p(refRect);
55 
56     const qreal resolution = 72.0 / 72.0;
57     p.image->setResolution(resolution, resolution);
58 
59     doc->setCurrentImage(p.image);
60 
61 
62     KisShapeLayerSP shapeLayer1 = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 150);
63 
64     {
65         KoPathShape* path = new KoPathShape();
66         path->setShapeId(KoPathShapeId);
67         path->moveTo(QPointF(5, 5));
68         path->lineTo(QPointF(5, 55));
69         path->lineTo(QPointF(20, 55));
70         path->lineTo(QPointF(20,  5));
71         path->close();
72         path->normalize();
73         path->setBackground(toQShared(new KoColorBackground(Qt::red)));
74 
75         path->setName("shape1");
76         path->setZIndex(1);
77         shapeLayer1->addShape(path);
78     }
79 
80     KisShapeLayerSP shapeLayer2 = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer2", 255);
81 
82     {
83         KoPathShape* path = new KoPathShape();
84         path->setShapeId(KoPathShapeId);
85         path->moveTo(QPointF(15, 10));
86         path->lineTo(QPointF(15, 55));
87         path->lineTo(QPointF(50, 55));
88         path->lineTo(QPointF(50,  10));
89         path->close();
90         path->normalize();
91         path->setBackground(toQShared(new KoColorBackground(Qt::green)));
92 
93         path->setName("shape2");
94         path->setZIndex(1);
95         shapeLayer2->addShape(path);
96     }
97 
98     p.image->addNode(shapeLayer1);
99     p.image->addNode(shapeLayer2);
100     shapeLayer1->setDirty();
101     shapeLayer2->setDirty();
102 
103     p.waitForImageAndShapeLayers();
104 
105     QCOMPARE(int(p.image->root()->childCount()), 3);
106 
107     chk.checkImage(p.image, "00_initial_layer_update");
108 
109     if (useImageTransformations) {
110 
111         KisFilterStrategy *strategy = new KisBilinearFilterStrategy();
112         p.image->scaleImage(QSize(32, 32), p.image->xRes(), p.image->yRes(), strategy);
113         p.waitForImageAndShapeLayers();
114 
115         chk.checkImage(p.image, "01_after_scale_down");
116     }
117 
118     p.image->mergeDown(shapeLayer2, KisMetaData::MergeStrategyRegistry::instance()->get("Drop"));
119     p.waitForImageAndShapeLayers();
120 
121     QCOMPARE(int(p.image->root()->childCount()), 2);
122 
123     KisShapeLayer *newShapeLayer = dynamic_cast<KisShapeLayer*>(p.image->root()->lastChild().data());
124     QVERIFY(newShapeLayer);
125 
126     QVERIFY(newShapeLayer != shapeLayer1.data());
127     QVERIFY(newShapeLayer != shapeLayer2.data());
128 
129     chk.checkImage(p.image, "02_after_merge_down");
130 
131     QVERIFY(chk.testPassed());
132 }
133 
testMergeDown()134 void KisShapeLayerTest::testMergeDown()
135 {
136     testMergeDownImpl(false);
137 }
138 
testScaleAndMergeDown()139 void KisShapeLayerTest::testScaleAndMergeDown()
140 {
141     testMergeDownImpl(true);
142 }
143 
144 namespace {
createSimpleShape(int zIndex)145 KoPathShape* createSimpleShape(int zIndex)
146 {
147     KoPathShape* path = new KoPathShape();
148     path->setShapeId(KoPathShapeId);
149     path->moveTo(QPointF(15, 10));
150     path->lineTo(QPointF(15, 55));
151     path->lineTo(QPointF(50, 55));
152     path->lineTo(QPointF(50,  10));
153     path->close();
154     path->normalize();
155     path->setBackground(toQShared(new KoColorBackground(Qt::green)));
156 
157     path->setName(QString("shape_%1").arg(zIndex));
158     path->setZIndex(zIndex);
159 
160     return path;
161 }
162 }
163 
164 #include "commands/KoShapeReorderCommand.h"
165 #include <limits>
166 
testMergingShapeZIndexesImpl(int firstIndexStart,int firstIndexStep,int firstIndexSize,int secondIndexStart,int secondIndexStep,int secondIndexSize)167 void testMergingShapeZIndexesImpl(int firstIndexStart,
168                                   int firstIndexStep,
169                                   int firstIndexSize,
170                                   int secondIndexStart,
171                                   int secondIndexStep,
172                                   int secondIndexSize)
173 {
174     QList<KoShape *> shapesBelow;
175     QList<KoShape *> shapesAbove;
176 
177     qDebug() << "Test zIndex merge:";
178     qDebug() << "    " << ppVar(firstIndexStart) << ppVar(firstIndexStep) << ppVar(firstIndexSize);
179     qDebug() << "    " << ppVar(secondIndexStart) << ppVar(secondIndexStep) << ppVar(secondIndexSize);
180 
181 
182     for (int i = 0; i < firstIndexSize; i++) {
183         shapesBelow.append(createSimpleShape(firstIndexStart + firstIndexStep * i));
184     }
185 
186     for (int i = 0; i < secondIndexSize; i++) {
187         shapesAbove.append(createSimpleShape(secondIndexStart + secondIndexStep * i));
188     }
189 
190     QList<KoShapeReorderCommand::IndexedShape> shapes =
191         KoShapeReorderCommand::mergeDownShapes(shapesBelow, shapesAbove);
192 
193     KoShapeReorderCommand cmd(shapes);
194     cmd.redo();
195 
196     for (int i = 0; i < shapesBelow.size(); i++) {
197         if (i > 0 && shapesBelow[i - 1]->zIndex() >= shapesBelow[i]->zIndex()) {
198             qDebug()  << ppVar(i);
199             qDebug() << ppVar(shapesBelow[i - 1]->zIndex()) << ppVar(shapesBelow[i]->zIndex());
200             QFAIL("Shapes have wrong ordering after merge!");
201         }
202     }
203 
204     if (!shapesBelow.isEmpty() && !shapesAbove.isEmpty()) {
205         if (shapesBelow.last()->zIndex() >= shapesAbove.first()->zIndex()) {
206             qDebug() << ppVar(shapesBelow.last()->zIndex()) << ppVar(shapesAbove.first()->zIndex());
207             QFAIL("Two shape groups have intersections after merge!");
208         }
209     }
210 
211     for (int i = 0; i < shapesAbove.size(); i++) {
212         if (i > 0 && shapesAbove[i - 1]->zIndex() >= shapesAbove[i]->zIndex()) {
213             qDebug()  << ppVar(i);
214             qDebug() << ppVar(shapesAbove[i - 1]->zIndex()) << ppVar(shapesAbove[i]->zIndex());
215             QFAIL("Shapes have wrong ordering after merge!");
216         }
217     }
218 
219 }
220 
testMergingShapeZIndexes()221 void KisShapeLayerTest::testMergingShapeZIndexes()
222 {
223     testMergingShapeZIndexesImpl(0, 1, 10,
224                                  5, 1, 10);
225 
226     testMergingShapeZIndexesImpl(0, 1, 0,
227                                  5, 1, 10);
228 
229     testMergingShapeZIndexesImpl(0, 1, 10,
230                                  5, 1, 0);
231 
232     testMergingShapeZIndexesImpl(std::numeric_limits<qint16>::max() - 10, 1, 10,
233                                  5, 1, 10);
234 
235     testMergingShapeZIndexesImpl(-32768, 1024, 64,
236                                  0, 1024, 31);
237 
238     testMergingShapeZIndexesImpl(-32768+1023, 1024, 64,
239                                  0, 1, 1024);
240 }
241 
testCloneScaledLayer()242 void KisShapeLayerTest::testCloneScaledLayer()
243 {
244     using namespace TestUtil;
245 
246     ReferenceImageChecker chk("scale_and_clone", "shape_layer_test", ReferenceImageChecker::InternalStorage);
247     chk.setMaxFailingPixels(10);
248 
249     QScopedPointer<KisDocument> doc(KisPart::instance()->createDocument());
250 
251     const QRect refRect(0,0,64,64);
252     MaskParent p(refRect);
253 
254     const qreal resolution = 72.0 / 72.0;
255     p.image->setResolution(resolution, resolution);
256 
257     doc->setCurrentImage(p.image);
258 
259 
260     KisShapeLayerSP shapeLayer1 = new KisShapeLayer(doc->shapeController(), p.image, "shapeLayer1", 150);
261 
262     {
263         KoPathShape* path = new KoPathShape();
264         path->setShapeId(KoPathShapeId);
265         path->moveTo(QPointF(5, 5));
266         path->lineTo(QPointF(5, 55));
267         path->lineTo(QPointF(20, 55));
268         path->lineTo(QPointF(20,  5));
269         path->close();
270         path->normalize();
271         path->setBackground(toQShared(new KoColorBackground(Qt::red)));
272 
273         path->setName("shape1");
274         path->setZIndex(1);
275         shapeLayer1->addShape(path);
276     }
277 
278     p.image->addNode(shapeLayer1);
279     shapeLayer1->setDirty();
280 
281     p.waitForImageAndShapeLayers();
282 
283     QCOMPARE(int(p.image->root()->childCount()), 2);
284 
285     chk.checkImage(p.image, "00_initial_layer_update");
286 
287     {
288 
289         KisFilterStrategy *strategy = new KisBilinearFilterStrategy();
290         p.image->scaleImage(QSize(32, 32), p.image->xRes(), p.image->yRes(), strategy);
291         p.waitForImageAndShapeLayers();
292 
293         chk.checkImage(p.image, "01_after_scale_down");
294     }
295 
296     KisNodeSP clonedLayer = shapeLayer1->clone();
297 
298     p.image->removeNode(shapeLayer1);
299     p.image->addNode(clonedLayer);
300     clonedLayer->setDirty();
301 
302     p.waitForImageAndShapeLayers();
303 
304     QCOMPARE(int(p.image->root()->childCount()), 2);
305     chk.checkImage(p.image, "01_after_scale_down");
306 
307     QVERIFY(chk.testPassed());
308 }
309 
310 KISTEST_MAIN(KisShapeLayerTest)
311