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