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 }