File indexing completed on 2024-05-19 04:32:39
0001 /* 0002 * SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "stroke_testing_utils.h" 0008 0009 #include <simpletest.h> 0010 0011 #include <QDir> 0012 #include <KoColor.h> 0013 #include <KoColorSpace.h> 0014 #include <KoColorSpaceRegistry.h> 0015 #include <KoCompositeOpRegistry.h> 0016 #include <brushengine/kis_paintop_preset.h> 0017 #include <resources/KoPattern.h> 0018 #include "kis_canvas_resource_provider.h" 0019 #include "kis_image.h" 0020 #include "kis_paint_device.h" 0021 #include "kis_paint_layer.h" 0022 #include "kis_group_layer.h" 0023 #include <KisViewManager.h> 0024 0025 #include <testutil.h> 0026 #include <KisGlobalResourcesInterface.h> 0027 0028 0029 KisImageSP utils::createImage(KisUndoStore *undoStore, const QSize &imageSize) { 0030 QRect imageRect(0,0,imageSize.width(),imageSize.height()); 0031 0032 const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); 0033 KisImageSP image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "stroke test"); 0034 0035 KisPaintLayerSP paintLayer1 = new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8); 0036 KisPaintLayerSP paintLayer2 = new KisPaintLayer(image, "paint2", OPACITY_OPAQUE_U8); 0037 KisPaintLayerSP paintLayer3 = new KisPaintLayer(image, "paint3", OPACITY_OPAQUE_U8); 0038 KisPaintLayerSP paintLayer4 = new KisPaintLayer(image, "paint4", OPACITY_OPAQUE_U8); 0039 KisPaintLayerSP paintLayer5 = new KisPaintLayer(image, "paint5", OPACITY_OPAQUE_U8); 0040 0041 image->barrierLock(); 0042 image->addNode(paintLayer1); 0043 image->addNode(paintLayer2); 0044 image->addNode(paintLayer3); 0045 image->addNode(paintLayer4); 0046 image->addNode(paintLayer5); 0047 image->unlock(); 0048 return image; 0049 } 0050 0051 KoCanvasResourceProvider* utils::createResourceManager(KisImageWSP image, 0052 KisNodeSP node, 0053 const QString &presetFileName) 0054 { 0055 KoCanvasResourceProvider *manager = new KoCanvasResourceProvider(); 0056 KisViewManager::initializeResourceManager(manager); 0057 0058 QVariant i; 0059 0060 i.setValue(KoColor(Qt::black, image->colorSpace())); 0061 manager->setResource(KoCanvasResource::ForegroundColor, i); 0062 0063 i.setValue(KoColor(Qt::white, image->colorSpace())); 0064 manager->setResource(KoCanvasResource::BackgroundColor, i); 0065 0066 i.setValue(static_cast<void*>(0)); 0067 manager->setResource(KoCanvasResource::CurrentPattern, i); 0068 manager->setResource(KoCanvasResource::CurrentGradient, i); 0069 manager->setResource(KoCanvasResource::CurrentGeneratorConfiguration, i); 0070 0071 if(!node) { 0072 node = image->root(); 0073 0074 while(node && !dynamic_cast<KisPaintLayer*>(node.data())) { 0075 node = node->firstChild(); 0076 } 0077 0078 Q_ASSERT(node && dynamic_cast<KisPaintLayer*>(node.data())); 0079 } 0080 0081 i.setValue(node); 0082 manager->setResource(KoCanvasResource::CurrentKritaNode, i); 0083 0084 KisPaintOpPresetSP preset; 0085 0086 if (!presetFileName.isEmpty()) { 0087 QString fullFileName = TestUtil::fetchDataFileLazy(presetFileName); 0088 preset = KisPaintOpPresetSP(new KisPaintOpPreset(fullFileName)); 0089 bool presetValid = preset->load(KisGlobalResourcesInterface::instance()); 0090 Q_ASSERT(presetValid); Q_UNUSED(presetValid); 0091 0092 i.setValue(preset); 0093 manager->setResource(KoCanvasResource::CurrentPaintOpPreset, i); 0094 } 0095 0096 i.setValue(COMPOSITE_OVER); 0097 manager->setResource(KoCanvasResource::CurrentCompositeOp, i); 0098 0099 i.setValue(false); 0100 manager->setResource(KoCanvasResource::MirrorHorizontal, i); 0101 0102 i.setValue(false); 0103 manager->setResource(KoCanvasResource::MirrorVertical, i); 0104 0105 i.setValue(1.0); 0106 manager->setResource(KoCanvasResource::Opacity, i); 0107 0108 i.setValue(1.0); 0109 manager->setResource(KoCanvasResource::HdrExposure, i); 0110 0111 return manager; 0112 } 0113 0114 utils::StrokeTester::StrokeTester(const QString &name, const QSize &imageSize, const QString &presetFilename) 0115 : m_name(name), 0116 m_imageSize(imageSize), 0117 m_presetFilename(presetFilename), 0118 m_numIterations(1), 0119 m_baseFuzziness(1) 0120 { 0121 } 0122 0123 utils::StrokeTester::~StrokeTester() 0124 { 0125 } 0126 0127 void utils::StrokeTester::setNumIterations(int value) 0128 { 0129 m_numIterations = value; 0130 } 0131 0132 void utils::StrokeTester::setBaseFuzziness(int value) 0133 { 0134 m_baseFuzziness = value; 0135 } 0136 0137 void utils::StrokeTester::setCancelOnIteration(int value) 0138 { 0139 m_cancelOnIteration = value; 0140 } 0141 0142 void utils::StrokeTester::testSimpleStroke() 0143 { 0144 testOneStroke(false, true, false, true); 0145 } 0146 0147 void utils::StrokeTester::testSimpleStrokeCancelled() 0148 { 0149 testOneStroke(true, true, false, true); 0150 } 0151 0152 int utils::StrokeTester::lastStrokeTime() const 0153 { 0154 return m_strokeTime; 0155 } 0156 0157 void utils::StrokeTester::test() 0158 { 0159 testOneStroke(false, false, false); 0160 testOneStroke(false, true, false); 0161 testOneStroke(true, false, false); 0162 testOneStroke(true, true, false); 0163 0164 // The same but with updates (compare against projection) 0165 0166 testOneStroke(false, false, false, true); 0167 testOneStroke(false, true, false, true); 0168 testOneStroke(true, false, false, true); 0169 testOneStroke(true, true, false, true); 0170 0171 // The same, but with an external layer 0172 0173 testOneStroke(false, false, true); 0174 testOneStroke(false, true, true); 0175 testOneStroke(true, false, true); 0176 testOneStroke(true, true, true); 0177 } 0178 0179 void utils::StrokeTester::benchmark() 0180 { 0181 // not cancelled, indirect painting, internal, no updates, no qimage 0182 doStroke(false, false, false, false); 0183 } 0184 0185 void utils::StrokeTester::testSimpleStrokeNoVerification() 0186 { 0187 doStroke(false, false, true, false); 0188 } 0189 0190 void utils::StrokeTester::testOneStroke(bool cancelled, 0191 bool indirectPainting, 0192 bool externalLayer, 0193 bool testUpdates) 0194 { 0195 // TODO: indirectPainting option is not used anymore! The real value is 0196 // taken from the preset! 0197 0198 QString testName = formatTestName(m_name, 0199 cancelled, 0200 indirectPainting, 0201 externalLayer); 0202 0203 dbgKrita << "Testcase:" << testName 0204 << "(compare against " << (testUpdates ? "projection" : "layer") << ")"; 0205 0206 QImage resultImage; 0207 resultImage = doStroke(cancelled, externalLayer, testUpdates); 0208 0209 QImage refImage; 0210 refImage.load(referenceFile(testName)); 0211 0212 QPoint temp; 0213 if(!TestUtil::compareQImages(temp, refImage, resultImage, m_baseFuzziness, m_baseFuzziness)) { 0214 refImage.save(dumpReferenceFile(testName)); 0215 resultImage.save(resultFile(testName)); 0216 0217 QFAIL("Images do not coincide"); 0218 } 0219 } 0220 0221 QString utils::StrokeTester::formatTestName(const QString &baseName, 0222 bool cancelled, 0223 bool indirectPainting, 0224 bool externalLayer) 0225 { 0226 QString result = baseName; 0227 result += "_" + m_presetFilename; 0228 result += indirectPainting ? "_indirect" : "_incremental"; 0229 result += cancelled ? "_cancelled" : "_finished"; 0230 result += externalLayer ? "_external" : "_internal"; 0231 return result; 0232 } 0233 0234 QString utils::StrokeTester::referenceFile(const QString &testName) 0235 { 0236 QString path = 0237 QString(FILES_DATA_DIR) + '/' + 0238 m_name + '/'; 0239 0240 path += testName; 0241 path += ".png"; 0242 return path; 0243 } 0244 0245 QString utils::StrokeTester::dumpReferenceFile(const QString &testName) 0246 { 0247 QString path = QString(FILES_OUTPUT_DIR) + '/'; 0248 path += testName; 0249 path += "_expected"; 0250 path += ".png"; 0251 return path; 0252 } 0253 0254 QString utils::StrokeTester::resultFile(const QString &testName) 0255 { 0256 QString path = QString(FILES_OUTPUT_DIR) + '/'; 0257 path += testName; 0258 path += ".png"; 0259 return path; 0260 } 0261 0262 QImage utils::StrokeTester::doStroke(bool cancelled, 0263 bool externalLayer, 0264 bool testUpdates, 0265 bool needQImage) 0266 { 0267 KisUndoStore *undoStore = new KisSurrogateUndoStore(); 0268 0269 KisImageSP image = utils::createImage(undoStore, m_imageSize); 0270 KoCanvasResourceProvider *manager = utils::createResourceManager(image, 0, m_presetFilename); 0271 KisNodeSP currentNode; 0272 0273 for (int i = 0; i < m_numIterations; i++) { 0274 modifyResourceManager(manager, image, i); 0275 0276 KisResourcesSnapshotSP resources = 0277 new KisResourcesSnapshot(image, 0278 image->rootLayer()->firstChild(), 0279 manager); 0280 0281 if(externalLayer) { 0282 KisNodeSP externalNode = new KisPaintLayer(0, "extlyr", OPACITY_OPAQUE_U8, image->colorSpace()); 0283 resources->setCurrentNode(externalNode); 0284 Q_ASSERT(resources->currentNode() == externalNode); 0285 } 0286 0287 initImage(image, resources->currentNode(), i); 0288 0289 QElapsedTimer strokeTime; 0290 strokeTime.start(); 0291 0292 KisStrokeStrategy *stroke = createStroke(resources, image); 0293 m_strokeId = image->startStroke(stroke); 0294 addPaintingJobs(image, resources, i); 0295 0296 if(!cancelled || i < m_cancelOnIteration) { 0297 image->endStroke(m_strokeId); 0298 } 0299 else { 0300 image->cancelStroke(m_strokeId); 0301 } 0302 0303 image->waitForDone(); 0304 0305 m_strokeTime = strokeTime.elapsed(); 0306 currentNode = resources->currentNode(); 0307 0308 iterationEndedCallback(image, currentNode, i); 0309 } 0310 0311 beforeCheckingResult(image, currentNode); 0312 0313 QImage resultImage; 0314 if(needQImage) { 0315 KisPaintDeviceSP device = testUpdates ? 0316 image->projection() : 0317 currentNode->paintDevice(); 0318 0319 resultImage = device->convertToQImage(0, 0, 0, image->width(), image->height()); 0320 } 0321 0322 image = 0; 0323 delete manager; 0324 return resultImage; 0325 } 0326 0327 void utils::StrokeTester::modifyResourceManager(KoCanvasResourceProvider *manager, 0328 KisImageWSP image, int iteration) 0329 { 0330 Q_UNUSED(iteration); 0331 modifyResourceManager(manager, image); 0332 } 0333 0334 void utils::StrokeTester::modifyResourceManager(KoCanvasResourceProvider *manager, 0335 KisImageWSP image) 0336 { 0337 Q_UNUSED(manager); 0338 Q_UNUSED(image); 0339 } 0340 0341 void utils::StrokeTester::initImage(KisImageWSP image, KisNodeSP activeNode, int iteration) 0342 { 0343 Q_UNUSED(iteration); 0344 initImage(image, activeNode); 0345 } 0346 0347 void utils::StrokeTester::initImage(KisImageWSP image, KisNodeSP activeNode) 0348 { 0349 Q_UNUSED(image); 0350 Q_UNUSED(activeNode); 0351 } 0352 0353 void utils::StrokeTester::addPaintingJobs(KisImageWSP image, 0354 KisResourcesSnapshotSP resources, 0355 int iteration) 0356 { 0357 Q_UNUSED(iteration); 0358 addPaintingJobs(image, resources); 0359 } 0360 0361 void utils::StrokeTester::addPaintingJobs(KisImageWSP image, 0362 KisResourcesSnapshotSP resources) 0363 { 0364 Q_UNUSED(image); 0365 Q_UNUSED(resources); 0366 } 0367 0368 void utils::StrokeTester::beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode) 0369 { 0370 Q_UNUSED(image); 0371 Q_UNUSED(activeNode); 0372 } 0373 0374 void utils::StrokeTester::iterationEndedCallback(KisImageWSP image, KisNodeSP activeNode, int iteration) 0375 { 0376 Q_UNUSED(image); 0377 Q_UNUSED(activeNode); 0378 Q_UNUSED(iteration); 0379 }