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