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