File indexing completed on 2024-12-22 04:10:13

0001 /*
0002  *  SPDX-FileCopyrightText: 2009 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_async_merger_test.h"
0008 
0009 #include "kis_merge_walker.h"
0010 #include "kis_full_refresh_walker.h"
0011 #include "kis_async_merger.h"
0012 
0013 #include <simpletest.h>
0014 #include <KoColorSpaceRegistry.h>
0015 #include <KoColorSpace.h>
0016 #include "kis_image.h"
0017 #include "kis_paint_layer.h"
0018 #include "kis_group_layer.h"
0019 #include "kis_clone_layer.h"
0020 #include "kis_adjustment_layer.h"
0021 #include "kis_filter_mask.h"
0022 #include "kis_selection.h"
0023 #include "kis_paint_device_debug_utils.h"
0024 #include <KisGlobalResourcesInterface.h>
0025 
0026 #include "filter/kis_filter.h"
0027 #include "filter/kis_filter_configuration.h"
0028 #include "filter/kis_filter_registry.h"
0029 
0030 #include "../../sdk/tests/testutil.h"
0031 
0032 #include "kis_image_config.h"
0033 #include "KisImageConfigNotifier.h"
0034 
0035 void KisAsyncMergerTest::init()
0036 {
0037     KisImageConfig::resetConfig();
0038 }
0039 
0040 
0041     /*
0042       +-----------+
0043       |root       |
0044       | group     |
0045       |  blur 1   |
0046       |  paint 2  |
0047       | paint 1   |
0048       +-----------+
0049      */
0050 
0051 void KisAsyncMergerTest::testMerger()
0052 {
0053     const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0054     KisImageSP image = new KisImage(0, 640, 441, colorSpace, "merger test");
0055 
0056     QImage sourceImage1(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
0057     QImage sourceImage2(QString(FILES_DATA_DIR) + '/' + "inverted_hakonepa.png");
0058     QImage referenceProjection(QString(FILES_DATA_DIR) + '/' + "merged_hakonepa.png");
0059 
0060     KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
0061     KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
0062     device1->convertFromQImage(sourceImage1, 0, 0, 0);
0063     device2->convertFromQImage(sourceImage2, 0, 0, 0);
0064 
0065     KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
0066     Q_ASSERT(filter);
0067     KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
0068     Q_ASSERT(configuration);
0069 
0070     KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
0071     KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
0072     KisLayerSP groupLayer = new KisGroupLayer(image, "group", 200/*OPACITY_OPAQUE*/);
0073     KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
0074 
0075     image->addNode(paintLayer1, image->rootLayer());
0076     image->addNode(groupLayer, image->rootLayer());
0077 
0078     image->addNode(paintLayer2, groupLayer);
0079     image->addNode(blur1, groupLayer);
0080 
0081     QRect testRect1(0,0,100,441);
0082     QRect testRect2(100,0,400,441);
0083     QRect testRect3(500,0,140,441);
0084     QRect testRect4(580,381,40,40);
0085 
0086     QRect cropRect(image->bounds());
0087 
0088     KisMergeWalker walker(cropRect);
0089     KisAsyncMerger merger;
0090 
0091     walker.collectRects(paintLayer2, testRect1);
0092     merger.startMerge(walker);
0093 
0094     walker.collectRects(paintLayer2, testRect2);
0095     merger.startMerge(walker);
0096 
0097     walker.collectRects(paintLayer2, testRect3);
0098     merger.startMerge(walker);
0099 
0100     walker.collectRects(paintLayer2, testRect4);
0101     merger.startMerge(walker);
0102 
0103     // Old style merging: has artifacts at x=100 and x=500
0104     // And should be turned on inside KisLayer
0105 /*    paintLayer2->setDirty(testRect1);
0106     QTest::qSleep(3000);
0107     paintLayer2->setDirty(testRect2);
0108     QTest::qSleep(3000);
0109     paintLayer2->setDirty(testRect3);
0110     QTest::qSleep(3000);
0111     paintLayer2->setDirty(testRect4);
0112     QTest::qSleep(3000);
0113 */
0114 
0115     KisLayerSP rootLayer = image->rootLayer();
0116     QVERIFY(rootLayer->exactBounds() == image->bounds());
0117 
0118     QImage resultProjection = rootLayer->projection()->convertToQImage(0);
0119     resultProjection.save(QString(FILES_OUTPUT_DIR) + '/' + "actual_merge_result.png");
0120     QPoint pt;
0121     QVERIFY(TestUtil::compareQImages(pt, resultProjection, referenceProjection, 5, 0, 0));
0122 }
0123 
0124 
0125 /**
0126  * This in not fully automated test for child obliging in KisAsyncMerger.
0127  * It just checks whether devices are shared. To check if the merger
0128  * touches originals you can add a debug message to the merger
0129  * and take a look.
0130  */
0131 
0132     /*
0133       +-----------+
0134       |root       |
0135       | group     |
0136       |  paint 1  |
0137       +-----------+
0138      */
0139 
0140 void KisAsyncMergerTest::debugObligeChild()
0141 {
0142     const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0143     KisImageSP image = new KisImage(0, 640, 441, colorSpace, "merger test");
0144 
0145     QImage sourceImage1(QString(FILES_DATA_DIR) + '/' + "hakonepa.png");
0146     KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
0147     device1->convertFromQImage(sourceImage1, 0, 0, 0);
0148 
0149     KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
0150     KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
0151 
0152     image->addNode(groupLayer, image->rootLayer());
0153     image->addNode(paintLayer1, groupLayer);
0154 
0155     QRect testRect1(0,0,640,441);
0156     QRect cropRect(image->bounds());
0157 
0158     KisMergeWalker walker(cropRect);
0159     KisAsyncMerger merger;
0160 
0161     walker.collectRects(paintLayer1, testRect1);
0162     merger.startMerge(walker);
0163 
0164     KisLayerSP rootLayer = image->rootLayer();
0165     QVERIFY(rootLayer->original() == groupLayer->projection());
0166     QVERIFY(groupLayer->original() == paintLayer1->projection());
0167 }
0168 
0169     /*
0170       +--------------+
0171       |root          |
0172       | paint 1      |
0173       |  invert_mask |
0174       | clone_of_1   |
0175       +--------------+
0176      */
0177 
0178 void KisAsyncMergerTest::testFullRefreshWithClones()
0179 {
0180     const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0181     KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
0182 
0183     KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
0184     device1->fill(image->bounds(), KoColor( Qt::white, colorSpace));
0185 
0186     KisFilterSP filter = KisFilterRegistry::instance()->value("invert");
0187     Q_ASSERT(filter);
0188     KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
0189     Q_ASSERT(configuration);
0190 
0191     KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
0192     KisFilterMaskSP invertMask1 = new KisFilterMask(image, "invert_mask");
0193     invertMask1->initSelection(paintLayer1);
0194     invertMask1->setFilter(configuration->cloneWithResourcesSnapshot());
0195 
0196     KisLayerSP cloneLayer1 = new KisCloneLayer(paintLayer1, image, "clone_of_1", OPACITY_OPAQUE_U8);
0197     /**
0198      * The clone layer must have a projection to allow us
0199      * to read what it got from its source. Just shift it.
0200      */
0201     cloneLayer1->setX(10);
0202     cloneLayer1->setY(10);
0203 
0204     image->addNode(cloneLayer1, image->rootLayer());
0205     image->addNode(paintLayer1, image->rootLayer());
0206     image->addNode(invertMask1, paintLayer1);
0207 
0208     QRect cropRect(image->bounds());
0209 
0210     KisFullRefreshWalker walker(cropRect);
0211     KisAsyncMerger merger;
0212 
0213     walker.collectRects(image->rootLayer(), image->bounds());
0214     merger.startMerge(walker);
0215 
0216     // Wait for additional jobs generated by the clone are finished
0217     image->waitForDone();
0218 
0219     QRect filledRect(10, 10,
0220                      image->width() - cloneLayer1->x(),
0221                      image->height() - cloneLayer1->y());
0222 
0223     const int pixelSize = device1->pixelSize();
0224     const int numPixels = filledRect.width() * filledRect.height();
0225 
0226     QByteArray bytes(numPixels * pixelSize, 13);
0227     cloneLayer1->projection()->readBytes((quint8*)bytes.data(), filledRect);
0228 
0229     KoColor desiredPixel(Qt::black, colorSpace);
0230     quint8 *srcPtr = (quint8*)bytes.data();
0231     quint8 *dstPtr = desiredPixel.data();
0232     for(int i = 0; i < numPixels; i++) {
0233         if(memcmp(srcPtr, dstPtr, pixelSize)) {
0234             dbgKrita << "expected:" << dstPtr[0] << dstPtr[1] << dstPtr[2] << dstPtr[3];
0235             dbgKrita << "result:  " << srcPtr[0] << srcPtr[1] << srcPtr[2] << srcPtr[3];
0236             QFAIL("Failed to compare pixels");
0237         }
0238         srcPtr += pixelSize;
0239     }
0240 }
0241 
0242     /*
0243       +--------------+
0244       |root          |
0245       | paint 2      |
0246       | paint 1      |
0247       +--------------+
0248      */
0249 
0250 void KisAsyncMergerTest::testSubgraphingWithoutUpdatingParent()
0251 {
0252     const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0253     KisImageSP image = new KisImage(0, 128, 128, colorSpace, "clones test");
0254 
0255     KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
0256     device1->fill(image->bounds(), KoColor(Qt::white, colorSpace));
0257     KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
0258 
0259     KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
0260     device2->fill(image->bounds(), KoColor(Qt::black, colorSpace));
0261     KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", 128, device2);
0262 
0263     image->addNode(paintLayer1, image->rootLayer());
0264     image->addNode(paintLayer2, image->rootLayer());
0265 
0266     image->initialRefreshGraph();
0267 
0268     QImage refImage(QString(FILES_DATA_DIR) + '/' + "subgraphing_without_updating.png");
0269 
0270     {
0271         QImage resultImage = image->projection()->convertToQImage(0);
0272         QCOMPARE(resultImage, refImage);
0273     }
0274 
0275     QRect cropRect(image->bounds());
0276 
0277     KisRefreshSubtreeWalker walker(cropRect);
0278     KisAsyncMerger merger;
0279 
0280     walker.collectRects(paintLayer2, image->bounds());
0281     merger.startMerge(walker);
0282 
0283     {
0284         QImage resultImage = image->projection()->convertToQImage(0);
0285         QCOMPARE(resultImage, refImage);
0286     }
0287 }
0288 
0289 
0290 #include <KoCompositeOpRegistry.h>
0291 
0292 enum DependentNodeType {
0293     GROUP_LAYER,
0294     ADJUSTMENT_LAYER
0295 };
0296 
0297 /*
0298   +--------------+
0299   |root          |
0300   | (blur1)      |
0301   | group        |
0302   |  blur_mask   |
0303   |  paint 2     |
0304   |  paint 3     |
0305   | paint 1      |
0306   +--------------+
0307  */
0308 void testFullRefreshForDependentNodes(const DependentNodeType dependentNode,
0309                                       const bool useFilterMask,
0310                                       const bool useLayerStyle)
0311 {
0312     const QRect imageRect = QRect(0, 0, 128, 128);
0313     const QRect fillRect = QRect(32, 32, 64, 64);
0314     const QRect smallRect = QRect(60, 60, 8, 8);
0315 
0316     const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0317     KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), colorSpace, "blur test");
0318 
0319     KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
0320 
0321     if (dependentNode == GROUP_LAYER && !useLayerStyle) {
0322         device1->fill(imageRect, KoColor(Qt::black, colorSpace));
0323     }
0324 
0325     KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
0326 
0327     KisPaintDeviceSP device2 = new KisPaintDevice(colorSpace);
0328     device2->fill(fillRect, KoColor(Qt::white, colorSpace));
0329     KisLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8, device2);
0330 
0331     device2->fill(QRect(63, 0, 1, 64), KoColor(Qt::green, colorSpace));
0332 
0333     KisPaintDeviceSP device3 = new KisPaintDevice(colorSpace);
0334     device3->fill(smallRect, KoColor(Qt::red, colorSpace));
0335     KisLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8, device3);
0336 
0337     KisLayerSP groupLayer = new KisGroupLayer(image, "group", OPACITY_OPAQUE_U8);
0338 
0339 
0340     image->addNode(paintLayer1, image->rootLayer());
0341     image->addNode(groupLayer, image->rootLayer());
0342     image->addNode(paintLayer2, groupLayer);
0343     image->addNode(paintLayer3, groupLayer);
0344 
0345     KisLayerSP testingLayer = groupLayer;
0346 
0347     if (dependentNode == ADJUSTMENT_LAYER) {
0348         KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
0349         KIS_ASSERT(filter);
0350         KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
0351         KIS_ASSERT(configuration);
0352 
0353         KisLayerSP blur1 = new KisAdjustmentLayer(image, "blur1", configuration->cloneWithResourcesSnapshot(), 0);
0354         blur1->setCompositeOpId(COMPOSITE_OVER);
0355 
0356         image->addNode(blur1, image->rootLayer());
0357     }
0358 
0359     if (useFilterMask) {
0360         KisFilterSP filter = KisFilterRegistry::instance()->value("blur");
0361         KIS_ASSERT(filter);
0362         KisFilterConfigurationSP configuration = filter->defaultConfiguration(KisGlobalResourcesInterface::instance());
0363         KIS_ASSERT(configuration);
0364 
0365         KisFilterMaskSP blurMask1 = new KisFilterMask(image, "blur_mask");
0366         blurMask1->initSelection(groupLayer);
0367         blurMask1->setFilter(configuration->cloneWithResourcesSnapshot());
0368         image->addNode(blurMask1, testingLayer);
0369     }
0370 
0371     if (useLayerStyle) {
0372         KisPSDLayerStyleSP style(new KisPSDLayerStyle());
0373 
0374         style->context()->keep_original = true;
0375         style->dropShadow()->setEffectEnabled(true);
0376         style->dropShadow()->setDistance(9);
0377         style->dropShadow()->setSpread(100);
0378         style->dropShadow()->setSize(9);
0379         style->dropShadow()->setNoise(0);
0380         style->dropShadow()->setKnocksOut(false);
0381         style->dropShadow()->setOpacity(90);
0382         style->dropShadow()->setAngle(0);
0383 
0384         testingLayer->setLayerStyle(style);
0385     }
0386 
0387 
0388     QRect cropRect(image->bounds());
0389 
0390     KisFullRefreshWalker walker(cropRect);
0391     KisAsyncMerger merger;
0392 
0393 
0394     QVector<QRect> patchRects;
0395     patchRects << QRect(0,0,64,64);
0396     patchRects << QRect(64,0,64,64);
0397     patchRects << QRect(0,64,64,64);
0398     patchRects << QRect(64,64,64,64);
0399 
0400     Q_FOREACH(const QRect &rc, patchRects) {
0401         walker.collectRects(image->rootLayer(), rc);
0402         merger.startMerge(walker);
0403     }
0404 
0405     // Wait for additional jobs generated by the clone are finished
0406     image->waitForDone();
0407 
0408     QString testName = dependentNode == GROUP_LAYER ? "group" : "adj";
0409 
0410     if (useFilterMask) {
0411         testName += "_mask";
0412     }
0413 
0414     if (useLayerStyle) {
0415         testName += "_style";
0416     }
0417 
0418     TestUtil::checkQImage(image->projection()->convertToQImage(0, imageRect),
0419                           "async_merger_test", "dependent", testName, 3);
0420 }
0421 
0422 void KisAsyncMergerTest::testFullRefreshGroupWithMask()
0423 {
0424     testFullRefreshForDependentNodes(GROUP_LAYER, true, false);
0425 }
0426 
0427 void KisAsyncMergerTest::testFullRefreshGroupWithStyle()
0428 {
0429     testFullRefreshForDependentNodes(GROUP_LAYER, false, true);
0430 }
0431 
0432 void KisAsyncMergerTest::testFullRefreshGroupWithMaskAndStyle()
0433 {
0434     testFullRefreshForDependentNodes(GROUP_LAYER, true, true);
0435 }
0436 
0437 void KisAsyncMergerTest::testFullRefreshAdjustmentWithMask()
0438 {
0439     testFullRefreshForDependentNodes(ADJUSTMENT_LAYER, true, false);
0440 }
0441 
0442 void KisAsyncMergerTest::testFullRefreshAdjustmentWithStyle()
0443 {
0444     testFullRefreshForDependentNodes(ADJUSTMENT_LAYER, false, true);
0445 }
0446 
0447 /*
0448   +---------------------------------+
0449   |root                             |
0450   | adj 2 (filter 2, color balance) |
0451   |  mask 3 (filter 3, blur)        |
0452   | paint 1                         |
0453   +---------------------------------+
0454  */
0455 
0456 void KisAsyncMergerTest::testFilterMaskOnFilterLayer()
0457 {
0458     {
0459         KisImageConfig cfg(false);
0460         cfg.setUpdatePatchWidth(64);
0461         cfg.setUpdatePatchHeight(64);
0462         cfg.setMaxNumberOfThreads(1);
0463     }
0464     KisImageConfigNotifier::instance()->notifyConfigChanged();
0465 
0466 
0467     const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0468     KisImageSP image = new KisImage(0, 128, 128, colorSpace, "masks test");
0469 
0470     KisPaintDeviceSP device1 = new KisPaintDevice(colorSpace);
0471     device1->fill(image->bounds(), KoColor(Qt::yellow, colorSpace));
0472     KisLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8, device1);
0473     image->addNode(paintLayer1, image->rootLayer());
0474 
0475 
0476     KisFilterSP filter2 = KisFilterRegistry::instance()->value("colorbalance");
0477     KIS_ASSERT(filter2);
0478     KisFilterConfigurationSP configuration2 = filter2->defaultConfiguration(KisGlobalResourcesInterface::instance());
0479     KIS_ASSERT(configuration2);
0480     KisLayerSP adjLayer2 = new KisAdjustmentLayer(image, "adj2", configuration2->cloneWithResourcesSnapshot(), 0);
0481     image->addNode(adjLayer2, image->rootLayer());
0482 
0483 
0484     KisFilterSP filter3 = KisFilterRegistry::instance()->value("blur");
0485     KIS_ASSERT(filter3);
0486     KisFilterConfigurationSP configuration3 = filter3->defaultConfiguration(KisGlobalResourcesInterface::instance());
0487     KIS_ASSERT(configuration3);
0488     KisFilterMaskSP mask3 = new KisFilterMask(image, "mask3");
0489     mask3->initSelection(adjLayer2);
0490     mask3->setFilter(configuration3->cloneWithResourcesSnapshot());
0491     image->addNode(mask3, adjLayer2);
0492 
0493     image->initialRefreshGraph();
0494 
0495     QVERIFY(TestUtil::checkQImage(image->projection()->convertToQImage(0),
0496                                   "async_merger_test", "mask_on_adj", "initial", 3));
0497 }
0498 
0499 
0500 SIMPLE_TEST_MAIN(KisAsyncMergerTest)
0501