diff --git a/libs/image/tests/kis_iterator_test.cpp b/libs/image/tests/kis_iterator_test.cpp index fe6bb0771b..1d663fc435 100644 --- a/libs/image/tests/kis_iterator_test.cpp +++ b/libs/image/tests/kis_iterator_test.cpp @@ -1,479 +1,479 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_iterator_test.h" #include #include #include #include #include #include #include "kis_random_accessor_ng.h" #include "kis_random_sub_accessor.h" #include #include "kis_paint_device.h" #include #include "sdk/tests/kistest.h" void KisIteratorTest::allCsApplicator(void (KisIteratorTest::* funcPtr)(const KoColorSpace*cs)) { QList colorsapces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile); Q_FOREACH (const KoColorSpace* cs, colorsapces) { dbgKrita << "Testing with" << cs->id(); if (cs->id() != "GRAYU16") // No point in testing extend for GRAYU16 (this->*funcPtr)(cs); } } inline quint8* allocatePixels(const KoColorSpace *colorSpace, int numPixels) { quint8 *bytes = new quint8[colorSpace->pixelSize() * 64 * 64 * 10]; KoColor color(Qt::red, colorSpace); const int pixelSize = colorSpace->pixelSize(); for(int i = 0; i < numPixels; i++) { memcpy(bytes + i * pixelSize, color.data(), pixelSize); } return bytes; } void KisIteratorTest::writeBytes(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); // Check allocation on tile boundaries // Allocate memory for a 2 * 5 tiles grid QScopedArrayPointer bytes(allocatePixels(colorSpace, 64 * 64 * 10)); // Covers 5 x 2 tiles dev.writeBytes(bytes.data(), 0, 0, 5 * 64, 2 * 64); // Covers QCOMPARE(dev.extent(), QRect(0, 0, 64 * 5, 64 * 2)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 64 * 5, 64 * 2)); dev.clear(); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); dev.clear(); // Covers three by three tiles dev.writeBytes(bytes.data(), 10, 10, 130, 130); QCOMPARE(dev.extent(), QRect(0, 0, 64 * 3, 64 * 3)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 130, 130)); dev.clear(); // Covers 11 x 2 tiles dev.writeBytes(bytes.data(), -10, -10, 10 * 64, 64); QCOMPARE(dev.extent(), QRect(-64, -64, 64 * 11, 64 * 2)); QCOMPARE(dev.exactBounds(), QRect(-10, -10, 640, 64)); } void KisIteratorTest::fill(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); dev.fill(0, 0, 5, 5, bytes.data()); QCOMPARE(dev.extent(), QRect(0, 0, 64, 64)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 5, 5)); dev.clear(); dev.fill(5, 5, 5, 5, bytes.data()); QCOMPARE(dev.extent(), QRect(0, 0, 64, 64)); QCOMPARE(dev.exactBounds(), QRect(5, 5, 5, 5)); dev.clear(); dev.fill(5, 5, 500, 500, bytes.data()); QCOMPARE(dev.extent(), QRect(0, 0, 8 * 64, 8 * 64)); QCOMPARE(dev.exactBounds(), QRect(5, 5, 500, 500)); dev.clear(); dev.fill(33, -10, 348, 1028, bytes.data()); QCOMPARE(dev.extent(), QRect(0, -64, 6 * 64, 17 * 64)); QCOMPARE(dev.exactBounds(), QRect(33, -10, 348, 1028)); } void KisIteratorTest::hLineIter(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisHLineConstIteratorSP cit = dev.createHLineConstIteratorNG(0, 0, 128); do {} while (cit->nextPixel()); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1))); { dev.clear(); KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 128, 64)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 1)); } dev.clear(); { KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 1, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 128, 64)); QCOMPARE(dev.exactBounds(), QRect(0, 1, 128, 1)); } dev.clear(); { KisHLineIteratorSP it = dev.createHLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 192, 64)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1)); } dev.clear(); dev.setX(10); dev.setY(-15); { KisHLineIteratorSP it = dev.createHLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(10, -15, 128, 64)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1)); } } void KisIteratorTest::vLineIter(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisVLineConstIteratorSP cit = dev.createVLineConstIteratorNG(0, 0, 128); do {} while (cit->nextPixel()); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1))); { KisVLineIteratorSP it = dev.createVLineIteratorNG(0, 0, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE((QRect) dev.extent(), QRect(0, 0, 64, 128)); QCOMPARE((QRect) dev.exactBounds(), QRect(0, 0, 1, 128)); } dev.clear(); { KisVLineIteratorSP it = dev.createVLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 64, 192)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128)); } dev.clear(); dev.setX(10); dev.setY(-15); { KisVLineIteratorSP it = dev.createVLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(10, -15, 64, 192)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128)); } } void KisIteratorTest::randomAccessor(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisRandomConstAccessorSP acc = dev.createRandomConstAccessorNG(0, 0); for (int y = 0; y < 128; ++y) { for (int x = 0; x < 128; ++x) { acc->moveTo(x, y); } } - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0); for (int y = 0; y < 128; ++y) { for (int x = 0; x < 128; ++x) { ac->moveTo(x, y); memcpy(ac->rawData(), bytes.data(), colorSpace->pixelSize()); } } QCOMPARE(dev.extent(), QRect(0, 0, 128, 128)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128)); dev.clear(); dev.setX(10); dev.setY(-15); { KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0); for (int y = 0; y < 128; ++y) { for (int x = 0; x < 128; ++x) { ac->moveTo(x, y); memcpy(ac->rawData(), bytes.data(), colorSpace->pixelSize()); } } QCOMPARE(dev.extent(), QRect(-54, -15, 192, 192)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128)); } } void KisIteratorTest::repeatHLineIter(const KoColorSpace* cs) { KoColor color(Qt::green, cs); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(5, 5, 10, 10, color.data()); KisRepeatHLineConstIteratorSP iter = dev->createRepeatHLineConstIterator(0, 0, 20, QRect(5, 5, 10, 10)); for(int i = 0; i < 20; i++) { do { QVERIFY(!memcmp(color.data(), iter->oldRawData(), cs->pixelSize())); } while (iter->nextPixel()); iter->nextRow(); } } void KisIteratorTest::repeatVLineIter(const KoColorSpace* cs) { KoColor color(Qt::green, cs); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(5, 5, 10, 10, color.data()); KisRepeatVLineConstIteratorSP iter = dev->createRepeatVLineConstIterator(0, 0, 20, QRect(5, 5, 10, 10)); for(int i = 0; i < 20; i++) { do { QVERIFY(!memcmp(color.data(), iter->oldRawData(), cs->pixelSize())); } while (iter->nextPixel()); iter->nextColumn(); } } void KisIteratorTest::writeBytes() { allCsApplicator(&KisIteratorTest::writeBytes); } void KisIteratorTest::fill() { allCsApplicator(&KisIteratorTest::fill); } void KisIteratorTest::hLineIter() { allCsApplicator(&KisIteratorTest::hLineIter); } void KisIteratorTest::vLineIter() { allCsApplicator(&KisIteratorTest::vLineIter); } void KisIteratorTest::randomAccessor() { allCsApplicator(&KisIteratorTest::randomAccessor); } void KisIteratorTest::repeatHLineIter() { allCsApplicator(&KisIteratorTest::repeatHLineIter); } void KisIteratorTest::repeatVLineIter() { allCsApplicator(&KisIteratorTest::repeatVLineIter); } #define NUM_CYCLES 10000 #define NUM_THREADS 10 class DataReaderThread : public QRunnable { public: DataReaderThread(KisPaintDeviceSP device, const QRect &rect) : m_device(device), m_rect(rect) {} void run() override { for(int i = 0; i < NUM_CYCLES; i++) { KisRandomAccessorSP iter = m_device->createRandomAccessorNG(m_rect.x(), m_rect.y()); qint32 rowsRemaining = m_rect.height(); qint32 y = m_rect.y(); while (rowsRemaining > 0) { qint32 columnsRemaining = m_rect.width(); qint32 x = m_rect.x(); qint32 numContiguousRows = iter->numContiguousRows(y); qint32 rows = qMin(numContiguousRows, rowsRemaining); while (columnsRemaining > 0) { qint32 numContiguousColumns = iter->numContiguousColumns(x); qint32 columns = qMin(numContiguousColumns, columnsRemaining); qint32 rowStride = iter->rowStride(x, y); iter->moveTo(x, y); // dbgKrita << "BitBlt:" << ppVar(x) << ppVar(y) // << ppVar(columns) << ppVar(rows) // << ppVar(rowStride); doBitBlt(iter->rawData(), rowStride, m_device->pixelSize(), rows, columns); x += columns; columnsRemaining -= columns; } y += rows; rowsRemaining -= rows; } } } private: void doBitBltConst(const quint8* data, qint32 rowStride, qint32 pixelSize, qint32 rows, qint32 columns) { for(int i = 0; i < rows; i++) { Q_ASSERT(columns * pixelSize < 256); quint8 tempData[256]; memcpy(tempData, data, columns * pixelSize); data += rowStride; } } void doBitBlt(quint8* data, qint32 rowStride, qint32 pixelSize, qint32 rows, qint32 columns) { for(int i = 0; i < rows; i++) { // Let's write something here... memset(data, 0x13, columns * pixelSize); data += rowStride; } } private: KisPaintDeviceSP m_device; QRect m_rect; }; class NastyThread : public QRunnable { public: NastyThread(KisPaintDeviceSP device) : m_device(device) {} void run() override { for(int i = 0; i < NUM_CYCLES; i++) { m_device->setX(-0x400 + (qrand() & 0x7FF)); m_device->setY(-0x400 + (qrand() & 0x7FF)); QTest::qSleep(10); } } private: KisPaintDeviceSP m_device; }; void KisIteratorTest::stressTest() { const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8(); QRect imageRect(0,0,2000,2000); KisPaintDeviceSP device = new KisPaintDevice(colorSpace); device->fill(imageRect, KoColor(Qt::red, colorSpace)); QThreadPool threadPool; threadPool.setMaxThreadCount(NUM_THREADS); for(int i = 0; i< NUM_THREADS; i++) { QRect rc = QRect(double(i) / NUM_THREADS * 2000, 0, 2000 / NUM_THREADS, 2000); // dbgKrita << rc; DataReaderThread *reader = new DataReaderThread(device, rc); threadPool.start(reader); if(!(i & 0x1)) { NastyThread *nasty = new NastyThread(device); threadPool.start(nasty); } } threadPool.waitForDone(); } KISTEST_MAIN(KisIteratorTest) diff --git a/libs/image/tests/kis_iterators_ng_test.cpp b/libs/image/tests/kis_iterators_ng_test.cpp index 67ec986d4e..578b981a7c 100644 --- a/libs/image/tests/kis_iterators_ng_test.cpp +++ b/libs/image/tests/kis_iterators_ng_test.cpp @@ -1,497 +1,497 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_iterators_ng_test.h" #include #include #include #include #include #include #include "kis_random_accessor_ng.h" #include "kis_random_sub_accessor.h" #include "kis_paint_device.h" #include #include "kis_global.h" #include "testutil.h" void KisIteratorNGTest::allCsApplicator(void (KisIteratorNGTest::* funcPtr)(const KoColorSpace*cs)) { QList colorspaces = KoColorSpaceRegistry::instance()->allColorSpaces(KoColorSpaceRegistry::AllColorSpaces, KoColorSpaceRegistry::OnlyDefaultProfile); Q_FOREACH (const KoColorSpace* cs, colorspaces) { dbgKrita << "Testing with" << cs->id(); if (cs->id() != "GRAYU16") // No point in testing extend for GRAYU16 (this->*funcPtr)(cs); } } inline quint8* allocatePixels(const KoColorSpace *colorSpace, int numPixels) { quint8 * bytes = new quint8[colorSpace->pixelSize() * 64 * 64 * 10]; KoColor color(Qt::red, colorSpace); const int pixelSize = colorSpace->pixelSize(); for(int i = 0; i < numPixels; i++) { memcpy(bytes + i * pixelSize, color.data(), pixelSize); } return bytes; } void KisIteratorNGTest::writeBytes(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); // Check allocation on tile boundaries // Allocate memory for a 2 * 5 tiles grid QScopedArrayPointer bytes(allocatePixels(colorSpace, 64 * 64 * 10)); // Covers 5 x 2 tiles dev.writeBytes(bytes.data(), 0, 0, 5 * 64, 2 * 64); // Covers QCOMPARE(dev.extent(), QRect(0, 0, 64 * 5, 64 * 2)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 64 * 5, 64 * 2)); dev.clear(); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); dev.clear(); // Covers three by three tiles dev.writeBytes(bytes.data(), 10, 10, 130, 130); QCOMPARE(dev.extent(), QRect(0, 0, 64 * 3, 64 * 3)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 130, 130)); dev.clear(); // Covers 11 x 2 tiles dev.writeBytes(bytes.data(), -10, -10, 10 * 64, 64); QCOMPARE(dev.extent(), QRect(-64, -64, 64 * 11, 64 * 2)); QCOMPARE(dev.exactBounds(), QRect(-10, -10, 640, 64)); } void KisIteratorNGTest::fill(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); dev.fill(0, 0, 5, 5, bytes.data()); QCOMPARE(dev.extent(), QRect(0, 0, 64, 64)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 5, 5)); dev.clear(); dev.fill(5, 5, 5, 5, bytes.data()); QCOMPARE(dev.extent(), QRect(0, 0, 64, 64)); QCOMPARE(dev.exactBounds(), QRect(5, 5, 5, 5)); dev.clear(); dev.fill(5, 5, 500, 500, bytes.data()); QCOMPARE(dev.extent(), QRect(0, 0, 8 * 64, 8 * 64)); QCOMPARE(dev.exactBounds(), QRect(5, 5, 500, 500)); dev.clear(); dev.fill(33, -10, 348, 1028, bytes.data()); QCOMPARE(dev.extent(), QRect(0, -64, 6 * 64, 17 * 64)); QCOMPARE(dev.exactBounds(), QRect(33, -10, 348, 1028)); } void KisIteratorNGTest::sequentialIter(const KoColorSpace * colorSpace) { KisPaintDeviceSP dev = new KisPaintDevice(colorSpace); - QCOMPARE(dev->extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev->extent(), QRect()); // Const does not extend the extent { KisSequentialConstIterator it(dev, QRect(0, 0, 128, 128)); while (it.nextPixel()); - QCOMPARE(dev->extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev->extent(), QRect()); QCOMPARE(dev->exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1))); } // Non-const does { KisSequentialIterator it(dev, QRect(0, 0, 128, 128)); int i = -1; while (it.nextPixel()) { i++; KoColor c(QColor(i % 255, i / 255, 0), colorSpace); memcpy(it.rawData(), c.data(), colorSpace->pixelSize()); QCOMPARE(it.x(), i % 128); QCOMPARE(it.y(), i / 128); } QCOMPARE(dev->extent(), QRect(0, 0, 128, 128)); QCOMPARE(dev->exactBounds(), QRect(0, 0, 128, 128)); } { // check const iterator KisSequentialConstIterator it(dev, QRect(0, 0, 128, 128)); int i = -1; while (it.nextPixel()) { i++; KoColor c(QColor(i % 255, i / 255, 0), colorSpace); QVERIFY(memcmp(it.rawDataConst(), c.data(), colorSpace->pixelSize()) == 0); } QCOMPARE(dev->extent(), QRect(0, 0, 128, 128)); QCOMPARE(dev->exactBounds(), QRect(0, 0, 128, 128)); } { // check const iterator with **empty** area! It should neither crash nor enter the loop KisSequentialConstIterator it(dev, QRect()); QVERIFY(!it.rawDataConst()); QVERIFY(!it.oldRawData()); while (it.nextPixel()) { QVERIFY(0 && "we should never enter the loop"); } } { // check const iterator with strides KisSequentialConstIterator it(dev, QRect(0, 0, 128, 128)); int i = -1; int numConseqPixels = it.nConseqPixels(); while (it.nextPixels(numConseqPixels)) { numConseqPixels = it.nConseqPixels(); for (int j = 0; j < numConseqPixels; j++) { i++; KoColor c(QColor(i % 255, i / 255, 0), colorSpace); QVERIFY(memcmp(it.rawDataConst() + j * colorSpace->pixelSize(), c.data(), colorSpace->pixelSize()) == 0); } } QCOMPARE(dev->extent(), QRect(0, 0, 128, 128)); QCOMPARE(dev->exactBounds(), QRect(0, 0, 128, 128)); } { // check const iterator with strides and **empty** area KisSequentialConstIterator it(dev, QRect()); QVERIFY(!it.rawDataConst()); QVERIFY(!it.oldRawData()); int numConseqPixels = it.nConseqPixels(); while (it.nextPixels(numConseqPixels)) { QVERIFY(0 && "we should never enter the loop"); } } dev->clear(); { KisSequentialIterator it(dev, QRect(10, 10, 128, 128)); int i = -1; while (it.nextPixel()) { i++; KoColor c(QColor(i % 255, i / 255, 0), colorSpace); memcpy(it.rawData(), c.data(), colorSpace->pixelSize()); } QCOMPARE(dev->extent(), QRect(0, 0, 3 * 64, 3 * 64)); QCOMPARE(dev->exactBounds(), QRect(10, 10, 128, 128)); } dev->clear(); dev->setX(10); dev->setY(-15); { KisSequentialIterator it(dev, QRect(10, 10, 128, 128)); int i = -1; while (it.nextPixel()) { i++; KoColor c(QColor(i % 255, i / 255, 0), colorSpace); memcpy(it.rawData(), c.data(), colorSpace->pixelSize()); } QCOMPARE(dev->extent(), QRect(10, -15, 128, 192)); QCOMPARE(dev->exactBounds(), QRect(10, 10, 128, 128)); } { KisSequentialIterator it(dev, QRect(10, 10, 128, 128)); QCOMPARE(it.rawData(), it.oldRawData()); } } void KisIteratorNGTest::hLineIter(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisHLineConstIteratorSP cit = dev.createHLineConstIteratorNG(0, 0, 128); while (!cit->nextPixel()); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1))); dev.clear(); KisHLineIteratorSP it = dev.createHLineIteratorNG(0, 0, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 128, 64)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 1)); dev.clear(); it = dev.createHLineIteratorNG(0, 1, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 128, 64)); QCOMPARE(dev.exactBounds(), QRect(0, 1, 128, 1)); dev.clear(); it = dev.createHLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 192, 64)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1)); dev.clear(); dev.setX(10); dev.setY(-15); it = dev.createHLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while (it->nextPixel()); QCOMPARE(dev.extent(), QRect(10, -15, 128, 64)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 128, 1)); it = dev.createHLineIteratorNG(10, 10, 128); it->nextRow(); QCOMPARE(it->rawData(), it->oldRawData()); } void KisIteratorNGTest::justCreation(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); dev.createVLineConstIteratorNG(0, 0, 128); dev.createVLineIteratorNG(0, 0, 128); dev.createHLineConstIteratorNG(0, 0, 128); dev.createHLineIteratorNG(0, 0, 128); } void KisIteratorNGTest::vLineIter(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisVLineConstIteratorSP cit = dev.createVLineConstIteratorNG(0, 0, 128); while (cit->nextPixel()); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); QCOMPARE(dev.exactBounds(), QRect(QPoint(0, 0), QPoint(-1, -1))); cit.clear(); KisVLineIteratorSP it = dev.createVLineIteratorNG(0, 0, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while(it->nextPixel()); QCOMPARE((QRect) dev.extent(), QRect(0, 0, 64, 128)); QCOMPARE((QRect) dev.exactBounds(), QRect(0, 0, 1, 128)); it.clear(); dev.clear(); it = dev.createVLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while(it->nextPixel()); QCOMPARE(dev.extent(), QRect(0, 0, 64, 192)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128)); dev.clear(); dev.setX(10); dev.setY(-15); it = dev.createVLineIteratorNG(10, 10, 128); do { memcpy(it->rawData(), bytes.data(), colorSpace->pixelSize()); } while(it->nextPixel()); QCOMPARE(dev.extent(), QRect(10, -15, 64, 192)); QCOMPARE(dev.exactBounds(), QRect(10, 10, 1, 128)); it = dev.createVLineIteratorNG(10, 10, 128); it->nextColumn(); QCOMPARE(it->rawData(), it->oldRawData()); } void KisIteratorNGTest::randomAccessor(const KoColorSpace * colorSpace) { KisPaintDevice dev(colorSpace); QScopedArrayPointer bytes(allocatePixels(colorSpace, 1)); - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisRandomConstAccessorSP acc = dev.createRandomConstAccessorNG(0, 0); for (int y = 0; y < 128; ++y) { for (int x = 0; x < 128; ++x) { acc->moveTo(x, y); } } - QCOMPARE(dev.extent(), QRect(qint32_MAX, qint32_MAX, 0, 0)); + QCOMPARE(dev.extent(), QRect()); KisRandomAccessorSP ac = dev.createRandomAccessorNG(0, 0); for (int y = 0; y < 128; ++y) { for (int x = 0; x < 128; ++x) { ac->moveTo(x, y); memcpy(ac->rawData(), bytes.data(), colorSpace->pixelSize()); } } QCOMPARE(dev.extent(), QRect(0, 0, 128, 128)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128)); dev.clear(); dev.setX(10); dev.setY(-15); ac = dev.createRandomAccessorNG(0, 0); for (int y = 0; y < 128; ++y) { for (int x = 0; x < 128; ++x) { ac->moveTo(x, y); memcpy(ac->rawData(), bytes.data(), colorSpace->pixelSize()); } } QCOMPARE(dev.extent(), QRect(-54, -15, 192, 192)); QCOMPARE(dev.exactBounds(), QRect(0, 0, 128, 128)); } void KisIteratorNGTest::writeBytes() { allCsApplicator(&KisIteratorNGTest::writeBytes); } void KisIteratorNGTest::fill() { allCsApplicator(&KisIteratorNGTest::fill); } void KisIteratorNGTest::sequentialIter() { allCsApplicator(&KisIteratorNGTest::sequentialIter); } #include void KisIteratorNGTest::sequentialIteratorWithProgress() { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); const QRect rc(10,10,200,200); TestUtil::TestProgressBar proxy; KisSequentialConstIteratorProgress it (dev, rc, &proxy); while (it.nextPixel()) { QCOMPARE(proxy.min(), rc.top()); QCOMPARE(proxy.max(), rc.top() + rc.height()); QCOMPARE(proxy.value(), it.y()); } QCOMPARE(proxy.max(), rc.top() + rc.height()); QCOMPARE(proxy.value(), proxy.max()); } void KisIteratorNGTest::sequentialIteratorWithProgressIncomplete() { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); const QRect rc(10,10,100,100); TestUtil::TestProgressBar proxy; { KisSequentialConstIteratorProgress it (dev, rc, &proxy); QCOMPARE(proxy.max(), rc.top() + rc.height()); QCOMPARE(proxy.value(), rc.top()); } // on desruction, iterator automatically completes progress reporting QCOMPARE(proxy.max(), rc.top() + rc.height()); QCOMPARE(proxy.value(), proxy.max()); } void KisIteratorNGTest::hLineIter() { allCsApplicator(&KisIteratorNGTest::hLineIter); } void KisIteratorNGTest::justCreation() { allCsApplicator(&KisIteratorNGTest::justCreation); } void KisIteratorNGTest::vLineIter() { allCsApplicator(&KisIteratorNGTest::vLineIter); } void KisIteratorNGTest::randomAccessor() { allCsApplicator(&KisIteratorNGTest::randomAccessor); } KISTEST_MAIN(KisIteratorNGTest) diff --git a/libs/image/tiles3/KisTiledExtentManager.cpp b/libs/image/tiles3/KisTiledExtentManager.cpp index 66f85a0327..1ac535ce07 100644 --- a/libs/image/tiles3/KisTiledExtentManager.cpp +++ b/libs/image/tiles3/KisTiledExtentManager.cpp @@ -1,328 +1,328 @@ /* * Copyright (c) 2017 Dmitry Kazakov * Copyright (c) 2018 Andrey Kamakin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisTiledExtentManager.h" #include #include #include "kis_tile_data_interface.h" #include "kis_assert.h" #include "kis_global.h" #include "kis_debug.h" KisTiledExtentManager::Data::Data() : m_min(qint32_MAX), m_max(qint32_MIN), m_count(0) { QWriteLocker lock(&m_migrationLock); m_capacity = InitialBufferSize; m_offset = 1; m_buffer = new QAtomicInt[m_capacity]; } KisTiledExtentManager::Data::~Data() { QWriteLocker lock(&m_migrationLock); delete[] m_buffer; } inline bool KisTiledExtentManager::Data::add(qint32 index) { QReadLocker lock(&m_migrationLock); qint32 currentIndex = m_offset + index; if (currentIndex < 0 || currentIndex >= m_capacity) { lock.unlock(); migrate(index); lock.relock(); currentIndex = m_offset + index; } KIS_ASSERT_RECOVER_NOOP(m_buffer[currentIndex].loadAcquire() >= 0); bool needsUpdateExtent = false; QReadLocker rl(&m_extentLock); if (!m_buffer[currentIndex].loadAcquire()) { rl.unlock(); QWriteLocker wl(&m_extentLock); if (!m_buffer[currentIndex].load()) { m_buffer[currentIndex].store(1); if (m_min > index) m_min = index; if (m_max < index) m_max = index; ++m_count; needsUpdateExtent = true; } else { m_buffer[currentIndex].ref(); } } else { m_buffer[currentIndex].ref(); } return needsUpdateExtent; } inline bool KisTiledExtentManager::Data::remove(qint32 index) { QReadLocker lock(&m_migrationLock); qint32 currentIndex = m_offset + index; KIS_ASSERT_RECOVER_NOOP(m_buffer[currentIndex].loadAcquire() > 0); bool needsUpdateExtent = false; QReadLocker rl(&m_extentLock); if (m_buffer[currentIndex].loadAcquire() == 1) { rl.unlock(); QWriteLocker wl(&m_extentLock); if (m_buffer[currentIndex].load() == 1) { m_buffer[currentIndex].store(0); if (m_min == index) updateMin(); if (m_max == index) updateMax(); --m_count; needsUpdateExtent = true; } else { m_buffer[currentIndex].deref(); } } else { m_buffer[currentIndex].deref(); } return needsUpdateExtent; } void KisTiledExtentManager::Data::replace(const QVector &indexes) { QWriteLocker lock(&m_migrationLock); QWriteLocker l(&m_extentLock); for (qint32 i = 0; i < m_capacity; ++i) { m_buffer[i].store(0); } m_min = qint32_MAX; m_max = qint32_MIN; m_count = 0; Q_FOREACH (const qint32 index, indexes) { unsafeAdd(index); } } void KisTiledExtentManager::Data::clear() { QWriteLocker lock(&m_migrationLock); QWriteLocker l(&m_extentLock); for (qint32 i = 0; i < m_capacity; ++i) { m_buffer[i].store(0); } m_min = qint32_MAX; m_max = qint32_MIN; m_count = 0; } bool KisTiledExtentManager::Data::isEmpty() { return m_count == 0; } qint32 KisTiledExtentManager::Data::min() { return m_min; } qint32 KisTiledExtentManager::Data::max() { return m_max; } void KisTiledExtentManager::Data::unsafeAdd(qint32 index) { qint32 currentIndex = m_offset + index; if (currentIndex < 0 || currentIndex >= m_capacity) { unsafeMigrate(index); currentIndex = m_offset + index; } if (!m_buffer[currentIndex].fetchAndAddRelaxed(1)) { if (m_min > index) m_min = index; if (m_max < index) m_max = index; ++m_count; } } void KisTiledExtentManager::Data::unsafeMigrate(qint32 index) { qint32 oldCapacity = m_capacity; qint32 oldOffset = m_offset; qint32 currentIndex = m_offset + index; while (currentIndex < 0 || currentIndex >= m_capacity) { m_capacity <<= 1; if (currentIndex < 0) { m_offset <<= 1; currentIndex = m_offset + index; } } if (m_capacity != oldCapacity) { QAtomicInt *newBuffer = new QAtomicInt[m_capacity]; qint32 start = m_offset - oldOffset; for (qint32 i = 0; i < oldCapacity; ++i) { newBuffer[start + i].store(m_buffer[i].load()); } delete[] m_buffer; m_buffer = newBuffer; } } void KisTiledExtentManager::Data::migrate(qint32 index) { QWriteLocker lock(&m_migrationLock); unsafeMigrate(index); } void KisTiledExtentManager::Data::updateMin() { qint32 start = m_min + m_offset; for (qint32 i = start; i < m_capacity; ++i) { qint32 current = m_buffer[i].load(); if (current > 0) { m_min = i - m_offset; break; } } } void KisTiledExtentManager::Data::updateMax() { qint32 start = m_max + m_offset; for (qint32 i = start; i >= 0; --i) { qint32 current = m_buffer[i].load(); if (current > 0) { m_max = i - m_offset; break; } } } KisTiledExtentManager::KisTiledExtentManager() { QWriteLocker l(&m_extentLock); - m_currentExtent = QRect(qint32_MAX, qint32_MAX, 0, 0); + m_currentExtent = QRect(); } void KisTiledExtentManager::notifyTileAdded(qint32 col, qint32 row) { bool needsUpdateExtent = false; needsUpdateExtent |= m_colsData.add(col); needsUpdateExtent |= m_rowsData.add(row); if (needsUpdateExtent) { updateExtent(); } } void KisTiledExtentManager::notifyTileRemoved(qint32 col, qint32 row) { bool needsUpdateExtent = false; needsUpdateExtent |= m_colsData.remove(col); needsUpdateExtent |= m_rowsData.remove(row); if (needsUpdateExtent) { updateExtent(); } } void KisTiledExtentManager::replaceTileStats(const QVector &indexes) { QVector colsIndexes; QVector rowsIndexes; Q_FOREACH (const QPoint &index, indexes) { colsIndexes.append(index.x()); rowsIndexes.append(index.y()); } m_colsData.replace(colsIndexes); m_rowsData.replace(rowsIndexes); updateExtent(); } void KisTiledExtentManager::clear() { m_colsData.clear(); m_rowsData.clear(); QWriteLocker lock(&m_extentLock); - m_currentExtent = QRect(qint32_MAX, qint32_MAX, 0, 0); + m_currentExtent = QRect(); } QRect KisTiledExtentManager::extent() const { QReadLocker lock(&m_extentLock); return m_currentExtent; } void KisTiledExtentManager::updateExtent() { - qint32 minX, maxX, minY, maxY; + qint32 minX, width, minY, height; { QReadLocker cl(&m_colsData.m_extentLock); if (m_colsData.isEmpty()) { - minX = qint32_MAX; - maxX = 0; + minX = 0; + width = 0; } else { minX = m_colsData.min() * KisTileData::WIDTH; - maxX = (m_colsData.max() + 1) * KisTileData::WIDTH - minX; + width = (m_colsData.max() + 1) * KisTileData::WIDTH - minX; } } { QReadLocker rl(&m_rowsData.m_extentLock); if (m_rowsData.isEmpty()) { - minY = qint32_MAX; - maxY = 0; + minY = 0; + height = 0; } else { minY = m_rowsData.min() * KisTileData::HEIGHT; - maxY = (m_rowsData.max() + 1) * KisTileData::HEIGHT - minY; + height = (m_rowsData.max() + 1) * KisTileData::HEIGHT - minY; } } QWriteLocker lock(&m_extentLock); - m_currentExtent = QRect(minX, minY, maxX, maxY); + m_currentExtent = QRect(minX, minY, width, height); } diff --git a/libs/image/tiles3/kis_memento.h b/libs/image/tiles3/kis_memento.h index 1e561bcfca..bf70301a15 100644 --- a/libs/image/tiles3/kis_memento.h +++ b/libs/image/tiles3/kis_memento.h @@ -1,112 +1,122 @@ /* * Copyright (c) 2005 C. Boemann * (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MEMENTO_H_ #define KIS_MEMENTO_H_ #include #include #include "kis_global.h" #include #include class KisMementoManager; class KisMemento; typedef KisSharedPtr KisMementoSP; class KisMemento : public KisShared { public: inline KisMemento(KisMementoManager* /*mementoManager*/) { m_extentMinX = qint32_MAX; m_extentMinY = qint32_MAX; m_extentMaxX = qint32_MIN; m_extentMaxY = qint32_MIN; m_oldDefaultPixel = 0; m_newDefaultPixel = 0; } inline ~KisMemento() { delete[] m_oldDefaultPixel; delete[] m_newDefaultPixel; } inline void extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) { - x = m_extentMinX; - y = m_extentMinY; - w = (m_extentMaxX >= m_extentMinX) ? m_extentMaxX - m_extentMinX + 1 : 0; - h = (m_extentMaxY >= m_extentMinY) ? m_extentMaxY - m_extentMinY + 1 : 0; + const bool extentIsValid = + m_extentMaxX >= m_extentMinX && m_extentMaxY >= m_extentMinY; + + if (extentIsValid) { + x = m_extentMinX; + y = m_extentMinY; + w = m_extentMaxX - m_extentMinX + 1; + h = m_extentMaxY - m_extentMinY + 1; + } else { + x = 0; + y = 0; + w = 0; + h = 0; + } } inline QRect extent() { qint32 x, y, w, h; extent(x, y, w, h); return QRect(x, y, w, h); } void saveOldDefaultPixel(const quint8* pixel, quint32 pixelSize) { m_oldDefaultPixel = new quint8[pixelSize]; memcpy(m_oldDefaultPixel, pixel, pixelSize); } void saveNewDefaultPixel(const quint8* pixel, quint32 pixelSize) { m_newDefaultPixel = new quint8[pixelSize]; memcpy(m_newDefaultPixel, pixel, pixelSize); } const quint8* oldDefaultPixel() const { return m_oldDefaultPixel; } const quint8* newDefaultPixel() const { return m_newDefaultPixel; } private: friend class KisMementoManager; inline void updateExtent(qint32 col, qint32 row) { const qint32 tileMinX = col * KisTileData::WIDTH; const qint32 tileMinY = row * KisTileData::HEIGHT; const qint32 tileMaxX = tileMinX + KisTileData::WIDTH - 1; const qint32 tileMaxY = tileMinY + KisTileData::HEIGHT - 1; m_extentMinX = qMin(m_extentMinX, tileMinX); m_extentMaxX = qMax(m_extentMaxX, tileMaxX); m_extentMinY = qMin(m_extentMinY, tileMinY); m_extentMaxY = qMax(m_extentMaxY, tileMaxY); } private: quint8 *m_oldDefaultPixel; quint8 *m_newDefaultPixel; qint32 m_extentMinX; qint32 m_extentMaxX; qint32 m_extentMinY; qint32 m_extentMaxY; }; #endif // KIS_MEMENTO_H_ diff --git a/libs/image/tiles3/tests/kis_tiled_data_manager_test.cpp b/libs/image/tiles3/tests/kis_tiled_data_manager_test.cpp index 96c399a7ec..22894c3964 100644 --- a/libs/image/tiles3/tests/kis_tiled_data_manager_test.cpp +++ b/libs/image/tiles3/tests/kis_tiled_data_manager_test.cpp @@ -1,947 +1,947 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tiled_data_manager_test.h" #include #include "tiles3/kis_tiled_data_manager.h" #include "tiles_test_utils.h" #include "config-limit-long-tests.h" bool KisTiledDataManagerTest::checkHole(quint8* buffer, quint8 holeColor, QRect holeRect, quint8 backgroundColor, QRect backgroundRect) { for(qint32 y = backgroundRect.y(); y <= backgroundRect.bottom(); y++) { for(qint32 x = backgroundRect.x(); x <= backgroundRect.right(); x++) { quint8 expectedColor = holeRect.contains(x,y) ? holeColor : backgroundColor; if(*buffer != expectedColor) { qDebug() << "Expected" << expectedColor << "but found" << *buffer; return false; } buffer++; } } return true; } bool KisTiledDataManagerTest::checkTilesShared(KisTiledDataManager *srcDM, KisTiledDataManager *dstDM, bool takeOldSrc, bool takeOldDst, QRect tilesRect) { for(qint32 row = tilesRect.y(); row <= tilesRect.bottom(); row++) { for(qint32 col = tilesRect.x(); col <= tilesRect.right(); col++) { KisTileSP srcTile = takeOldSrc ? srcDM->getOldTile(col, row) : srcDM->getTile(col, row, false); KisTileSP dstTile = takeOldDst ? dstDM->getOldTile(col, row) : dstDM->getTile(col, row, false); if(srcTile->tileData() != dstTile->tileData()) { qDebug() << "Expected tile data (" << col << row << ")" << srcTile->extent() << srcTile->tileData() << "but found" << dstTile->tileData(); qDebug() << "Expected" << srcTile->data()[0] << "but found" << dstTile->data()[0]; return false; } } } return true; } bool KisTiledDataManagerTest::checkTilesNotShared(KisTiledDataManager *srcDM, KisTiledDataManager *dstDM, bool takeOldSrc, bool takeOldDst, QRect tilesRect) { for(qint32 row = tilesRect.y(); row <= tilesRect.bottom(); row++) { for(qint32 col = tilesRect.x(); col <= tilesRect.right(); col++) { KisTileSP srcTile = takeOldSrc ? srcDM->getOldTile(col, row) : srcDM->getTile(col, row, false); KisTileSP dstTile = takeOldDst ? dstDM->getOldTile(col, row) : dstDM->getTile(col, row, false); if(srcTile->tileData() == dstTile->tileData()) { qDebug() << "Expected tiles not be shared:"<< srcTile->extent(); return false; } } } return true; } void KisTiledDataManagerTest::testUndoingNewTiles() { // "growing extent bug" - const QRect nullRect(qint32_MAX,qint32_MAX,0,0); + const QRect nullRect; quint8 defaultPixel = 0; KisTiledDataManager srcDM(1, &defaultPixel); KisTileSP emptyTile = srcDM.getTile(0, 0, false); QCOMPARE(srcDM.extent(), nullRect); KisMementoSP memento0 = srcDM.getMemento(); KisTileSP createdTile = srcDM.getTile(0, 0, true); srcDM.commit(); QCOMPARE(srcDM.extent(), QRect(0,0,64,64)); srcDM.rollback(memento0); QCOMPARE(srcDM.extent(), nullRect); } void KisTiledDataManagerTest::testPurgedAndEmptyTransactions() { quint8 defaultPixel = 0; KisTiledDataManager srcDM(1, &defaultPixel); quint8 oddPixel1 = 128; QRect rect(0,0,512,512); QRect clearRect1(50,50,100,100); QRect clearRect2(150,50,100,100); quint8 *buffer = new quint8[rect.width()*rect.height()]; // purged transaction KisMementoSP memento0 = srcDM.getMemento(); srcDM.clear(clearRect1, &oddPixel1); srcDM.purgeHistory(memento0); memento0 = 0; srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, clearRect1, defaultPixel, rect)); // one more purged transaction KisMementoSP memento1 = srcDM.getMemento(); srcDM.clear(clearRect2, &oddPixel1); srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2, defaultPixel, rect)); srcDM.purgeHistory(memento1); memento1 = 0; srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2, defaultPixel, rect)); // empty one KisMementoSP memento2 = srcDM.getMemento(); srcDM.commit(); srcDM.rollback(memento2); srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2, defaultPixel, rect)); // now check that everything works still KisMementoSP memento3 = srcDM.getMemento(); srcDM.setExtent(clearRect2); srcDM.commit(); srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, clearRect2, defaultPixel, rect)); srcDM.rollback(memento3); srcDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, clearRect1 | clearRect2, defaultPixel, rect)); } void KisTiledDataManagerTest::testUnversionedBitBlt() { quint8 defaultPixel = 0; KisTiledDataManager srcDM(1, &defaultPixel); KisTiledDataManager dstDM(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; QRect rect(0,0,512,512); QRect cloneRect(81,80,250,250); QRect tilesRect(2,2,3,3); srcDM.clear(rect, &oddPixel1); dstDM.clear(rect, &oddPixel2); dstDM.bitBlt(&srcDM, cloneRect); quint8 *buffer = new quint8[rect.width()*rect.height()]; dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, cloneRect, oddPixel2, rect)); delete[] buffer; // Test whether tiles became shared QVERIFY(checkTilesShared(&srcDM, &dstDM, false, false, tilesRect)); } void KisTiledDataManagerTest::testVersionedBitBlt() { quint8 defaultPixel = 0; KisTiledDataManager srcDM1(1, &defaultPixel); KisTiledDataManager srcDM2(1, &defaultPixel); KisTiledDataManager dstDM(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; quint8 oddPixel3 = 130; quint8 oddPixel4 = 131; QRect rect(0,0,512,512); QRect cloneRect(81,80,250,250); QRect tilesRect(2,2,3,3); KisMementoSP memento1 = srcDM1.getMemento(); srcDM1.clear(rect, &oddPixel1); srcDM2.clear(rect, &oddPixel2); dstDM.clear(rect, &oddPixel3); KisMementoSP memento2 = dstDM.getMemento(); dstDM.bitBlt(&srcDM1, cloneRect); QVERIFY(checkTilesShared(&srcDM1, &dstDM, false, false, tilesRect)); QVERIFY(checkTilesNotShared(&srcDM1, &srcDM1, true, false, tilesRect)); QVERIFY(checkTilesNotShared(&dstDM, &dstDM, true, false, tilesRect)); dstDM.commit(); QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect)); KisMementoSP memento3 = srcDM2.getMemento(); srcDM2.clear(rect, &oddPixel4); KisMementoSP memento4 = dstDM.getMemento(); dstDM.bitBlt(&srcDM2, cloneRect); QVERIFY(checkTilesShared(&srcDM2, &dstDM, false, false, tilesRect)); QVERIFY(checkTilesNotShared(&srcDM2, &srcDM2, true, false, tilesRect)); QVERIFY(checkTilesNotShared(&dstDM, &dstDM, true, false, tilesRect)); dstDM.commit(); QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect)); dstDM.rollback(memento4); QVERIFY(checkTilesShared(&srcDM1, &dstDM, false, false, tilesRect)); QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect)); QVERIFY(checkTilesNotShared(&srcDM1, &srcDM1, true, false, tilesRect)); dstDM.rollforward(memento4); QVERIFY(checkTilesShared(&srcDM2, &dstDM, false, false, tilesRect)); QVERIFY(checkTilesShared(&dstDM, &dstDM, true, false, tilesRect)); QVERIFY(checkTilesNotShared(&srcDM1, &srcDM1, true, false, tilesRect)); } void KisTiledDataManagerTest::testBitBltOldData() { quint8 defaultPixel = 0; KisTiledDataManager srcDM(1, &defaultPixel); KisTiledDataManager dstDM(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; QRect rect(0,0,512,512); QRect cloneRect(81,80,250,250); QRect tilesRect(2,2,3,3); quint8 *buffer = new quint8[rect.width()*rect.height()]; KisMementoSP memento1 = srcDM.getMemento(); srcDM.clear(rect, &oddPixel1); srcDM.commit(); dstDM.bitBltOldData(&srcDM, cloneRect); dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, cloneRect, defaultPixel, rect)); KisMementoSP memento2 = srcDM.getMemento(); srcDM.clear(rect, &oddPixel2); dstDM.bitBltOldData(&srcDM, cloneRect); srcDM.commit(); dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, cloneRect, defaultPixel, rect)); delete[] buffer; } void KisTiledDataManagerTest::testBitBltRough() { quint8 defaultPixel = 0; KisTiledDataManager srcDM(1, &defaultPixel); KisTiledDataManager dstDM(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; quint8 oddPixel3 = 130; QRect rect(0,0,512,512); QRect cloneRect(81,80,250,250); QRect actualCloneRect(64,64,320,320); QRect tilesRect(1,1,4,4); srcDM.clear(rect, &oddPixel1); dstDM.clear(rect, &oddPixel2); dstDM.bitBltRough(&srcDM, cloneRect); quint8 *buffer = new quint8[rect.width()*rect.height()]; dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, actualCloneRect, oddPixel2, rect)); // Test whether tiles became shared QVERIFY(checkTilesShared(&srcDM, &dstDM, false, false, tilesRect)); // check bitBltRoughOldData KisMementoSP memento1 = srcDM.getMemento(); srcDM.clear(rect, &oddPixel3); dstDM.bitBltRoughOldData(&srcDM, cloneRect); srcDM.commit(); dstDM.readBytes(buffer, rect.x(), rect.y(), rect.width(), rect.height()); QVERIFY(checkHole(buffer, oddPixel1, actualCloneRect, oddPixel2, rect)); delete[] buffer; } void KisTiledDataManagerTest::testTransactions() { quint8 defaultPixel = 0; KisTiledDataManager dm(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; quint8 oddPixel3 = 130; KisTileSP tile00; KisTileSP oldTile00; // Create a named transaction: versioning is enabled KisMementoSP memento1 = dm.getMemento(); dm.clear(0, 0, 64, 64, &oddPixel1); tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(defaultPixel, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; // Create an anonymous transaction: versioning is disabled dm.commit(); tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel1, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; dm.clear(0, 0, 64, 64, &oddPixel2); // Versioning is disabled, i said! >:) tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel2, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; // And the last round: named transaction: KisMementoSP memento2 = dm.getMemento(); dm.clear(0, 0, 64, 64, &oddPixel3); tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel3, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel2, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; } void KisTiledDataManagerTest::testPurgeHistory() { quint8 defaultPixel = 0; KisTiledDataManager dm(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; quint8 oddPixel3 = 130; quint8 oddPixel4 = 131; KisMementoSP memento1 = dm.getMemento(); dm.clear(0, 0, 64, 64, &oddPixel1); dm.commit(); KisMementoSP memento2 = dm.getMemento(); dm.clear(0, 0, 64, 64, &oddPixel2); KisTileSP tile00; KisTileSP oldTile00; tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel1, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; dm.purgeHistory(memento1); /** * Nothing nas changed in the visible state of the data manager */ tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel1, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; dm.commit(); dm.purgeHistory(memento2); /** * We've removed all the history of the device, so it * became "unversioned". * NOTE: the return value for getOldTile() when there is no * history present is a subject for change */ tile00 = dm.getTile(0, 0, false); oldTile00 = dm.getOldTile(0, 0); QVERIFY(memoryIsFilled(oddPixel2, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel2, oldTile00->data(), TILESIZE)); tile00 = oldTile00 = 0; /** * Just test we won't crash when the memento is not * present in history anymore */ KisMementoSP memento3 = dm.getMemento(); dm.clear(0, 0, 64, 64, &oddPixel3); dm.commit(); KisMementoSP memento4 = dm.getMemento(); dm.clear(0, 0, 64, 64, &oddPixel4); dm.commit(); dm.rollback(memento4); dm.purgeHistory(memento3); dm.purgeHistory(memento4); } void KisTiledDataManagerTest::testUndoSetDefaultPixel() { quint8 defaultPixel = 0; KisTiledDataManager dm(1, &defaultPixel); quint8 oddPixel1 = 128; quint8 oddPixel2 = 129; QRect fillRect(0,0,64,64); KisTileSP tile00; KisTileSP tile10; tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(defaultPixel, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE)); KisMementoSP memento1 = dm.getMemento(); dm.clear(fillRect, &oddPixel1); dm.commit(); tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE)); KisMementoSP memento2 = dm.getMemento(); dm.setDefaultPixel(&oddPixel2); dm.commit(); tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel2, tile10->data(), TILESIZE)); dm.rollback(memento2); tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE)); dm.rollback(memento1); tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(defaultPixel, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE)); dm.rollforward(memento1); tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(defaultPixel, tile10->data(), TILESIZE)); dm.rollforward(memento2); tile00 = dm.getTile(0, 0, false); tile10 = dm.getTile(1, 0, false); QVERIFY(memoryIsFilled(oddPixel1, tile00->data(), TILESIZE)); QVERIFY(memoryIsFilled(oddPixel2, tile10->data(), TILESIZE)); } //#include void KisTiledDataManagerTest::benchmarkReadOnlyTileLazy() { quint8 defaultPixel = 0; KisTiledDataManager dm(1, &defaultPixel); /* * See KisTileHashTableTraits2 for more details */ const qint32 numTilesToTest = 0x7fff; //CALLGRIND_START_INSTRUMENTATION; QBENCHMARK_ONCE { for(qint32 i = 0; i < numTilesToTest; i++) { KisTileSP tile = dm.getTile(i, i, false); } } //CALLGRIND_STOP_INSTRUMENTATION; } class KisSimpleClass : public KisShared { qint64 m_int; public: KisSimpleClass() { Q_UNUSED(m_int); } }; typedef KisSharedPtr KisSimpleClassSP; void KisTiledDataManagerTest::benchmarkSharedPointers() { const qint32 numIterations = 2 * 1000000; //CALLGRIND_START_INSTRUMENTATION; QBENCHMARK_ONCE { for(qint32 i = 0; i < numIterations; i++) { KisSimpleClassSP pointer = new KisSimpleClass; pointer = 0; } } //CALLGRIND_STOP_INSTRUMENTATION; } void KisTiledDataManagerTest::benchmarkCOWImpl() { const int pixelSize = 8; quint8 defaultPixel[pixelSize]; memset(defaultPixel, 1, pixelSize); KisTiledDataManager dm(pixelSize, defaultPixel); KisMementoSP memento1 = dm.getMemento(); /** * Imagine a regular image of 4096x2048 pixels * (64x32 tiles) */ for (int i = 0; i < 32; i++) { for (int j = 0; j < 64; j++) { KisTileSP tile = dm.getTile(j, i, true); tile->lockForWrite(); tile->unlockForWrite(); } } dm.commit(); QTest::qSleep(200); KisMementoSP memento2 = dm.getMemento(); QTest::qSleep(200); QBENCHMARK_ONCE { for (int i = 0; i < 32; i++) { for (int j = 0; j < 64; j++) { KisTileSP tile = dm.getTile(j, i, true); tile->lockForWrite(); tile->unlockForWrite(); } } } dm.commit(); } void KisTiledDataManagerTest::benchmarkCOWNoPooler() { KisTileDataStore::instance()->testingSuspendPooler(); QTest::qSleep(200); benchmarkCOWImpl(); KisTileDataStore::instance()->testingResumePooler(); QTest::qSleep(200); } void KisTiledDataManagerTest::benchmarkCOWWithPooler() { benchmarkCOWImpl(); } /******************* Stress job ***********************/ #ifdef LIMIT_LONG_TESTS #define NUM_CYCLES 10000 #else #define NUM_CYCLES 100000 #endif #define NUM_TYPES 12 #define TILE_DIMENSION 64 /** * The data manager has partial guarantees of reentrancy. That is * you can call any arbitrary number of methods concurrently as long * as their access areas do not intersect. * * Though the rule can be quite tricky -- some of the methods always * use entire image as their access area, so they cannot be called * concurrently in any circumstances. * The examples are: clear(), commit(), rollback() and etc... */ #define run_exclusive(lock, _i) for(_i = 0, (lock).lockForWrite(); _i < 1; _i++, (lock).unlock()) #define run_concurrent(lock, _i) for(_i = 0, (lock).lockForRead(); _i < 1; _i++, (lock).unlock()) //#define run_exclusive(lock, _i) while(0) //#define run_concurrent(lock, _i) while(0) class KisStressJob : public QRunnable { public: KisStressJob(KisTiledDataManager &dataManager, QRect rect, QReadWriteLock &_lock) : m_accessRect(rect), dm(dataManager), lock(_lock) { } void run() override { qsrand(QTime::currentTime().msec()); for(qint32 i = 0; i < NUM_CYCLES; i++) { qint32 type = qrand() % NUM_TYPES; qint32 t; switch(type) { case 0: run_concurrent(lock,t) { quint8 *buf; buf = new quint8[dm.pixelSize()]; memcpy(buf, dm.defaultPixel(), dm.pixelSize()); dm.setDefaultPixel(buf); delete[] buf; } break; case 1: case 2: run_concurrent(lock,t) { KisTileSP tile; tile = dm.getTile(m_accessRect.x() / TILE_DIMENSION, m_accessRect.y() / TILE_DIMENSION, false); tile->lockForRead(); tile->unlockForRead(); tile = dm.getTile(m_accessRect.x() / TILE_DIMENSION, m_accessRect.y() / TILE_DIMENSION, true); tile->lockForWrite(); tile->unlockForWrite(); tile = dm.getOldTile(m_accessRect.x() / TILE_DIMENSION, m_accessRect.y() / TILE_DIMENSION); tile->lockForRead(); tile->unlockForRead(); } break; case 3: run_concurrent(lock,t) { QRect newRect = dm.extent(); Q_UNUSED(newRect); } break; case 4: run_concurrent(lock,t) { dm.clear(m_accessRect.x(), m_accessRect.y(), m_accessRect.width(), m_accessRect.height(), 4); } break; case 5: run_concurrent(lock,t) { quint8 *buf; buf = new quint8[m_accessRect.width() * m_accessRect.height() * dm.pixelSize()]; dm.readBytes(buf, m_accessRect.x(), m_accessRect.y(), m_accessRect.width(), m_accessRect.height()); dm.writeBytes(buf, m_accessRect.x(), m_accessRect.y(), m_accessRect.width(), m_accessRect.height()); delete[] buf; } break; case 6: run_concurrent(lock,t) { quint8 oddPixel = 13; KisTiledDataManager srcDM(1, &oddPixel); dm.bitBlt(&srcDM, m_accessRect); } break; case 7: case 8: run_exclusive(lock,t) { m_memento = dm.getMemento(); dm.clear(m_accessRect.x(), m_accessRect.y(), m_accessRect.width(), m_accessRect.height(), 2); dm.commit(); dm.rollback(m_memento); dm.rollforward(m_memento); dm.purgeHistory(m_memento); m_memento = 0; } break; case 9: run_exclusive(lock,t) { bool b = dm.hasCurrentMemento(); Q_UNUSED(b); } break; case 10: run_exclusive(lock,t) { dm.clear(); } break; case 11: run_exclusive(lock,t) { dm.setExtent(m_accessRect); } break; } } } private: KisMementoSP m_memento; QRect m_accessRect; KisTiledDataManager &dm; QReadWriteLock &lock; }; void KisTiledDataManagerTest::stressTest() { quint8 defaultPixel = 0; KisTiledDataManager dm(1, &defaultPixel); QReadWriteLock lock; #ifdef LIMIT_LONG_TESTS const int numThreads = 8; const int numWorkers = 8; #else const int numThreads = 16; const int numWorkers = 48; #endif QThreadPool pool; pool.setMaxThreadCount(numThreads); QRect accessRect(0,0,512,512); for(qint32 i = 0; i < numWorkers; i++) { KisStressJob *job = new KisStressJob(dm, accessRect, lock); pool.start(job); accessRect.translate(512, 0); } pool.waitForDone(); } template void applyToRect(const QRect &rc, Func func) { for (int y = rc.y(); y < rc.y() + rc.height(); y += KisTileData::HEIGHT) { for (int x = rc.x(); x < rc.x() + rc.width(); x += KisTileData::WIDTH) { const int col = x / KisTileData::WIDTH; const int row = y / KisTileData::HEIGHT; func(col, row); } } } class LazyCopyingStressJob : public QRunnable { public: LazyCopyingStressJob(KisTiledDataManager &dataManager, const QRect &rect, QReadWriteLock &dmExclusiveLock, QReadWriteLock &tileExclusiveLock, int numCycles, bool isWriter) : m_accessRect(rect), dm(dataManager), m_dmExclusiveLock(dmExclusiveLock), m_tileExclusiveLock(tileExclusiveLock), m_numCycles(numCycles), m_isWriter(isWriter) { } void run() override { for(qint32 i = 0; i < m_numCycles; i++) { //const int epoch = i % 100; int t; if (m_isWriter && 0) { } else { const bool shouldClear = i % 5 <= 1; // 40% of requests are clears const bool shouldWrite = i % 5 <= 3; // other 40% of requests are writes run_concurrent(m_dmExclusiveLock, t) { if (shouldClear) { QWriteLocker locker(&m_tileExclusiveLock); dm.clear(m_accessRect, 4); } else { auto readFunc = [this] (int col, int row) { KisTileSP tile = dm.getTile(col, row, false); tile->lockForRead(); tile->unlockForRead(); }; auto writeFunc = [this] (int col, int row) { KisTileSP tile = dm.getTile(col, row, true); tile->lockForWrite(); tile->unlockForWrite(); }; auto readOldFunc = [this] (int col, int row) { KisTileSP tile = dm.getOldTile(col, row); tile->lockForRead(); tile->unlockForRead(); }; applyToRect(m_accessRect, readFunc); if (shouldWrite) { QReadLocker locker(&m_tileExclusiveLock); applyToRect(m_accessRect, writeFunc); } applyToRect(m_accessRect, readOldFunc); } } } } } private: KisMementoSP m_memento; QRect m_accessRect; KisTiledDataManager &dm; QReadWriteLock &m_dmExclusiveLock; QReadWriteLock &m_tileExclusiveLock; const int m_numCycles; const bool m_isWriter; }; void KisTiledDataManagerTest::stressTestLazyCopying() { quint8 defaultPixel = 0; KisTiledDataManager dm(1, &defaultPixel); QReadWriteLock dmLock; QReadWriteLock tileLock; #ifdef LIMIT_LONG_TESTS const int numCycles = 10000; const int numThreads = 8; const int numWorkers = 8; #else const int numThreads = 16; const int numWorkers = 32; const int numCycles = 100000; #endif QThreadPool pool; pool.setMaxThreadCount(numThreads); const QRect accessRect(0,0,512,256); for(qint32 i = 0; i < numWorkers; i++) { const bool isWriter = i == 0; LazyCopyingStressJob *job = new LazyCopyingStressJob(dm, accessRect, dmLock, tileLock, numCycles, isWriter); pool.start(job); } pool.waitForDone(); } QTEST_MAIN(KisTiledDataManagerTest)