1 /*
2  *  Copyright (c) 2007 Boudewijn Rempt <boud@valdyas.org>
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_paint_device_test.h"
20 #include <QTest>
21 
22 #include <QTime>
23 
24 #include <KoColor.h>
25 #include <KoColorSpace.h>
26 #include <KoColorSpaceRegistry.h>
27 #include <KoStore.h>
28 
29 #include "kis_paint_device_writer.h"
30 #include "kis_painter.h"
31 #include "kis_types.h"
32 #include "kis_paint_device.h"
33 #include "kis_layer.h"
34 #include "kis_paint_layer.h"
35 #include "kis_selection.h"
36 #include "kis_datamanager.h"
37 #include "kis_global.h"
38 #include "testutil.h"
39 #include "kis_transaction.h"
40 #include "kis_image.h"
41 #include "config-limit-long-tests.h"
42 #include "kistest.h"
43 
44 
45 class KisFakePaintDeviceWriter : public KisPaintDeviceWriter {
46 public:
KisFakePaintDeviceWriter(KoStore * store)47     KisFakePaintDeviceWriter(KoStore *store)
48         : m_store(store)
49     {
50     }
51 
write(const QByteArray & data)52     bool write(const QByteArray &data) override {
53         return (m_store->write(data) == data.size());
54     }
55 
write(const char * data,qint64 length)56     bool write(const char* data, qint64 length) override {
57         return (m_store->write(data, length) == length);
58     }
59 
60     KoStore *m_store;
61 };
62 
testCreation()63 void KisPaintDeviceTest::testCreation()
64 {
65     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
66     KisPaintDeviceSP dev = new KisPaintDevice(cs);
67     QVERIFY(dev->objectName().isEmpty());
68 
69     dev = new KisPaintDevice(cs);
70     QVERIFY(*dev->colorSpace() == *cs);
71     QVERIFY(dev->x() == 0);
72     QVERIFY(dev->y() == 0);
73     QVERIFY(dev->pixelSize() == cs->pixelSize());
74     QVERIFY(dev->channelCount() == cs->channelCount());
75     QVERIFY(dev->dataManager() != 0);
76 
77     KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
78     KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);
79 
80     dev = new KisPaintDevice(layer.data(), cs);
81     QVERIFY(*dev->colorSpace() == *cs);
82     QVERIFY(dev->x() == 0);
83     QVERIFY(dev->y() == 0);
84     QVERIFY(dev->pixelSize() == cs->pixelSize());
85     QVERIFY(dev->channelCount() == cs->channelCount());
86     QVERIFY(dev->dataManager() != 0);
87 
88     // Let the layer go out of scope and see what happens
89     {
90         KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250);
91         dev = new KisPaintDevice(l2.data(), cs);
92     }
93 
94 }
95 
96 
testStore()97 void KisPaintDeviceTest::testStore()
98 {
99 
100     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
101     KisPaintDeviceSP dev = new KisPaintDevice(cs);
102 
103     KoStore * readStore =
104         KoStore::createStore(QString(FILES_DATA_DIR) + '/' + "store_test.kra", KoStore::Read);
105     readStore->open("built image/layers/layer0");
106     QVERIFY(dev->read(readStore->device()));
107     readStore->close();
108     delete readStore;
109 
110     QVERIFY(dev->exactBounds() == QRect(0, 0, 100, 100));
111 
112     KoStore * writeStore =
113         KoStore::createStore(QString(FILES_OUTPUT_DIR) + '/' + "store_test_out.kra", KoStore::Write);
114     KisFakePaintDeviceWriter fakeWriter(writeStore);
115     writeStore->open("built image/layers/layer0");
116     QVERIFY(dev->write(fakeWriter));
117     writeStore->close();
118     delete writeStore;
119 
120     KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
121     readStore =
122         KoStore::createStore(QString(FILES_OUTPUT_DIR) + '/' + "store_test_out.kra", KoStore::Read);
123     readStore->open("built image/layers/layer0");
124     QVERIFY(dev2->read(readStore->device()));
125     readStore->close();
126     delete readStore;
127 
128     QVERIFY(dev2->exactBounds() == QRect(0, 0, 100, 100));
129 
130     QPoint pt;
131     if (!TestUtil::comparePaintDevices(pt, dev, dev2)) {
132         QFAIL(QString("Loading a saved image is not pixel perfect, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1());
133     }
134 
135 }
136 
testGeometry()137 void KisPaintDeviceTest::testGeometry()
138 {
139     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
140     KisPaintDeviceSP dev = new KisPaintDevice(cs);
141 
142     quint8* pixel = new quint8[cs->pixelSize()];
143     cs->fromQColor(Qt::white, pixel);
144     dev->fill(0, 0, 512, 512, pixel);
145 
146     QCOMPARE(dev->exactBounds(), QRect(0, 0, 512, 512));
147     QCOMPARE(dev->extent(), QRect(0, 0, 512, 512));
148 
149     dev->moveTo(10, 10);
150 
151     QCOMPARE(dev->exactBounds(), QRect(10, 10, 512, 512));
152     QCOMPARE(dev->extent(), QRect(10, 10, 512, 512));
153 
154     dev->crop(50, 50, 50, 50);
155     QCOMPARE(dev->exactBounds(), QRect(50, 50, 50, 50));
156     QCOMPARE(dev->extent(), QRect(10, 10, 128, 128));
157 
158     QColor c;
159 
160     dev->clear(QRect(50, 50, 50, 50));
161     dev->pixel(80, 80, &c);
162     QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8);
163 
164     dev->fill(0, 0, 512, 512, pixel);
165     dev->pixel(80, 80, &c);
166     QVERIFY(c == Qt::white);
167     QVERIFY(c.alpha() == OPACITY_OPAQUE_U8);
168 
169     dev->clear();
170     dev->pixel(80, 80, &c);
171     QVERIFY(c.alpha() == OPACITY_TRANSPARENT_U8);
172 
173     QVERIFY(dev->extent().isEmpty());
174     QVERIFY(dev->exactBounds().isEmpty());
175 
176 }
177 
testClear()178 void KisPaintDeviceTest::testClear()
179 {
180     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
181     KisPaintDeviceSP dev = new KisPaintDevice(cs);
182 
183     QVERIFY(!dev->extent().isValid());
184     QVERIFY(!dev->exactBounds().isValid());
185 
186     dev->clear();
187 
188     QVERIFY(!dev->extent().isValid());
189     QVERIFY(!dev->exactBounds().isValid());
190 
191     QRect fillRect1(50, 100, 150, 100);
192     dev->fill(fillRect1, KoColor(Qt::red, cs));
193 
194     QCOMPARE(dev->extent(), QRect(0, 64, 256, 192));
195     QCOMPARE(dev->exactBounds(), fillRect1);
196 
197     dev->clear(QRect(100, 100, 100, 100));
198 
199     QCOMPARE(dev->extent(), QRect(0, 64, 256, 192));
200     QCOMPARE(dev->exactBounds(), QRect(50, 100, 50, 100));
201 
202     dev->clear();
203 
204     QVERIFY(!dev->extent().isValid());
205     QVERIFY(!dev->exactBounds().isValid());
206 }
207 
testCrop()208 void KisPaintDeviceTest::testCrop()
209 {
210     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
211     KisPaintDeviceSP dev = new KisPaintDevice(cs);
212     quint8* pixel = new quint8[cs->pixelSize()];
213     cs->fromQColor(Qt::white, pixel);
214     dev->fill(-14, 8, 433, 512, pixel);
215 
216     QVERIFY(dev->exactBounds() == QRect(-14, 8, 433, 512));
217 
218     // Crop inside
219     dev->crop(50, 50, 150, 150);
220     QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150));
221 
222     // Crop outside, pd should not grow
223     dev->crop(0, 0, 1000, 1000);
224     QVERIFY(dev->exactBounds() == QRect(50, 50, 150, 150));
225 }
226 
testRoundtripReadWrite()227 void KisPaintDeviceTest::testRoundtripReadWrite()
228 {
229     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
230     KisPaintDeviceSP dev = new KisPaintDevice(cs);
231     QImage image(QString(FILES_DATA_DIR) + '/' + "tile.png");
232     dev->convertFromQImage(image, 0);
233 
234     quint8* bytes = new quint8[cs->pixelSize() * image.width() * image.height()];
235     memset(bytes, 0, image.width() * image.height() * dev->pixelSize());
236     dev->readBytes(bytes, image.rect());
237 
238     KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
239     dev2->writeBytes(bytes, image.rect());
240     QVERIFY(dev2->exactBounds() == image.rect());
241 
242     dev2->convertToQImage(0, 0, 0, image.width(), image.height()).save("readwrite.png");
243 
244 
245     QPoint pt;
246     if (!TestUtil::comparePaintDevices(pt, dev, dev2)) {
247         QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1());
248     }
249 }
250 
logFailure(const QString & reason,const KoColorSpace * srcCs,const KoColorSpace * dstCs)251 void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs)
252 {
253     QString profile1("no profile");
254     QString profile2("no profile");
255     if (srcCs->profile())
256         profile1 = srcCs->profile()->name();
257     if (dstCs->profile())
258         profile2 = dstCs->profile()->name();
259 
260     QWARN(QString("Failed %1 %2 -> %3 %4 %5")
261           .arg(srcCs->name())
262           .arg(profile1)
263           .arg(dstCs->name())
264           .arg(profile2)
265           .arg(reason)
266           .toLatin1());
267 }
268 
testColorSpaceConversion()269 void KisPaintDeviceTest::testColorSpaceConversion()
270 {
271     QImage image(QString(FILES_DATA_DIR) + '/' + "tile.png");
272     const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8();
273     const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16();
274     KisPaintDeviceSP dev = new KisPaintDevice(srcCs);
275     dev->convertFromQImage(image, 0);
276     dev->moveTo(10, 10);   // Unalign with tile boundaries
277     KUndo2Command* cmd = new KUndo2Command();
278     dev->convertTo(dstCs,
279                    KoColorConversionTransformation::internalRenderingIntent(),
280                    KoColorConversionTransformation::internalConversionFlags(),
281                    cmd);
282 
283     QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height()));
284     QCOMPARE(dev->pixelSize(), dstCs->pixelSize());
285     QVERIFY(*dev->colorSpace() == *dstCs);
286 
287     cmd->redo();
288     cmd->undo();
289 
290     QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height()));
291     QCOMPARE(dev->pixelSize(), srcCs->pixelSize());
292     QVERIFY(*dev->colorSpace() == *srcCs);
293 
294     delete cmd;
295 }
296 
297 
testRoundtripConversion()298 void KisPaintDeviceTest::testRoundtripConversion()
299 {
300     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
301     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
302     KisPaintDeviceSP dev = new KisPaintDevice(cs);
303     dev->convertFromQImage(image, 0);
304     QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
305 
306     QPoint errpoint;
307 
308     if (!TestUtil::compareQImages(errpoint, image, result)) {
309         image.save("kis_paint_device_test_test_roundtrip_qimage.png");
310         result.save("kis_paint_device_test_test_roundtrip_result.png");
311         QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
312     }
313 }
314 
testFastBitBlt()315 void KisPaintDeviceTest::testFastBitBlt()
316 {
317     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
318     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
319     KisPaintDeviceSP dstDev = new KisPaintDevice(cs);
320     KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
321     srcDev->convertFromQImage(image, 0);
322 
323     QRect cloneRect(100,100,200,200);
324     QPoint errpoint;
325 
326 
327     QVERIFY(dstDev->fastBitBltPossible(srcDev));
328     dstDev->fastBitBlt(srcDev, cloneRect);
329 
330     QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
331                                                cloneRect.width(), cloneRect.height());
332     QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
333                                               cloneRect.width(), cloneRect.height());
334 
335     if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) {
336         QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
337     }
338 
339     // Test Rough version
340     dstDev->clear();
341     dstDev->fastBitBltRough(srcDev, cloneRect);
342 
343     srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
344                                        cloneRect.width(), cloneRect.height());
345     dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
346                                        cloneRect.width(), cloneRect.height());
347 
348     if (!TestUtil::compareQImages(errpoint, srcImage, dstImage)) {
349         QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
350     }
351 
352     srcDev->moveTo(10,10);
353     QVERIFY(!dstDev->fastBitBltPossible(srcDev));
354 }
355 
testMakeClone()356 void KisPaintDeviceTest::testMakeClone()
357 {
358     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
359 
360     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
361     KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
362     srcDev->convertFromQImage(image, 0);
363     srcDev->moveTo(10,10);
364 
365     const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16();
366     KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS);
367     dstDev->moveTo(1000,1000);
368 
369     QVERIFY(!dstDev->fastBitBltPossible(srcDev));
370 
371     QRect cloneRect(100,100,200,200);
372     QPoint errpoint;
373 
374     dstDev->makeCloneFrom(srcDev, cloneRect);
375 
376     QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace());
377     QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize());
378     QCOMPARE(dstDev->x(), srcDev->x());
379     QCOMPARE(dstDev->y(), srcDev->y());
380     QCOMPARE(dstDev->exactBounds(), cloneRect);
381 
382     QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
383                                               cloneRect.width(), cloneRect.height());
384     QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
385                                               cloneRect.width(), cloneRect.height());
386     if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) {
387         QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
388     }
389 }
390 
testThumbnail()391 void KisPaintDeviceTest::testThumbnail()
392 {
393     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
394     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
395     KisPaintDeviceSP dev = new KisPaintDevice(cs);
396     dev->convertFromQImage(image, 0);
397     {
398         KisPaintDeviceSP thumb = dev->createThumbnailDevice(50, 50);
399         QRect rc = thumb->exactBounds();
400         QVERIFY(rc.width() <= 50);
401         QVERIFY(rc.height() <= 50);
402     }
403     {
404         QImage thumb = dev->createThumbnail(50, 50);
405         QVERIFY(!thumb.isNull());
406         QVERIFY(thumb.width() <= 50);
407         QVERIFY(thumb.height() <= 50);
408         image.save("kis_paint_device_test_test_thumbnail.png");
409     }
410 }
411 
testThumbnailDeviceWithOffset()412 void KisPaintDeviceTest::testThumbnailDeviceWithOffset()
413 {
414     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
415     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
416     KisPaintDeviceSP dev = new KisPaintDevice(cs);
417     dev->convertFromQImage(image, 0);
418     dev->setX(10);
419     dev->setY(10);
420 
421     QImage thumb = dev->createThumbnail(640,441,QRect(10,10,640,441));
422 
423     image.save("oring.png");
424     thumb.save("thumb.png");
425 
426     QPoint pt;
427     QVERIFY(TestUtil::compareQImages(pt, thumb, image));
428 }
429 
testCaching()430 void KisPaintDeviceTest::testCaching()
431 {
432     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
433     KisPaintDeviceSP dev = new KisPaintDevice(cs);
434 
435     quint8* whitePixel = new quint8[cs->pixelSize()];
436     cs->fromQColor(Qt::white, whitePixel);
437 
438     quint8* blackPixel = new quint8[cs->pixelSize()];
439     cs->fromQColor(Qt::black, blackPixel);
440 
441     dev->fill(0, 0, 512, 512, whitePixel);
442     QImage thumb1 = dev->createThumbnail(50, 50);
443     QRect exactBounds1 = dev->exactBounds();
444 
445     dev->fill(0, 0, 768, 768, blackPixel);
446     QImage thumb2 = dev->createThumbnail(50, 50);
447     QRect exactBounds2 = dev->exactBounds();
448 
449     dev->moveTo(10, 10);
450     QImage thumb3 = dev->createThumbnail(50, 50);
451     QRect exactBounds3 = dev->exactBounds();
452 
453     dev->crop(50, 50, 50, 50);
454     QImage thumb4 = dev->createThumbnail(50, 50);
455     QRect exactBounds4 = dev->exactBounds();
456 
457     QVERIFY(thumb1 != thumb2);
458     QVERIFY(thumb2 == thumb3); // Cache miss, but image is the same
459     QVERIFY(thumb3 != thumb4);
460     QVERIFY(thumb4 != thumb1);
461 
462     QCOMPARE(exactBounds1, QRect(0,0,512,512));
463     QCOMPARE(exactBounds2, QRect(0,0,768,768));
464     QCOMPARE(exactBounds3, QRect(10,10,768,768));
465     QCOMPARE(exactBounds4, QRect(50,50,50,50));
466 }
467 
testRegion()468 void KisPaintDeviceTest::testRegion()
469 {
470     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
471     KisPaintDeviceSP dev = new KisPaintDevice(cs);
472 
473     quint8* whitePixel = new quint8[cs->pixelSize()];
474     cs->fromQColor(Qt::white, whitePixel);
475 
476     dev->fill(0, 0, 10, 10, whitePixel);
477     dev->fill(70, 70, 10, 10, whitePixel);
478     dev->fill(129, 0, 10, 10, whitePixel);
479     dev->fill(0, 1030, 10, 10, whitePixel);
480 
481     QVector<QRect> referenceRegion;
482     referenceRegion += QRect(0,0,64,64);
483     referenceRegion += QRect(64,64,64,64);
484     referenceRegion += QRect(128,0,64,64);
485     referenceRegion += QRect(0,1024,64,64);
486 
487     QCOMPARE(dev->exactBounds(), QRect(0,0,139,1040));
488     QCOMPARE(dev->region(), KisRegion(referenceRegion));
489 }
490 
testPixel()491 void KisPaintDeviceTest::testPixel()
492 {
493     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
494     KisPaintDeviceSP dev = new KisPaintDevice(cs);
495 
496     QColor c = Qt::red;
497     quint8 opacity = 125;
498 
499     c.setAlpha(opacity);
500     dev->setPixel(5, 5, c);
501 
502     QColor c2;
503 
504     dev->pixel(5, 5, &c2);
505 
506     QVERIFY(c == c2);
507     QVERIFY(opacity == c2.alpha());
508 
509 }
510 
testPlanarReadWrite()511 void KisPaintDeviceTest::testPlanarReadWrite()
512 {
513     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
514     KisPaintDeviceSP dev = new KisPaintDevice(cs);
515 
516     quint8* pixel = new quint8[cs->pixelSize()];
517     cs->fromQColor(QColor(255, 200, 155, 100), pixel);
518     dev->fill(0, 0, 5000, 5000, pixel);
519     delete[] pixel;
520 
521     QColor c1;
522     dev->pixel(5, 5, &c1);
523 
524     QVector<quint8*> planes = dev->readPlanarBytes(500, 500, 100, 100);
525     QVector<quint8*> swappedPlanes;
526 
527     QCOMPARE((int)planes.size(), (int)dev->channelCount());
528 
529     for (int i = 0; i < 100*100; i++) {
530         // BGRA encoded
531         QVERIFY(planes.at(2)[i] == 255);
532         QVERIFY(planes.at(1)[i] == 200);
533         QVERIFY(planes.at(0)[i] == 155);
534         QVERIFY(planes.at(3)[i] == 100);
535     }
536 
537     for (uint i = 1; i < dev->channelCount() + 1; ++i) {
538         swappedPlanes.append(planes[dev->channelCount() - i]);
539     }
540 
541     dev->writePlanarBytes(swappedPlanes, 0, 0, 100, 100);
542 
543     dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar.png");
544 
545     dev->pixel(5, 5, &c1);
546 
547     QVERIFY(c1.red() == 200);
548     QVERIFY(c1.green() == 255);
549     QVERIFY(c1.blue() == 100);
550     QVERIFY(c1.alpha() == 155);
551 
552     dev->pixel(75, 50, &c1);
553 
554     QVERIFY(c1.red() == 200);
555     QVERIFY(c1.green() == 255);
556     QVERIFY(c1.blue() == 100);
557     QVERIFY(c1.alpha() == 155);
558 
559     // check if one of the planes is Null.
560     Q_ASSERT(planes.size() == 4);
561     delete [] planes[2];
562     planes[2] = 0;
563     dev->writePlanarBytes(planes, 0, 0, 100, 100);
564     dev->convertToQImage(0, 0, 0, 1000, 1000).save("planar_noR.png");
565 
566     dev->pixel(75, 50, &c1);
567 
568     QCOMPARE(c1.red(), 200);
569     QCOMPARE(c1.green(), 200);
570     QCOMPARE(c1.blue(), 155);
571     QCOMPARE(c1.alpha(), 100);
572 
573     QVector<quint8*>::iterator i;
574     for (i = planes.begin(); i != planes.end(); ++i)
575     {
576         delete [] *i;
577     }
578     swappedPlanes.clear();
579 }
580 
testBltPerformance()581 void KisPaintDeviceTest::testBltPerformance()
582 {
583     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa_transparent.png");
584     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
585     KisPaintDeviceSP fdev = new KisPaintDevice(cs);
586     fdev->convertFromQImage(image, 0);
587 
588     KisPaintDeviceSP dev = new KisPaintDevice(cs);
589     dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
590 
591     QTime t;
592     t.start();
593 
594     int x;
595 #ifdef LIMIT_LONG_TESTS
596     int steps = 10;
597 #else
598     int steps = 1000;
599 #endif
600     for (x = 0; x < steps; ++x) {
601         KisPainter gc(dev);
602         gc.bitBlt(QPoint(0, 0), fdev, image.rect());
603     }
604 
605     dbgKrita << x
606     << "blits"
607     << " done in "
608     << t.elapsed()
609     << "ms";
610 
611 
612 }
613 
testDeviceDuplication()614 void KisPaintDeviceTest::testDeviceDuplication()
615 {
616     QRect fillRect(0,0,64,64);
617     quint8 fillPixel[4]={255,255,255,255};
618     QRect clearRect(10,10,20,20);
619     QImage referenceImage;
620     QImage resultImage;
621 
622     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
623     KisPaintDeviceSP device = new KisPaintDevice(cs);
624 
625 //    dbgKrita<<"FILLING";
626     device->fill(fillRect.left(), fillRect.top(),
627                  fillRect.width(), fillRect.height(),fillPixel);
628     referenceImage = device->convertToQImage(0);
629 
630 
631     KisTransaction transaction1(device);
632 //    dbgKrita<<"CLEARING";
633     device->clear(clearRect);
634 
635     transaction1.revert();
636 
637     resultImage = device->convertToQImage(0);
638     QVERIFY(resultImage == referenceImage);
639 
640     KisPaintDeviceSP clone =  new KisPaintDevice(*device);
641 
642     KisTransaction transaction(clone);
643 //    dbgKrita<<"CLEARING";
644     clone->clear(clearRect);
645 
646     transaction.revert();
647 
648     resultImage = clone->convertToQImage(0);
649     QVERIFY(resultImage == referenceImage);
650 
651 }
652 
testTranslate()653 void KisPaintDeviceTest::testTranslate()
654 {
655     QRect fillRect(0,0,64,64);
656     quint8 fillPixel[4]={255,255,255,255};
657 
658     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
659     KisPaintDeviceSP device = new KisPaintDevice(cs);
660 
661     device->fill(fillRect.left(), fillRect.top(),
662                  fillRect.width(), fillRect.height(),fillPixel);
663 
664     device->setX(-10);
665     device->setY(10);
666     QCOMPARE(device->exactBounds(), QRect(-10,10,64,64));
667     QCOMPARE(device->extent(), QRect(-10,10,64,64));
668 }
669 
testOpacity()670 void KisPaintDeviceTest::testOpacity()
671 {
672     // blt a semi-transparent image on a white paint device
673 
674     QImage image(QString(FILES_DATA_DIR) + '/' + "hakonepa_transparent.png");
675     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
676     KisPaintDeviceSP fdev = new KisPaintDevice(cs);
677     fdev->convertFromQImage(image, 0);
678 
679     KisPaintDeviceSP dev = new KisPaintDevice(cs);
680     dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data());
681     KisPainter gc(dev);
682     gc.bitBlt(QPoint(0, 0), fdev, image.rect());
683 
684     QImage result = dev->convertToQImage(0, 0, 0, 640, 441);
685     QImage checkResult(QString(FILES_DATA_DIR) + '/' + "hakonepa_transparent_result.png");
686     QPoint errpoint;
687 
688     if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) {
689         checkResult.save("kis_paint_device_test_test_blt_fixed_opactiy_expected.png");
690         result.save("kis_paint_device_test_test_blt_fixed_opacity_result.png");
691         QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
692     }
693 }
694 
testExactBoundsWeirdNullAlphaCase()695 void KisPaintDeviceTest::testExactBoundsWeirdNullAlphaCase()
696 {
697     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
698     KisPaintDeviceSP dev = new KisPaintDevice(cs);
699 
700     QVERIFY(dev->exactBounds().isEmpty());
701 
702     dev->fill(QRect(10,10,10,10), KoColor(Qt::white, cs));
703 
704     QCOMPARE(dev->exactBounds(), QRect(10,10,10,10));
705 
706     const quint8 weirdPixelData[4] = {0,10,0,0};
707     KoColor weirdColor(weirdPixelData, cs);
708     dev->setPixel(6,6,weirdColor);
709 
710     // such weird pixels should not change our opinion about
711     // device's size
712     QCOMPARE(dev->exactBounds(), QRect(10,10,10,10));
713 }
714 
benchmarkExactBoundsNullDefaultPixel()715 void KisPaintDeviceTest::benchmarkExactBoundsNullDefaultPixel()
716 {
717     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
718     KisPaintDeviceSP dev = new KisPaintDevice(cs);
719 
720     QVERIFY(dev->exactBounds().isEmpty());
721 
722     QRect fillRect(60,60, 1930, 1930);
723 
724     dev->fill(fillRect, KoColor(Qt::white, cs));
725 
726     QRect measuredRect;
727 
728     QBENCHMARK {
729         // invalidate the cache
730         dev->setDirty();
731         measuredRect = dev->exactBounds();
732     }
733 
734     QCOMPARE(measuredRect, fillRect);
735 }
736 
testAmortizedExactBounds()737 void KisPaintDeviceTest::testAmortizedExactBounds()
738 {
739     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
740     KisPaintDeviceSP dev = new KisPaintDevice(cs);
741 
742     QVERIFY(dev->exactBounds().isEmpty());
743 
744     QRect fillRect(60,60, 833, 833);
745     QRect extent(0,0,896,896);
746 
747     dev->fill(fillRect, KoColor(Qt::white, cs));
748 
749     QEXPECT_FAIL("", "Expecting the extent, we somehow get the fillrect", Continue);
750     QCOMPARE(dev->exactBounds(), extent);
751     QCOMPARE(dev->extent(), extent);
752 
753     QCOMPARE(dev->exactBoundsAmortized(), fillRect);
754 
755     dev->setDirty();
756     QEXPECT_FAIL("", "Expecting the fillRect, we somehow get the extent", Continue);
757     QCOMPARE(dev->exactBoundsAmortized(), fillRect);
758 
759     dev->setDirty();
760     QCOMPARE(dev->exactBoundsAmortized(), extent);
761 
762     QTest::qSleep(1100);
763     QEXPECT_FAIL("", "Expecting the fillRect, we somehow get the extent", Continue);
764     QCOMPARE(dev->exactBoundsAmortized(), fillRect);
765 }
766 
testNonDefaultPixelArea()767 void KisPaintDeviceTest::testNonDefaultPixelArea()
768 {
769     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
770     KisPaintDeviceSP dev = new KisPaintDevice(cs);
771 
772     QVERIFY(dev->exactBounds().isEmpty());
773     QVERIFY(dev->nonDefaultPixelArea().isEmpty());
774 
775     KoColor defPixel(Qt::red, cs);
776     dev->setDefaultPixel(defPixel);
777 
778     QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
779     QVERIFY(dev->nonDefaultPixelArea().isEmpty());
780 
781     QRect fillRect(10,11,18,14);
782 
783     dev->fill(fillRect, KoColor(Qt::white, cs));
784 
785     QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
786     QCOMPARE(dev->nonDefaultPixelArea(), fillRect);
787 
788 
789     // non-default pixel variant should also handle weird pixels
790 
791     const quint8 weirdPixelData[4] = {0,10,0,0};
792     KoColor weirdColor(weirdPixelData, cs);
793     dev->setPixel(100,100,weirdColor);
794 
795     // such weird pixels should not change our opinion about
796     // device's size
797     QCOMPARE(dev->exactBounds(), KisDefaultBounds::infiniteRect);
798     QCOMPARE(dev->nonDefaultPixelArea(), fillRect | QRect(100,100,1,1));
799 }
800 
testExactBoundsNonTransparent()801 void KisPaintDeviceTest::testExactBoundsNonTransparent()
802 {
803     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
804     KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
805     KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);
806     KisPaintDeviceSP dev = layer->paintDevice();
807 
808     QVERIFY(dev);
809 
810     QRect imageRect(0,0,1000,1000);
811 
812     KoColor defPixel(Qt::red, cs);
813     dev->setDefaultPixel(defPixel);
814 
815     QCOMPARE(dev->exactBounds(), imageRect);
816     QVERIFY(dev->nonDefaultPixelArea().isEmpty());
817 
818     KoColor fillPixel(Qt::white, cs);
819 
820     dev->fill(imageRect, KoColor(Qt::white, cs));
821     QCOMPARE(dev->exactBounds(), imageRect);
822     QCOMPARE(dev->nonDefaultPixelArea(), imageRect);
823 
824     dev->fill(QRect(1000,0, 1, 1000), KoColor(Qt::white, cs));
825     QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1000));
826     QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1000));
827 
828     dev->fill(QRect(0,1000, 1000, 1), KoColor(Qt::white, cs));
829     QCOMPARE(dev->exactBounds(), QRect(0,0,1001,1001));
830     QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,0,1001,1001));
831 
832     dev->fill(QRect(0,-1, 1000, 1), KoColor(Qt::white, cs));
833     QCOMPARE(dev->exactBounds(), QRect(0,-1,1001,1002));
834     QCOMPARE(dev->nonDefaultPixelArea(), QRect(0,-1,1001,1002));
835 
836     dev->fill(QRect(-1,0, 1, 1000), KoColor(Qt::white, cs));
837     QCOMPARE(dev->exactBounds(), QRect(-1,-1,1002,1002));
838     QCOMPARE(dev->nonDefaultPixelArea(), QRect(-1,-1,1002,1002));
839 }
840 
createWrapAroundPaintDevice(const KoColorSpace * cs)841 KisPaintDeviceSP createWrapAroundPaintDevice(const KoColorSpace *cs)
842 {
843     struct TestingDefaultBounds : public KisDefaultBoundsBase {
844         QRect bounds() const override {
845             return QRect(0,0,20,20);
846         }
847         bool wrapAroundMode() const override {
848             return true;
849         }
850         int currentLevelOfDetail() const override {
851             return 0;
852         }
853         int currentTime() const override {
854             return 0;
855         }
856         bool externalFrameActive() const override {
857             return false;
858         }
859         void * sourceCookie() const override {
860             return 0;
861         }
862     };
863 
864     KisPaintDeviceSP dev = new KisPaintDevice(cs);
865 
866     KisDefaultBoundsBaseSP bounds = new TestingDefaultBounds();
867     dev->setDefaultBounds(bounds);
868 
869     return dev;
870 }
871 
checkReadWriteRoundTrip(KisPaintDeviceSP dev,const QRect & rc)872 void checkReadWriteRoundTrip(KisPaintDeviceSP dev,
873                              const QRect &rc)
874 {
875     KisPaintDeviceSP deviceCopy = new KisPaintDevice(*dev.data());
876 
877     int bufSize = rc.width() * rc.height() * dev->pixelSize();
878 
879     QScopedArrayPointer<quint8> buf1(new quint8[bufSize]);
880 
881     deviceCopy->readBytes(buf1.data(), rc);
882 
883     deviceCopy->clear();
884     QVERIFY(deviceCopy->extent().isEmpty());
885 
886 
887     QScopedArrayPointer<quint8> buf2(new quint8[bufSize]);
888     deviceCopy->writeBytes(buf1.data(), rc);
889     deviceCopy->readBytes(buf2.data(), rc);
890 
891     QVERIFY(!memcmp(buf1.data(), buf2.data(), bufSize));
892 }
893 
894 
testReadBytesWrapAround()895 void KisPaintDeviceTest::testReadBytesWrapAround()
896 {
897     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
898     KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
899 
900     KoColor c1(Qt::red, cs);
901     KoColor c2(Qt::green, cs);
902 
903     dev->setPixel(3, 3, c1);
904     dev->setPixel(18, 18, c2);
905 
906     const int pixelSize = dev->pixelSize();
907 
908     {
909         QRect readRect(10, 10, 20, 20);
910         QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
911                                               readRect.height() *
912                                               pixelSize]);
913         dev->readBytes(buf.data(), readRect);
914         //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final1.png");
915 
916         QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
917         QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
918 
919         QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
920         QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
921 
922         checkReadWriteRoundTrip(dev, readRect);
923     }
924 
925     {
926         // check weird case when the read rect is larger than wrap rect
927         QRect readRect(10, 10, 30, 30);
928         QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
929                                               readRect.height() *
930                                               pixelSize]);
931         dev->readBytes(buf.data(), readRect);
932         //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final2.png");
933 
934         QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
935         QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
936 
937         QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
938         QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
939 
940         QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
941         QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
942 
943         QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
944         QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
945 
946         QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
947         QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
948 
949         checkReadWriteRoundTrip(dev, readRect);
950     }
951 
952     {
953         // even more large
954         QRect readRect(10, 10, 40, 40);
955         QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
956                                               readRect.height() *
957                                               pixelSize]);
958         dev->readBytes(buf.data(), readRect);
959         //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final3.png");
960 
961         QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
962         QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
963 
964         QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
965         QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
966 
967         QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize));
968         QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize));
969 
970         QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
971         QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
972 
973         QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize));
974         QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize));
975 
976         QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize));
977         QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize));
978 
979         QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize));
980         QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize));
981 
982         QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize));
983         QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize));
984 
985         checkReadWriteRoundTrip(dev, readRect);
986     }
987 
988     {
989         // check if the wrap rect contains the read rect entirely
990         QRect readRect(1, 1, 10, 10);
991         QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
992                                               readRect.height() *
993                                               pixelSize]);
994         dev->readBytes(buf.data(), readRect);
995         //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final4.png");
996 
997         QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
998         QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
999 
1000         checkReadWriteRoundTrip(dev, readRect);
1001     }
1002 
1003     {
1004         // check if the wrap happens only on vertical side of the rect
1005         QRect readRect(1, 1, 29, 10);
1006         QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
1007                                               readRect.height() *
1008                                               pixelSize]);
1009         dev->readBytes(buf.data(), readRect);
1010         //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final5.png");
1011 
1012         QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
1013         QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
1014 
1015         QVERIFY(memcmp(buf.data() + (21 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
1016         QVERIFY(!memcmp(buf.data() + (22 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
1017 
1018         checkReadWriteRoundTrip(dev, readRect);
1019     }
1020 
1021     {
1022         // check if the wrap happens only on horizontal side of the rect
1023         QRect readRect(1, 1, 10, 29);
1024         QScopedArrayPointer<quint8> buf(new quint8[readRect.width() *
1025                                               readRect.height() *
1026                                               pixelSize]);
1027         dev->readBytes(buf.data(), readRect);
1028         //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final6.png");
1029 
1030         QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize));
1031         QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize));
1032 
1033         QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 21) * pixelSize, c1.data(), pixelSize));
1034         QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 22) * pixelSize, c1.data(), pixelSize));
1035 
1036         checkReadWriteRoundTrip(dev, readRect);
1037     }
1038 }
1039 
1040 #include "kis_random_accessor_ng.h"
1041 
testWrappedRandomAccessor()1042 void KisPaintDeviceTest::testWrappedRandomAccessor()
1043 {
1044     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1045     KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
1046 
1047     KoColor c1(Qt::red, cs);
1048     KoColor c2(Qt::green, cs);
1049 
1050     dev->setPixel(3, 3, c1);
1051     dev->setPixel(18, 18, c2);
1052 
1053     const int pixelSize = dev->pixelSize();
1054 
1055     int x;
1056     int y;
1057 
1058     KisRandomAccessorSP dstIt = dev->createRandomAccessorNG();
1059     x = 3;
1060     y = 3;
1061     dstIt->moveTo(x, y);
1062 
1063     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1064     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1065     QCOMPARE(dstIt->numContiguousRows(y), 17);
1066 
1067     x = 23;
1068     y = 23;
1069     dstIt->moveTo(x, y);
1070     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1071     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1072     QCOMPARE(dstIt->numContiguousRows(y), 17);
1073 
1074     x = 3;
1075     y = 23;
1076     dstIt->moveTo(x, y);
1077     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1078     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1079     QCOMPARE(dstIt->numContiguousRows(y), 17);
1080 
1081     x = 23;
1082     y = 3;
1083     dstIt->moveTo(x, y);
1084     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1085     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1086     QCOMPARE(dstIt->numContiguousRows(y), 17);
1087 
1088     x = -17;
1089     y = 3;
1090     dstIt->moveTo(x, y);
1091     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1092     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1093     QCOMPARE(dstIt->numContiguousRows(y), 17);
1094 
1095     x = 3;
1096     y = -17;
1097     dstIt->moveTo(x, y);
1098     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1099     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1100     QCOMPARE(dstIt->numContiguousRows(y), 17);
1101 
1102     x = -17;
1103     y = -17;
1104     dstIt->moveTo(x, y);
1105     QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
1106     QCOMPARE(dstIt->numContiguousColumns(x), 17);
1107     QCOMPARE(dstIt->numContiguousRows(y), 17);
1108 }
1109 
1110 #include "kis_iterator_ng.h"
1111 
nextRowGeneral(KisHLineIteratorSP it,int y,const QRect & rc)1112 static bool nextRowGeneral(KisHLineIteratorSP it, int y, const QRect &rc) {
1113     it->nextRow();
1114     return y < rc.height();
1115 }
1116 
nextRowGeneral(KisVLineIteratorSP it,int y,const QRect & rc)1117 static bool nextRowGeneral(KisVLineIteratorSP it, int y, const QRect &rc) {
1118     it->nextColumn();
1119     return y < rc.width();
1120 }
1121 
1122 template <class T>
checkXY(const QPoint & pt,const QPoint & realPt)1123 bool checkXY(const QPoint &pt, const QPoint &realPt) {
1124     Q_UNUSED(pt);
1125     Q_UNUSED(realPt);
1126     return false;
1127 }
1128 
1129 template <>
checkXY(const QPoint & pt,const QPoint & realPt)1130 bool checkXY<KisHLineIteratorSP>(const QPoint &pt, const QPoint &realPt) {
1131     return pt == realPt;
1132 }
1133 
1134 template <>
checkXY(const QPoint & pt,const QPoint & realPt)1135 bool checkXY<KisVLineIteratorSP>(const QPoint &pt, const QPoint &realPt) {
1136     return pt.x() == realPt.y() && pt.y() == realPt.x();
1137 }
1138 #include <kis_wrapped_rect.h>
1139 template <class T>
checkConseqPixels(int value,const QPoint & pt,const KisWrappedRect & wrappedRect)1140 bool checkConseqPixels(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
1141     Q_UNUSED(value);
1142     Q_UNUSED(pt);
1143     Q_UNUSED(wrappedRect);
1144     return false;
1145 }
1146 
1147 template <>
checkConseqPixels(int value,const QPoint & pt,const KisWrappedRect & wrappedRect)1148 bool checkConseqPixels<KisHLineIteratorSP>(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
1149     int x = KisWrappedRect::xToWrappedX(pt.x(), wrappedRect.wrapRect());
1150     int borderX = wrappedRect.originalRect().x() + wrappedRect.wrapRect().width();
1151     int conseq = x >= borderX ? wrappedRect.wrapRect().right() - x + 1 : borderX - x;
1152     conseq = qMin(conseq, wrappedRect.originalRect().right() - pt.x() + 1);
1153 
1154     return value == conseq;
1155 }
1156 
1157 template <>
checkConseqPixels(int value,const QPoint & pt,const KisWrappedRect & wrappedRect)1158 bool checkConseqPixels<KisVLineIteratorSP>(int value, const QPoint &pt, const KisWrappedRect &wrappedRect) {
1159     Q_UNUSED(pt);
1160     Q_UNUSED(wrappedRect);
1161     return value == 1;
1162 }
1163 
1164 template <class IteratorSP>
createIterator(KisPaintDeviceSP dev,const QRect & rc)1165 IteratorSP createIterator(KisPaintDeviceSP dev, const QRect &rc) {
1166     Q_UNUSED(dev);
1167     Q_UNUSED(rc);
1168     return 0;
1169 }
1170 
1171 template <>
createIterator(KisPaintDeviceSP dev,const QRect & rc)1172 KisHLineIteratorSP createIterator(KisPaintDeviceSP dev,
1173                                   const QRect &rc) {
1174     return dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width());
1175 }
1176 
1177 template <>
createIterator(KisPaintDeviceSP dev,const QRect & rc)1178 KisVLineIteratorSP createIterator(KisPaintDeviceSP dev,
1179                                   const QRect &rc) {
1180     return dev->createVLineIteratorNG(rc.x(), rc.y(), rc.height());
1181 }
1182 
1183 template <class IteratorSP>
testWrappedLineIterator(QString testName,const QRect & rect)1184 void testWrappedLineIterator(QString testName, const QRect &rect)
1185 {
1186     testName = QString("%1_%2_%3_%4_%5")
1187         .arg(testName)
1188         .arg(rect.x())
1189         .arg(rect.y())
1190         .arg(rect.width())
1191         .arg(rect.height());
1192 
1193     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1194     KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
1195 
1196     // test rect fits the wrap rect in both dimensions
1197     IteratorSP it = createIterator<IteratorSP>(dev, rect);
1198 
1199     int y = 0;
1200     do {
1201         int x = 0;
1202         do {
1203             quint8 *data = it->rawData();
1204 
1205             data[0] = 10 * x;
1206             data[1] = 10 * y;
1207             data[2] = 0;
1208             data[3] = 255;
1209 
1210             x++;
1211         } while (it->nextPixel());
1212     } while (nextRowGeneral(it, ++y, rect));
1213 
1214     QRect rc = dev->defaultBounds()->bounds() | dev->exactBounds();
1215     QImage result = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
1216 
1217     QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators", testName));
1218 }
1219 
1220 template <class IteratorSP>
testWrappedLineIterator(const QString & testName)1221 void testWrappedLineIterator(const QString &testName)
1222 {
1223     testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,20,20));
1224     testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,10,20));
1225     testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,20,10));
1226     testWrappedLineIterator<IteratorSP>(testName, QRect(10,10,10,10));
1227     testWrappedLineIterator<IteratorSP>(testName, QRect(0,0,20,20));
1228 }
1229 
testWrappedHLineIterator()1230 void KisPaintDeviceTest::testWrappedHLineIterator()
1231 {
1232     testWrappedLineIterator<KisHLineIteratorSP>("hline_iterator");
1233 }
1234 
testWrappedVLineIterator()1235 void KisPaintDeviceTest::testWrappedVLineIterator()
1236 {
1237     testWrappedLineIterator<KisVLineIteratorSP>("vline_iterator");
1238 }
1239 
1240 template <class IteratorSP>
testWrappedLineIteratorReadMoreThanBounds(QString testName)1241 void testWrappedLineIteratorReadMoreThanBounds(QString testName)
1242 {
1243     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1244     KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
1245     KisPaintDeviceSP dst = new KisPaintDevice(cs);
1246 
1247     // fill device with a gradient
1248     QRect bounds = dev->defaultBounds()->bounds();
1249     for (int y = bounds.y(); y < bounds.y() + bounds.height(); y++) {
1250         for (int x = bounds.x(); x < bounds.x() + bounds.width(); x++) {
1251             QColor c((10 * x) % 255, (10 * y) % 255, 0, 255);
1252             dev->setPixel(x, y, c);
1253         }
1254     }
1255 
1256     // test rect doesn't fit the wrap rect in both dimensions
1257     const QRect &rect(bounds.adjusted(-6,-6,8,8));
1258     KisRandomAccessorSP dstIt = dst->createRandomAccessorNG();
1259     IteratorSP it = createIterator<IteratorSP>(dev, rect);
1260 
1261     for (int y = rect.y(); y < rect.y() + rect.height(); y++) {
1262         for (int x = rect.x(); x < rect.x() + rect.width(); x++) {
1263             quint8 *data = it->rawData();
1264 
1265             QVERIFY(checkConseqPixels<IteratorSP>(it->nConseqPixels(), QPoint(x, y), KisWrappedRect(rect, bounds)));
1266 
1267             dstIt->moveTo(x, y);
1268             memcpy(dstIt->rawData(), data, cs->pixelSize());
1269 
1270             QVERIFY(checkXY<IteratorSP>(QPoint(it->x(), it->y()), QPoint(x,y)));
1271 
1272             bool stepDone = it->nextPixel();
1273             QCOMPARE(stepDone, x < rect.x() + rect.width() - 1);
1274         }
1275         if (!nextRowGeneral(it, y, rect)) break;
1276     }
1277 
1278     testName = QString("%1_%2_%3_%4_%5")
1279         .arg(testName)
1280         .arg(rect.x())
1281         .arg(rect.y())
1282         .arg(rect.width())
1283         .arg(rect.height());
1284 
1285     QRect rc = rect;
1286     QImage result = dst->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
1287     QImage ref = dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height());
1288 
1289     QVERIFY(TestUtil::checkQImage(result, "paint_device_test", "wrapped_iterators_huge", testName, 1));
1290 }
1291 
testWrappedHLineIteratorReadMoreThanBounds()1292 void KisPaintDeviceTest::testWrappedHLineIteratorReadMoreThanBounds()
1293 {
1294     testWrappedLineIteratorReadMoreThanBounds<KisHLineIteratorSP>("hline_iterator");
1295 }
1296 
testWrappedVLineIteratorReadMoreThanBounds()1297 void KisPaintDeviceTest::testWrappedVLineIteratorReadMoreThanBounds()
1298 {
1299     testWrappedLineIteratorReadMoreThanBounds<KisVLineIteratorSP>("vline_iterator");
1300 }
1301 
testMoveWrapAround()1302 void KisPaintDeviceTest::testMoveWrapAround()
1303 {
1304     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1305     KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs);
1306 
1307     KoColor c1(Qt::red, cs);
1308     KoColor c2(Qt::green, cs);
1309 
1310     dev->setPixel(3, 3, c1);
1311     dev->setPixel(18, 18, c2);
1312 
1313     // QRect rc = dev->defaultBounds()->bounds();
1314 
1315     //dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move0.png");
1316     QCOMPARE(dev->exactBounds(), QRect(3,3,16,16));
1317     dev->moveTo(QPoint(10,10));
1318     QCOMPARE(dev->exactBounds(), QRect(8,8,6,6));
1319     //dev->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height()).save("move1.png");
1320 
1321 }
1322 
1323 #include "kis_lock_free_cache.h"
1324 
1325 #define NUM_TYPES 3
1326 
1327 // high-concurrency
1328 #define NUM_CYCLES 500000
1329 #define NUM_THREADS 4
1330 
1331 
1332 struct TestingCache : KisLockFreeCache<int> {
calculateNewValueTestingCache1333     int calculateNewValue() const override {
1334         return m_realValue;
1335     }
1336 
1337     QAtomicInt m_realValue;
1338 };
1339 
1340 class CacheStressJob : public QRunnable
1341 {
1342 public:
CacheStressJob(TestingCache & cache)1343     CacheStressJob(TestingCache &cache)
1344         : m_cache(cache),
1345           m_oldValue(0)
1346     {
1347     }
1348 
run()1349     void run() override {
1350         for(qint32 i = 0; i < NUM_CYCLES; i++) {
1351             qint32 type = i % NUM_TYPES;
1352 
1353             switch(type) {
1354             case 0:
1355                 m_cache.m_realValue.ref();
1356                 m_oldValue = m_cache.m_realValue;
1357                 m_cache.invalidate();
1358                 break;
1359             case 1:
1360             {
1361                 int newValue = m_cache.getValue();
1362                 KIS_ASSERT(newValue >= m_oldValue);
1363                 Q_UNUSED(newValue);
1364             }
1365                 break;
1366             case 3:
1367                 QTest::qSleep(3);
1368                 break;
1369             }
1370         }
1371     }
1372 
1373 private:
1374     TestingCache &m_cache;
1375     int m_oldValue;
1376 };
1377 
testCacheState()1378 void KisPaintDeviceTest::testCacheState()
1379 {
1380     TestingCache cache;
1381 
1382     QList<CacheStressJob*> jobsList;
1383     CacheStressJob *job;
1384 
1385     for(qint32 i = 0; i < NUM_THREADS; i++) {
1386         //job = new CacheStressJob(value, cacheValue, cacheState);
1387         job = new CacheStressJob(cache);
1388         job->setAutoDelete(true);
1389         jobsList.append(job);
1390     }
1391 
1392     QThreadPool pool;
1393     pool.setMaxThreadCount(NUM_THREADS);
1394 
1395     Q_FOREACH (job, jobsList) {
1396         pool.start(job);
1397     }
1398 
1399     pool.waitForDone();
1400 }
1401 
1402 struct TestingLodDefaultBounds : public KisDefaultBoundsBase {
TestingLodDefaultBoundsTestingLodDefaultBounds1403     TestingLodDefaultBounds(const QRect &bounds = QRect(0,0,100,100))
1404         : m_lod(0), m_bounds(bounds) {}
1405 
boundsTestingLodDefaultBounds1406     QRect bounds() const override {
1407         return m_bounds;
1408     }
wrapAroundModeTestingLodDefaultBounds1409     bool wrapAroundMode() const override {
1410         return false;
1411     }
1412 
currentLevelOfDetailTestingLodDefaultBounds1413     int currentLevelOfDetail() const override {
1414         return m_lod;
1415     }
1416 
currentTimeTestingLodDefaultBounds1417     int currentTime() const override {
1418         return 0;
1419     }
externalFrameActiveTestingLodDefaultBounds1420     bool externalFrameActive() const override {
1421         return false;
1422     }
1423 
testingSetLevelOfDetailTestingLodDefaultBounds1424     void testingSetLevelOfDetail(int lod) {
1425         m_lod = lod;
1426     }
sourceCookieTestingLodDefaultBounds1427     void * sourceCookie() const override {
1428         return 0;
1429     }
1430 
1431 private:
1432     int m_lod;
1433     QRect m_bounds;
1434 };
1435 
fillGradientDevice(KisPaintDeviceSP dev,const QRect & rect,bool flat=false)1436 void fillGradientDevice(KisPaintDeviceSP dev, const QRect &rect, bool flat = false)
1437 {
1438     if (flat) {
1439         dev->fill(rect, KoColor(Qt::red, dev->colorSpace()));
1440     } else {
1441         // fill device with a gradient
1442         KisSequentialIterator it(dev, rect);
1443         while (it.nextPixel()) {
1444             QColor c((10 * it.x()) & 0xFF, (10 * it.y()) & 0xFF, 0, 255);
1445             KoColor color(c, dev->colorSpace());
1446             memcpy(it.rawData(), color.data(), dev->pixelSize());
1447         }
1448     }
1449 }
1450 #include "kis_lod_transform.h"
testLodTransform()1451 void KisPaintDeviceTest::testLodTransform()
1452 {
1453     const int lod = 2; // round to 4
1454     KisLodTransform t(lod);
1455 
1456     QRect rc1(-16, -16, 8, 8);
1457     QRect rc2(-16, -16, 7, 7);
1458     QRect rc3(-15, -15, 7, 7);
1459 
1460     QCOMPARE(t.alignedRect(rc1, lod), rc1);
1461     QCOMPARE(t.alignedRect(rc2, lod), rc1);
1462     QCOMPARE(t.alignedRect(rc3, lod), rc1);
1463 }
1464 
1465 #include "krita_utils.h"
syncLodCache(KisPaintDeviceSP dev,int levelOfDetail)1466 void syncLodCache(KisPaintDeviceSP dev, int levelOfDetail)
1467 {
1468     KisPaintDevice::LodDataStruct* s = dev->createLodDataStruct(levelOfDetail);
1469 
1470     KisRegion region = dev->regionForLodSyncing();
1471     Q_FOREACH(QRect rect2, KritaUtils::splitRegionIntoPatches(region, KritaUtils::optimalPatchSize())) {
1472         dev->updateLodDataStruct(s, rect2);
1473     }
1474 
1475     dev->uploadLodDataStruct(s);
1476 }
1477 
testLodDevice()1478 void KisPaintDeviceTest::testLodDevice()
1479 {
1480     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1481     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1482 
1483     TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds();
1484     dev->setDefaultBounds(bounds);
1485 
1486     // fill device with a gradient
1487     // QRect rect = dev->defaultBounds()->bounds();
1488     // fillGradientDevice(dev, rect);
1489     fillGradientDevice(dev, QRect(50,50,30,30));
1490 
1491     QCOMPARE(dev->exactBounds(), QRect(50,50,30,30));
1492 
1493     QImage result;
1494 
1495     qDebug() << ppVar(dev->exactBounds());
1496     result = dev->convertToQImage(0,0,0,100,100);
1497     /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
1498                                   "lod", "initial"));
1499 
1500     bounds->testingSetLevelOfDetail(1);
1501     syncLodCache(dev, 1);
1502     QCOMPARE(dev->exactBounds(), QRect(25,25,15,15));
1503 
1504     qDebug() << ppVar(dev->exactBounds());
1505     result = dev->convertToQImage(0,0,0,100,100);
1506     /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
1507                                   "lod", "lod1"));
1508 
1509     bounds->testingSetLevelOfDetail(2);
1510     QCOMPARE(dev->exactBounds(), QRect(25,25,15,15));
1511 
1512     qDebug() << ppVar(dev->exactBounds());
1513     result = dev->convertToQImage(0,0,0,100,100);
1514     /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
1515                                   "lod", "lod1"));
1516 
1517     syncLodCache(dev, 2);
1518     QCOMPARE(dev->exactBounds(), QRect(12,12,8,8));
1519 
1520     qDebug() << ppVar(dev->exactBounds());
1521     result = dev->convertToQImage(0,0,0,100,100);
1522     /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
1523                                   "lod", "lod2"));
1524 
1525     bounds->testingSetLevelOfDetail(0);
1526 
1527     dev->setX(20);
1528     dev->setY(10);
1529 
1530     bounds->testingSetLevelOfDetail(1);
1531     syncLodCache(dev, 1);
1532 
1533     QCOMPARE(dev->exactBounds(), QRect(35,30,15,15));
1534 
1535 
1536     qDebug() << ppVar(dev->exactBounds()) << ppVar(dev->x()) << ppVar(dev->y());
1537     result = dev->convertToQImage(0,0,0,100,100);
1538     /*QVERIFY*/(TestUtil::checkQImage(result, "paint_device_test",
1539                                   "lod", "lod1-offset-6-14"));
1540 }
1541 
benchmarkLod1Generation()1542 void KisPaintDeviceTest::benchmarkLod1Generation()
1543 {
1544     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1545     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1546 
1547     TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,6000,4000));
1548     dev->setDefaultBounds(bounds);
1549 
1550     // fill device with a gradient
1551     QRect rect = dev->defaultBounds()->bounds();
1552     fillGradientDevice(dev, rect, true);
1553 
1554     QBENCHMARK {
1555         bounds->testingSetLevelOfDetail(1);
1556         syncLodCache(dev, 1);
1557     }
1558 }
1559 
benchmarkLod2Generation()1560 void KisPaintDeviceTest::benchmarkLod2Generation()
1561 {
1562     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1563     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1564 
1565     TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,6000,4000));
1566     dev->setDefaultBounds(bounds);
1567 
1568     // fill device with a gradient
1569     QRect rect = dev->defaultBounds()->bounds();
1570     fillGradientDevice(dev, rect, true);
1571 
1572     QBENCHMARK {
1573         bounds->testingSetLevelOfDetail(2);
1574         syncLodCache(dev, 2);
1575     }
1576 }
1577 
benchmarkLod3Generation()1578 void KisPaintDeviceTest::benchmarkLod3Generation()
1579 {
1580     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1581     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1582 
1583     TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,3000,2000));
1584     dev->setDefaultBounds(bounds);
1585 
1586     // fill device with a gradient
1587     QRect rect = dev->defaultBounds()->bounds();
1588     fillGradientDevice(dev, rect, true);
1589 
1590     QBENCHMARK {
1591         bounds->testingSetLevelOfDetail(3);
1592         syncLodCache(dev, 3);
1593     }
1594 }
1595 
benchmarkLod4Generation()1596 void KisPaintDeviceTest::benchmarkLod4Generation()
1597 {
1598     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1599     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1600 
1601     TestingLodDefaultBounds *bounds = new TestingLodDefaultBounds(QRect(0,0,3000,2000));
1602     dev->setDefaultBounds(bounds);
1603 
1604     // fill device with a gradient
1605     QRect rect = dev->defaultBounds()->bounds();
1606     fillGradientDevice(dev, rect, true);
1607 
1608     QBENCHMARK {
1609         bounds->testingSetLevelOfDetail(4);
1610         syncLodCache(dev, 4);
1611     }
1612 }
1613 
1614 #include "kis_keyframe_channel.h"
1615 #include "kis_raster_keyframe_channel.h"
1616 #include "kis_paint_device_frames_interface.h"
1617 #include "testing_timed_default_bounds.h"
1618 
testFramesLeaking()1619 void KisPaintDeviceTest::testFramesLeaking()
1620 {
1621     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1622     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1623 
1624     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
1625     dev->setDefaultBounds(bounds);
1626 
1627     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
1628     QVERIFY(channel);
1629 
1630     KisPaintDeviceFramesInterface *i = dev->framesInterface();
1631     QVERIFY(i);
1632 
1633     QCOMPARE(i->frames().size(), 1);
1634 
1635     KisPaintDeviceFramesInterface::TestingDataObjects o;
1636 
1637     // Itinial state: one frame, m_data is kept separate
1638     o = i->testingGetDataObjects();
1639     QVERIFY(o.m_data);
1640     QVERIFY(!o.m_lodData);
1641     QVERIFY(!o.m_externalFrameData);
1642     QCOMPARE(o.m_frames.size(), 1);
1643     QVERIFY(o.m_currentData == o.m_frames[0]);
1644 
1645     // add keyframe at position 10
1646     channel->addKeyframe(10);
1647 
1648     // two frames, m_data has a default empty value
1649     o = i->testingGetDataObjects();
1650     QVERIFY(o.m_data);
1651     QVERIFY(!o.m_lodData);
1652     QVERIFY(!o.m_externalFrameData);
1653     QCOMPARE(o.m_frames.size(), 2);
1654     QVERIFY(o.m_currentData == o.m_frames[0]);
1655 
1656     // add keyframe at position 20
1657     channel->addKeyframe(20);
1658 
1659     // three frames, m_data is default, current frame is 0
1660     o = i->testingGetDataObjects();
1661     QVERIFY(o.m_data);
1662     QVERIFY(!o.m_lodData);
1663     QVERIFY(!o.m_externalFrameData);
1664     QCOMPARE(o.m_frames.size(), 3);
1665     QVERIFY(o.m_currentData == o.m_frames[0]);
1666 
1667     // switch to frame 10
1668     bounds->testingSetTime(10);
1669 
1670     // three frames, m_data is default, current frame is 10
1671     o = i->testingGetDataObjects();
1672     QVERIFY(o.m_data);
1673     QVERIFY(!o.m_lodData);
1674     QVERIFY(!o.m_externalFrameData);
1675     QVERIFY(o.m_currentData == o.m_frames[1]);
1676     QCOMPARE(o.m_frames.size(), 3);
1677 
1678     // switch to frame 20
1679     bounds->testingSetTime(20);
1680 
1681     // three frames, m_data is default, current frame is 20
1682     o = i->testingGetDataObjects();
1683     QVERIFY(o.m_data);
1684     QVERIFY(!o.m_lodData);
1685     QVERIFY(!o.m_externalFrameData);
1686     QVERIFY(o.m_currentData == o.m_frames[2]);
1687     QCOMPARE(o.m_frames.size(), 3);
1688 
1689     // switch to frame 15
1690     bounds->testingSetTime(15);
1691 
1692     // three frames, m_data is default, current frame is 10
1693     o = i->testingGetDataObjects();
1694     QVERIFY(o.m_data);
1695     QVERIFY(!o.m_lodData);
1696     QVERIFY(!o.m_externalFrameData);
1697     QVERIFY(o.m_currentData == o.m_frames[1]);
1698     QCOMPARE(o.m_frames.size(), 3);
1699 
1700     KisKeyframeSP key;
1701 
1702     // deletion of frame 0 is forbidden
1703     key = channel->keyframeAt(0);
1704     QVERIFY(key);
1705     QVERIFY(channel->deleteKeyframe(key));
1706 
1707     // delete keyframe at position 11
1708     key = channel->activeKeyframeAt(11);
1709     QVERIFY(key);
1710     QCOMPARE(key->time(), 10);
1711     QVERIFY(channel->deleteKeyframe(key));
1712 
1713     // two frames, m_data is default, current frame is 0
1714     o = i->testingGetDataObjects();
1715     QVERIFY(o.m_data);
1716     QVERIFY(!o.m_lodData);
1717     QVERIFY(!o.m_externalFrameData);
1718     //QVERIFY(o.m_currentData == o.m_frames[0]);
1719     QCOMPARE(o.m_frames.size(), 2);
1720 
1721     // deletion of frame 0 is forbidden
1722     key = channel->activeKeyframeAt(11);
1723     QVERIFY(key);
1724     QCOMPARE(key->time(), 0);
1725     QVERIFY(channel->deleteKeyframe(key));
1726 
1727     // nothing changed
1728     o = i->testingGetDataObjects();
1729     QVERIFY(o.m_data);
1730     QVERIFY(!o.m_lodData);
1731     QVERIFY(!o.m_externalFrameData);
1732     //QVERIFY(o.m_currentData == o.m_frames[0]);
1733     QCOMPARE(o.m_frames.size(), 2);
1734 
1735     // delete keyframe at position 20
1736     key = channel->activeKeyframeAt(20);
1737     QVERIFY(key);
1738     QCOMPARE(key->time(), 20);
1739     QVERIFY(channel->deleteKeyframe(key));
1740 
1741     // one keyframe is left at position 0, m_data is default
1742     o = i->testingGetDataObjects();
1743     QVERIFY(o.m_data);
1744     QVERIFY(!o.m_lodData);
1745     QVERIFY(!o.m_externalFrameData);
1746     //QVERIFY(o.m_currentData == o.m_frames[0]);
1747     QCOMPARE(o.m_frames.size(), 1);
1748 
1749     // ensure all the objects in the list of all objects are unique
1750     QList<KisPaintDeviceData*> allObjects = i->testingGetDataObjectsList();
1751     QSet<KisPaintDeviceData*> uniqueObjects;
1752     Q_FOREACH (KisPaintDeviceData *obj, allObjects) {
1753         if (!obj) continue;
1754         QVERIFY(!uniqueObjects.contains(obj));
1755         uniqueObjects.insert(obj);
1756     }
1757 }
1758 
testFramesUndoRedo()1759 void KisPaintDeviceTest::testFramesUndoRedo()
1760 {
1761     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1762     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1763 
1764     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
1765     dev->setDefaultBounds(bounds);
1766 
1767     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
1768     QVERIFY(channel);
1769 
1770     KisPaintDeviceFramesInterface *i = dev->framesInterface();
1771     QVERIFY(i);
1772 
1773     QCOMPARE(i->frames().size(), 1);
1774 
1775     KisPaintDeviceFramesInterface::TestingDataObjects o;
1776 
1777     // Itinial state: one frame, m_data shared
1778     o = i->testingGetDataObjects();
1779     QVERIFY(o.m_data); // default m_data should always be present
1780     QVERIFY(!o.m_lodData);
1781     QVERIFY(!o.m_externalFrameData);
1782     QCOMPARE(o.m_frames.size(), 1);
1783     QVERIFY(o.m_currentData == o.m_frames[0]);
1784 
1785     // add a keyframe
1786 
1787     KUndo2Command cmdAdd;
1788     int frameId = -1;
1789     const int time = 1;
1790 
1791     channel->addKeyframe(time, &cmdAdd);
1792     frameId = channel->frameIdAt(time);
1793     //int frameId = i->createFrame(false, 0, QPoint(), &cmdAdd);
1794 
1795     QCOMPARE(frameId, 1);
1796 
1797     o = i->testingGetDataObjects();
1798     QVERIFY(o.m_data); // default m_data should always be present
1799     QVERIFY(!o.m_lodData);
1800     QVERIFY(!o.m_externalFrameData);
1801     QCOMPARE(o.m_frames.size(), 2);
1802     QVERIFY(o.m_currentData == o.m_frames[0]);
1803 
1804     cmdAdd.undo();
1805 
1806     o = i->testingGetDataObjects();
1807     QVERIFY(o.m_data); // default m_data should always be present
1808     QVERIFY(!o.m_lodData);
1809     QVERIFY(!o.m_externalFrameData);
1810     QCOMPARE(o.m_frames.size(), 1);
1811     QVERIFY(o.m_currentData == o.m_frames[0]);
1812 
1813     cmdAdd.redo();
1814 
1815     o = i->testingGetDataObjects();
1816     QVERIFY(o.m_data); // default m_data should always be present
1817     QVERIFY(!o.m_lodData);
1818     QVERIFY(!o.m_externalFrameData);
1819     QCOMPARE(o.m_frames.size(), 2);
1820     QVERIFY(o.m_currentData == o.m_frames[0]);
1821 
1822 
1823     KUndo2Command cmdRemove;
1824     KisKeyframeSP keyframe = channel->keyframeAt(time);
1825     QVERIFY(keyframe);
1826 
1827     channel->deleteKeyframe(keyframe, &cmdRemove);
1828 
1829     //i->deleteFrame(1, &cmdRemove);
1830 
1831     o = i->testingGetDataObjects();
1832     QVERIFY(o.m_data); // default m_data should always be present
1833     QVERIFY(!o.m_lodData);
1834     QVERIFY(!o.m_externalFrameData);
1835     QCOMPARE(o.m_frames.size(), 1);
1836     QVERIFY(o.m_currentData == o.m_frames[0]);
1837 
1838     cmdRemove.undo();
1839 
1840     o = i->testingGetDataObjects();
1841     QVERIFY(o.m_data); // default m_data should always be present
1842     QVERIFY(!o.m_lodData);
1843     QVERIFY(!o.m_externalFrameData);
1844     QCOMPARE(o.m_frames.size(), 2);
1845     QVERIFY(o.m_currentData == o.m_frames[0]);
1846 
1847     cmdRemove.redo();
1848 
1849     o = i->testingGetDataObjects();
1850     QVERIFY(o.m_data); // default m_data should always be present
1851     QVERIFY(!o.m_lodData);
1852     QVERIFY(!o.m_externalFrameData);
1853     QCOMPARE(o.m_frames.size(), 1);
1854     QVERIFY(o.m_currentData == o.m_frames[0]);
1855 }
1856 
testFramesUndoRedoWithChannel()1857 void KisPaintDeviceTest::testFramesUndoRedoWithChannel()
1858 {
1859     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
1860     KisPaintDeviceSP dev = new KisPaintDevice(cs);
1861 
1862     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
1863     dev->setDefaultBounds(bounds);
1864 
1865     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
1866     QVERIFY(channel);
1867 
1868     KisPaintDeviceFramesInterface *i = dev->framesInterface();
1869     QVERIFY(i);
1870 
1871     QCOMPARE(i->frames().size(), 1);
1872 
1873     KisPaintDeviceFramesInterface::TestingDataObjects o;
1874 
1875     // Itinial state: one frame, m_data shared
1876     o = i->testingGetDataObjects();
1877     QVERIFY(o.m_data); // default m_data should always be present
1878     QVERIFY(!o.m_lodData);
1879     QVERIFY(!o.m_externalFrameData);
1880     QCOMPARE(o.m_frames.size(), 1);
1881     QVERIFY(o.m_currentData == o.m_frames[0]);
1882 
1883 
1884     // add a keyframe
1885 
1886     KUndo2Command cmdAdd;
1887 
1888     KisKeyframeSP frame = channel->addKeyframe(10, &cmdAdd);
1889 
1890     QVERIFY(channel->keyframeAt(10));
1891 
1892     o = i->testingGetDataObjects();
1893     QVERIFY(o.m_data); // default m_data should always be present
1894     QVERIFY(!o.m_lodData);
1895     QVERIFY(!o.m_externalFrameData);
1896     QCOMPARE(o.m_frames.size(), 2);
1897     QVERIFY(o.m_currentData == o.m_frames[0]);
1898 
1899     cmdAdd.undo();
1900 
1901     QVERIFY(!channel->keyframeAt(10));
1902 
1903     o = i->testingGetDataObjects();
1904     QVERIFY(o.m_data); // default m_data should always be present
1905     QVERIFY(!o.m_lodData);
1906     QVERIFY(!o.m_externalFrameData);
1907     QCOMPARE(o.m_frames.size(), 1);
1908     QVERIFY(o.m_currentData == o.m_frames[0]);
1909 
1910     cmdAdd.redo();
1911 
1912     QVERIFY(channel->keyframeAt(10));
1913 
1914     o = i->testingGetDataObjects();
1915     QVERIFY(o.m_data); // default m_data should always be present
1916     QVERIFY(!o.m_lodData);
1917     QVERIFY(!o.m_externalFrameData);
1918     QCOMPARE(o.m_frames.size(), 2);
1919     QVERIFY(o.m_currentData == o.m_frames[0]);
1920 
1921 
1922     KUndo2Command cmdRemove;
1923     channel->deleteKeyframe(frame, &cmdRemove);
1924 
1925     QVERIFY(!channel->keyframeAt(10));
1926 
1927     o = i->testingGetDataObjects();
1928     QVERIFY(o.m_data); // default m_data should always be present
1929     QVERIFY(!o.m_lodData);
1930     QVERIFY(!o.m_externalFrameData);
1931     QCOMPARE(o.m_frames.size(), 1);
1932     QVERIFY(o.m_currentData == o.m_frames[0]);
1933 
1934     cmdRemove.undo();
1935 
1936     QVERIFY(channel->keyframeAt(10));
1937 
1938     o = i->testingGetDataObjects();
1939     QVERIFY(o.m_data); // default m_data should always be present
1940     QVERIFY(!o.m_lodData);
1941     QVERIFY(!o.m_externalFrameData);
1942     QCOMPARE(o.m_frames.size(), 2);
1943     QVERIFY(o.m_currentData == o.m_frames[0]);
1944 
1945     cmdRemove.redo();
1946 
1947     QVERIFY(!channel->keyframeAt(10));
1948 
1949     o = i->testingGetDataObjects();
1950     QVERIFY(o.m_data); // default m_data should always be present
1951     QVERIFY(!o.m_lodData);
1952     QVERIFY(!o.m_externalFrameData);
1953     QCOMPARE(o.m_frames.size(), 1);
1954     QVERIFY(o.m_currentData == o.m_frames[0]);
1955 
1956     cmdRemove.undo();
1957 
1958     QVERIFY(channel->keyframeAt(10));
1959 
1960     o = i->testingGetDataObjects();
1961     QVERIFY(o.m_data); // default m_data should always be present
1962     QVERIFY(!o.m_lodData);
1963     QVERIFY(!o.m_externalFrameData);
1964     QCOMPARE(o.m_frames.size(), 2);
1965     QVERIFY(o.m_currentData == o.m_frames[0]);
1966 
1967 
1968     KUndo2Command cmdMove;
1969     channel->moveKeyframe(frame, 12, &cmdMove);
1970 
1971     QVERIFY(!channel->keyframeAt(10));
1972     QVERIFY(channel->keyframeAt(12));
1973 
1974     o = i->testingGetDataObjects();
1975     QVERIFY(o.m_data); // default m_data should always be present
1976     QVERIFY(!o.m_lodData);
1977     QVERIFY(!o.m_externalFrameData);
1978     QCOMPARE(o.m_frames.size(), 2);
1979     QVERIFY(o.m_currentData == o.m_frames[0]);
1980 
1981     cmdMove.undo();
1982 
1983     QVERIFY(channel->keyframeAt(10));
1984     QVERIFY(!channel->keyframeAt(12));
1985 
1986     o = i->testingGetDataObjects();
1987     QVERIFY(o.m_data); // default m_data should always be present
1988     QVERIFY(!o.m_lodData);
1989     QVERIFY(!o.m_externalFrameData);
1990     QCOMPARE(o.m_frames.size(), 2);
1991     QVERIFY(o.m_currentData == o.m_frames[0]);
1992 
1993     cmdMove.redo();
1994 
1995     QVERIFY(!channel->keyframeAt(10));
1996     QVERIFY(channel->keyframeAt(12));
1997 
1998     o = i->testingGetDataObjects();
1999     QVERIFY(o.m_data); // default m_data should always be present
2000     QVERIFY(!o.m_lodData);
2001     QVERIFY(!o.m_externalFrameData);
2002     QCOMPARE(o.m_frames.size(), 2);
2003     QVERIFY(o.m_currentData == o.m_frames[0]);
2004 }
2005 
fillRect(KisPaintDeviceSP dev,int time,const QRect & rc,TestUtil::TestingTimedDefaultBounds * bounds)2006 void fillRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds)
2007 {
2008     KUndo2Command parentCommand;
2009     KisRasterKeyframeChannel *channel = dev->keyframeChannel();
2010     KisKeyframeSP frame = channel->addKeyframe(time, &parentCommand);
2011 
2012     const int oldTime = bounds->currentTime();
2013     bounds->testingSetTime(time);
2014 
2015     KoColor color(Qt::red, dev->colorSpace());
2016     dev->fill(rc, color);
2017 
2018     bounds->testingSetTime(oldTime);
2019 }
2020 
checkRect(KisPaintDeviceSP dev,int time,const QRect & rc,TestUtil::TestingTimedDefaultBounds * bounds)2021 bool checkRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds)
2022 {
2023     const int oldTime = bounds->currentTime();
2024     bounds->testingSetTime(time);
2025 
2026     bool result = dev->exactBounds() == rc;
2027 
2028     if (!result) {
2029         qDebug() << "Failed to check frame:" << ppVar(time) << ppVar(rc) << ppVar(dev->exactBounds());
2030     }
2031 
2032     bounds->testingSetTime(oldTime);
2033 
2034     return result;
2035 }
2036 
testCrossDeviceFrameCopyImpl(bool useChannel)2037 void testCrossDeviceFrameCopyImpl(bool useChannel)
2038 {
2039     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
2040     KisPaintDeviceSP dev1 = new KisPaintDevice(cs);
2041     KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
2042 
2043     const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16();
2044     KisPaintDeviceSP dev3 = new KisPaintDevice(cs3);
2045 
2046     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
2047     dev1->setDefaultBounds(bounds);
2048     dev2->setDefaultBounds(bounds);
2049     dev3->setDefaultBounds(bounds);
2050 
2051     KisRasterKeyframeChannel *channel1 = dev1->createKeyframeChannel(KisKeyframeChannel::Content);
2052     KisPaintDeviceFramesInterface *i1 = dev1->framesInterface();
2053     QVERIFY(channel1);
2054     QVERIFY(i1);
2055 
2056     KisRasterKeyframeChannel *channel2 = dev2->createKeyframeChannel(KisKeyframeChannel::Content);
2057     KisPaintDeviceFramesInterface *i2 = dev2->framesInterface();
2058     QVERIFY(channel2);
2059     QVERIFY(i2);
2060 
2061     KisRasterKeyframeChannel *channel3 = dev3->createKeyframeChannel(KisKeyframeChannel::Content);
2062     KisPaintDeviceFramesInterface *i3 = dev3->framesInterface();
2063     QVERIFY(channel3);
2064     QVERIFY(i3);
2065 
2066     fillRect(dev1, 10, QRect(100,100,100,100), bounds);
2067     fillRect(dev2, 20, QRect(200,200,100,100), bounds);
2068     fillRect(dev3, 30, QRect(300,300,100,100), bounds);
2069 
2070     QCOMPARE(dev1->exactBounds(), QRect());
2071 
2072     const int dstFrameId1 = channel1->frameIdAt(10);
2073     const int srcFrameId2 = channel2->frameIdAt(20);
2074     const int srcFrameId3 = channel3->frameIdAt(30);
2075 
2076     KUndo2Command cmd1;
2077     if (!useChannel) {
2078         dev1->framesInterface()->uploadFrame(srcFrameId2, dstFrameId1, dev2);
2079     } else {
2080         KisKeyframeSP k = channel1->copyExternalKeyframe(channel2, 20, 10, &cmd1);
2081     }
2082 
2083     QCOMPARE(dev1->exactBounds(), QRect());
2084     QVERIFY(checkRect(dev1, 10, QRect(200,200,100,100), bounds));
2085 
2086     if (useChannel) {
2087         cmd1.undo();
2088         QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds));
2089     }
2090 
2091     KUndo2Command cmd2;
2092     if (!useChannel) {
2093         dev1->framesInterface()->uploadFrame(srcFrameId3, dstFrameId1, dev3);
2094     } else {
2095         KisKeyframeSP k = channel1->copyExternalKeyframe(channel3, 30, 10, &cmd2);
2096     }
2097 
2098     QCOMPARE(dev1->exactBounds(), QRect());
2099     QVERIFY(checkRect(dev1, 10, QRect(300,300,100,100), bounds));
2100 
2101     if (useChannel) {
2102         cmd2.undo();
2103         QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds));
2104     }
2105 }
2106 
testCrossDeviceFrameCopyDirect()2107 void KisPaintDeviceTest::testCrossDeviceFrameCopyDirect()
2108 {
2109     testCrossDeviceFrameCopyImpl(false);
2110 }
2111 
testCrossDeviceFrameCopyChannel()2112 void KisPaintDeviceTest::testCrossDeviceFrameCopyChannel()
2113 {
2114     testCrossDeviceFrameCopyImpl(true);
2115 }
2116 
2117 #include "kis_surrogate_undo_adapter.h"
2118 
testLazyFrameCreation()2119 void KisPaintDeviceTest::testLazyFrameCreation()
2120 {
2121     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
2122     KisPaintDeviceSP dev = new KisPaintDevice(cs);
2123 
2124     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
2125     dev->setDefaultBounds(bounds);
2126 
2127     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
2128     QVERIFY(channel);
2129 
2130     KisPaintDeviceFramesInterface *i = dev->framesInterface();
2131     QVERIFY(i);
2132 
2133     QCOMPARE(i->frames().size(), 1);
2134 
2135     bounds->testingSetTime(10);
2136 
2137     QCOMPARE(i->frames().size(), 1);
2138 
2139     KisSurrogateUndoAdapter undoAdapter;
2140 
2141     {
2142         KisTransaction transaction1(dev);
2143         transaction1.commit(&undoAdapter);
2144     }
2145 
2146     QCOMPARE(i->frames().size(), 2);
2147 
2148     undoAdapter.undoAll();
2149 
2150     QCOMPARE(i->frames().size(), 1);
2151 
2152     undoAdapter.redoAll();
2153 
2154     QCOMPARE(i->frames().size(), 2);
2155 }
2156 
testCopyPaintDeviceWithFrames()2157 void KisPaintDeviceTest::testCopyPaintDeviceWithFrames()
2158 {
2159     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
2160     KisPaintDeviceSP dev = new KisPaintDevice(cs);
2161 
2162     TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
2163     dev->setDefaultBounds(bounds);
2164 
2165     KisRasterKeyframeChannel *channel = dev->createKeyframeChannel(KisKeyframeChannel::Content);
2166     QVERIFY(channel);
2167 
2168     KisPaintDeviceFramesInterface *i = dev->framesInterface();
2169     QVERIFY(i);
2170 
2171     QCOMPARE(i->frames().size(), 1);
2172 
2173     KisPaintDeviceFramesInterface::TestingDataObjects o;
2174 
2175     // Itinial state: one frame, m_data shared
2176     o = i->testingGetDataObjects();
2177     QVERIFY(o.m_data); // m_data should always be present
2178     QVERIFY(!o.m_lodData);
2179     QVERIFY(!o.m_externalFrameData);
2180     QCOMPARE(o.m_frames.size(), 1);
2181     QVERIFY(o.m_currentData == o.m_frames[0]);
2182 
2183 
2184     // add a keyframe
2185 
2186     KUndo2Command cmdAdd;
2187 
2188     KisKeyframeSP frame = channel->addKeyframe(10, &cmdAdd);
2189 
2190     QVERIFY(channel->keyframeAt(10));
2191 
2192     o = i->testingGetDataObjects();
2193     QVERIFY(o.m_data); // m_data should always be present
2194     QVERIFY(!o.m_lodData);
2195     QVERIFY(!o.m_externalFrameData);
2196     QCOMPARE(o.m_frames.size(), 2);
2197     //QVERIFY(o.m_currentData == o.m_frames[0]);
2198 
2199     KisPaintDeviceSP newDev = new KisPaintDevice(*dev, KritaUtils::CopyAllFrames);
2200 
2201     QVERIFY(channel->keyframeAt(0));
2202     QVERIFY(channel->keyframeAt(10));
2203 }
2204 
2205 #include <boost/accumulators/accumulators.hpp>
2206 #include <boost/accumulators/statistics/stats.hpp>
2207 #include <boost/accumulators/statistics/variance.hpp>
2208 #include <boost/accumulators/statistics/min.hpp>
2209 #include <boost/accumulators/statistics/max.hpp>
2210 #include <boost/random/mersenne_twister.hpp>
2211 #include <boost/random/uniform_smallint.hpp>
2212 
2213 #include "KoCompositeOpRegistry.h"
2214 
2215 
2216 using namespace boost::accumulators;
2217 
2218 accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
2219 
2220 
testCompositionAssociativity()2221 void KisPaintDeviceTest::testCompositionAssociativity()
2222 {
2223     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
2224 
2225     qsrand(500);
2226 
2227     boost::mt11213b _rnd0(qrand());
2228     boost::mt11213b _rnd1(qrand());
2229     boost::mt11213b _rnd2(qrand());
2230     boost::mt11213b _rnd3(qrand());
2231 
2232     boost::uniform_smallint<int> rnd0(0, 255);
2233     boost::uniform_smallint<int> rnd1(0, 255);
2234     boost::uniform_smallint<int> rnd2(0, 255);
2235     boost::uniform_smallint<int> rnd3(0, 255);
2236 
2237     QList<KoCompositeOp*> allCompositeOps = cs->compositeOps();
2238 
2239     Q_FOREACH (const KoCompositeOp *op, allCompositeOps) {
2240 
2241         accumulator_set<qreal, stats<tag::variance, tag::max, tag::min> > accum;
2242 
2243         const int numIterations = 10000;
2244 
2245         for (int j = 0; j < numIterations; j++) {
2246             KoColor c1(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
2247             KoColor c2(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
2248             KoColor c3(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
2249             //KoColor c4(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
2250             //KoColor c5(QColor(rnd0(_rnd0), rnd1(_rnd1), rnd2(_rnd2), rnd3(_rnd3)), cs);
2251 
2252             KoColor r1(QColor(Qt::transparent), cs);
2253             KoColor r2(QColor(Qt::transparent), cs);
2254             KoColor r3(QColor(Qt::transparent), cs);
2255 
2256             op->composite(r1.data(), 0, c1.data(), 0, 0,0, 1,1, 255);
2257             op->composite(r1.data(), 0, c2.data(), 0, 0,0, 1,1, 255);
2258             op->composite(r1.data(), 0, c3.data(), 0, 0,0, 1,1, 255);
2259             //op->composite(r1.data(), 0, c4.data(), 0, 0,0, 1,1, 255);
2260             //op->composite(r1.data(), 0, c5.data(), 0, 0,0, 1,1, 255);
2261 
2262             op->composite(r3.data(), 0, c2.data(), 0, 0,0, 1,1, 255);
2263             op->composite(r3.data(), 0, c3.data(), 0, 0,0, 1,1, 255);
2264             //op->composite(r3.data(), 0, c4.data(), 0, 0,0, 1,1, 255);
2265             //op->composite(r3.data(), 0, c5.data(), 0, 0,0, 1,1, 255);
2266 
2267             op->composite(r2.data(), 0, c1.data(), 0, 0,0, 1,1, 255);
2268             op->composite(r2.data(), 0, r3.data(), 0, 0,0, 1,1, 255);
2269 
2270             const quint8 *p1 = r1.data();
2271             const quint8 *p2 = r2.data();
2272 
2273             if (memcmp(p1, p2, 4) != 0) {
2274                 for (int i = 0; i < 4; i++) {
2275                     accum(qAbs(p1[i] - p2[i]));
2276                 }
2277             }
2278 
2279         }
2280 
2281         qDebug("Errors for op %25s err rate %7.2f var %7.2f max %7.2f",
2282                op->id().toLatin1().data(),
2283                (qreal(count(accum)) / (4 * numIterations)),
2284                variance(accum),
2285                count(accum) > 0 ? (max)(accum) : 0);
2286     }
2287 }
2288 
2289 #include <kundo2stack.h>
2290 
2291 struct FillWorker : public QRunnable
2292 {
FillWorkerFillWorker2293     FillWorker(KisPaintDeviceSP dev, const QRect &fillRect, bool clear)
2294         : m_dev(dev), m_fillRect(fillRect), m_clear(clear)
2295     {
2296         setAutoDelete(true);
2297     }
2298 
runFillWorker2299     void run() {
2300         if (m_clear) {
2301             m_dev->clear(m_fillRect);
2302         } else {
2303             const KoColor fillColor(Qt::red, m_dev->colorSpace());
2304             const int pixelSize = m_dev->colorSpace()->pixelSize();
2305 
2306             KisSequentialIterator it(m_dev, m_fillRect);
2307             while (it.nextPixel()) {
2308                 memcpy(it.rawData(), fillColor.data(), pixelSize);
2309             }
2310         }
2311     }
2312 
2313 private:
2314     KisPaintDeviceSP m_dev;
2315     QRect m_fillRect;
2316     bool m_clear;
2317 };
2318 
2319 #ifdef Q_OS_LINUX
2320 #include <malloc.h>
2321 #endif
2322 
stressTestMemoryFragmentation()2323 void KisPaintDeviceTest::stressTestMemoryFragmentation()
2324 {
2325     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
2326     KisPaintDeviceSP dev = new KisPaintDevice(cs);
2327 
2328     KUndo2Stack undoStack;
2329 
2330 #ifdef LIMIT_LONG_TESTS
2331     const int numCycles = 3;
2332     undoStack.setUndoLimit(1);
2333 #else
2334     const int numCycles = 200;
2335     undoStack.setUndoLimit(10);
2336 #endif
2337 
2338     const int numThreads = 16;
2339     const int desiredWidth = 10000;
2340     const int patchSize = 81;
2341     const int numSidePatches = desiredWidth / patchSize;
2342 
2343     QThreadPool pool;
2344     pool.setMaxThreadCount(numThreads);
2345 
2346 
2347     for (int i = 0; i < numCycles; i++) {
2348         qDebug() << "iteration"<< i;
2349         // KisTransaction t(dev);
2350 
2351         for (int y = 0; y < numSidePatches; y++) {
2352             for (int x = 0; x < numSidePatches; x++) {
2353                 const QRect workerRect(x * patchSize, y * patchSize, patchSize, patchSize);
2354                 pool.start(new FillWorker(dev, workerRect, (i + x + y) & 0x1));
2355             }
2356         }
2357 
2358         pool.waitForDone();
2359         // undoStack.push(t.endAndTake());
2360 
2361         qDebug() << "Iteration:" << i;
2362 
2363 #ifdef Q_OS_LINUX
2364         struct mallinfo info = mallinfo();
2365         qDebug() << "Allocated on heap:" << (info.arena >> 20) << "MiB";
2366         qDebug() << "Mmaped regions:" << info.hblks << (info.hblkhd >> 20) << "MiB";
2367         qDebug() << "Free fastbin chunks:" << info.smblks << (info.fsmblks >> 10)  << "KiB";
2368         qDebug() << "Allocated in ordinary blocks" << (info.uordblks >> 20) << "MiB";
2369         qDebug() << "Free in ordinary blockes" << info.ordblks << (info.fordblks >> 20) << "MiB";
2370 #endif
2371         qDebug() << "========================================";
2372     }
2373 
2374     undoStack.clear();
2375 }
2376 
2377 KISTEST_MAIN(KisPaintDeviceTest)
2378