File indexing completed on 2025-01-26 04:11:16

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisDabRenderingQueueTest.h"
0008 
0009 #include <simpletest.h>
0010 #include <KoColorSpace.h>
0011 #include <KoColorSpaceRegistry.h>
0012 
0013 #include <../KisDabRenderingQueue.h>
0014 #include <../KisRenderedDab.h>
0015 #include <../KisDabRenderingJob.h>
0016 
0017 struct SurrogateCacheInterface : public KisDabRenderingQueue::CacheInterface
0018 {
0019     void getDabType(bool hasDabInCache,
0020                     KisDabCacheUtils::DabRenderingResources *resources,
0021                     const KisDabCacheUtils::DabRequestInfo &request,
0022                     /* out */
0023                     KisDabCacheUtils::DabGenerationInfo *di,
0024                     bool *shouldUseCache) override
0025     {
0026         Q_UNUSED(resources);
0027         Q_UNUSED(request);
0028 
0029         if (!hasDabInCache || typeOverride == KisDabRenderingJob::Dab) {
0030             di->needsPostprocessing = false;
0031             *shouldUseCache = false;
0032         } else if (typeOverride == KisDabRenderingJob::Copy) {
0033             di->needsPostprocessing = false;
0034             *shouldUseCache = true;
0035         } else if (typeOverride == KisDabRenderingJob::Postprocess) {
0036             di->needsPostprocessing = true;
0037             *shouldUseCache = true;
0038         }
0039 
0040         di->info = request.info;
0041     }
0042 
0043     bool hasSeparateOriginal(KisDabCacheUtils::DabRenderingResources *resources) const override {
0044         Q_UNUSED(resources);
0045         return typeOverride == KisDabRenderingJob::Postprocess;
0046     }
0047 
0048     KisDabRenderingJob::JobType typeOverride = KisDabRenderingJob::Dab;
0049 };
0050 
0051 #include <kis_mask_generator.h>
0052 #include "kis_auto_brush.h"
0053 
0054 KisDabCacheUtils::DabRenderingResources *testResourcesFactory()
0055 {
0056     KisDabCacheUtils::DabRenderingResources *resources =
0057         new KisDabCacheUtils::DabRenderingResources();
0058 
0059     KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 1.0, 1.0, 1.0, 2, false);
0060     KisBrushSP brush(new KisAutoBrush(circle, 0.0, 0.0));
0061     resources->brush = brush;
0062 
0063     return resources;
0064 }
0065 
0066 void KisDabRenderingQueueTest::testCachedDabs()
0067 {
0068     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0069 
0070     SurrogateCacheInterface *cacheInterface = new SurrogateCacheInterface();
0071 
0072     KisDabRenderingQueue queue(cs, testResourcesFactory);
0073     queue.setCacheInterface(cacheInterface);
0074 
0075     KoColor color;
0076     QPointF pos1(10,10);
0077     QPointF pos2(20,20);
0078     KisDabShape shape;
0079     KisPaintInformation pi1(pos1);
0080     KisPaintInformation pi2(pos2);
0081 
0082     KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
0083     KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
0084 
0085     cacheInterface->typeOverride = KisDabRenderingJob::Dab;
0086     KisDabRenderingJobSP job0 = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0087 
0088     QVERIFY(job0);
0089     QCOMPARE(job0->seqNo, 0);
0090     QCOMPARE(job0->generationInfo.info.pos(), request1.info.pos());
0091     QCOMPARE(job0->type, KisDabRenderingJob::Dab);
0092     QVERIFY(!job0->originalDevice);
0093     QVERIFY(!job0->postprocessedDevice);
0094 
0095     cacheInterface->typeOverride = KisDabRenderingJob::Dab;
0096     KisDabRenderingJobSP job1 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0097 
0098     QVERIFY(job1);
0099     QCOMPARE(job1->seqNo, 1);
0100     QCOMPARE(job1->generationInfo.info.pos(), request2.info.pos());
0101     QCOMPARE(job1->type, KisDabRenderingJob::Dab);
0102     QVERIFY(!job1->originalDevice);
0103     QVERIFY(!job1->postprocessedDevice);
0104 
0105     cacheInterface->typeOverride = KisDabRenderingJob::Copy;
0106     KisDabRenderingJobSP job2 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0107     QVERIFY(!job2);
0108 
0109     cacheInterface->typeOverride = KisDabRenderingJob::Copy;
0110     KisDabRenderingJobSP job3 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0111     QVERIFY(!job3);
0112 
0113     // we only added the dabs, but we haven't completed them yet
0114     QVERIFY(!queue.hasPreparedDabs());
0115     QCOMPARE(queue.testingGetQueueSize(), 4);
0116 
0117     QList<KisDabRenderingJobSP > jobs;
0118     QList<KisRenderedDab> renderedDabs;
0119 
0120 
0121     {
0122         // we've completed job0
0123         job0->originalDevice = new KisFixedPaintDevice(cs);
0124         job0->postprocessedDevice = job0->originalDevice;
0125 
0126         jobs = queue.notifyJobFinished(job0->seqNo);
0127         QVERIFY(jobs.isEmpty());
0128 
0129         // now we should have at least one job in prepared state
0130         QVERIFY(queue.hasPreparedDabs());
0131 
0132         // take the prepared dabs
0133         renderedDabs = queue.takeReadyDabs();
0134         QCOMPARE(renderedDabs.size(), 1);
0135 
0136         // the list should be empty again
0137         QVERIFY(!queue.hasPreparedDabs());
0138         QCOMPARE(queue.testingGetQueueSize(), 3);
0139     }
0140 
0141     {
0142         // we've completed job1
0143         job1->originalDevice = new KisFixedPaintDevice(cs);
0144         job1->postprocessedDevice = job1->originalDevice;
0145 
0146         jobs = queue.notifyJobFinished(job1->seqNo);
0147         QVERIFY(jobs.isEmpty());
0148 
0149         // now we should have at least one job in prepared state
0150         QVERIFY(queue.hasPreparedDabs());
0151 
0152         // take the prepared dabs
0153         renderedDabs = queue.takeReadyDabs();
0154         QCOMPARE(renderedDabs.size(), 3);
0155 
0156         // since they are copies, they should be the same
0157         QCOMPARE(renderedDabs[1].device, renderedDabs[0].device);
0158         QCOMPARE(renderedDabs[2].device, renderedDabs[0].device);
0159 
0160         // the list should be empty again
0161         QVERIFY(!queue.hasPreparedDabs());
0162 
0163         // we delete all the painted jobs except the latest 'dab' job
0164         QCOMPARE(queue.testingGetQueueSize(), 1);
0165     }
0166 
0167     {
0168         // add one more cached job and take it
0169         cacheInterface->typeOverride = KisDabRenderingJob::Copy;
0170         KisDabRenderingJobSP job = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0171         QVERIFY(!job);
0172 
0173         // now we should have at least one job in prepared state
0174         QVERIFY(queue.hasPreparedDabs());
0175 
0176         // take the prepared dabs
0177         renderedDabs = queue.takeReadyDabs();
0178         QCOMPARE(renderedDabs.size(), 1);
0179 
0180         // the list should be empty again
0181         QVERIFY(!queue.hasPreparedDabs());
0182 
0183         // we delete all the painted jobs except the latest 'dab' job
0184         QCOMPARE(queue.testingGetQueueSize(), 1);
0185     }
0186 
0187     {
0188         // add a 'dab' job and complete it
0189 
0190         cacheInterface->typeOverride = KisDabRenderingJob::Dab;
0191         KisDabRenderingJobSP job = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0192 
0193         QVERIFY(job);
0194         QCOMPARE(job->seqNo, 5);
0195         QCOMPARE(job->generationInfo.info.pos(), request1.info.pos());
0196         QCOMPARE(job->type, KisDabRenderingJob::Dab);
0197         QVERIFY(!job->originalDevice);
0198         QVERIFY(!job->postprocessedDevice);
0199 
0200         // now the queue can be cleared from the completed dabs!
0201         QCOMPARE(queue.testingGetQueueSize(), 1);
0202 
0203         job->originalDevice = new KisFixedPaintDevice(cs);
0204         job->postprocessedDevice = job->originalDevice;
0205 
0206         jobs = queue.notifyJobFinished(job->seqNo);
0207         QVERIFY(jobs.isEmpty());
0208 
0209         // now we should have at least one job in prepared state
0210         QVERIFY(queue.hasPreparedDabs());
0211 
0212         // take the prepared dabs
0213         renderedDabs = queue.takeReadyDabs();
0214         QCOMPARE(renderedDabs.size(), 1);
0215 
0216         // the list should be empty again
0217         QVERIFY(!queue.hasPreparedDabs());
0218 
0219         // we do not delete the queue of jobs until the next 'dab'
0220         // job arrives
0221         QCOMPARE(queue.testingGetQueueSize(), 1);
0222     }
0223 
0224 }
0225 
0226 void KisDabRenderingQueueTest::testPostprocessedDabs()
0227 {
0228     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0229 
0230     SurrogateCacheInterface *cacheInterface = new SurrogateCacheInterface();
0231 
0232     KisDabRenderingQueue queue(cs, testResourcesFactory);
0233     queue.setCacheInterface(cacheInterface);
0234 
0235     KoColor color;
0236     QPointF pos1(10,10);
0237     QPointF pos2(20,20);
0238     KisDabShape shape;
0239     KisPaintInformation pi1(pos1);
0240     KisPaintInformation pi2(pos2);
0241 
0242     KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
0243     KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
0244 
0245     cacheInterface->typeOverride = KisDabRenderingJob::Dab;
0246     KisDabRenderingJobSP job0 = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0247 
0248     QVERIFY(job0);
0249     QCOMPARE(job0->seqNo, 0);
0250     QCOMPARE(job0->generationInfo.info.pos(), request1.info.pos());
0251     QCOMPARE(job0->type, KisDabRenderingJob::Dab);
0252     QVERIFY(!job0->originalDevice);
0253     QVERIFY(!job0->postprocessedDevice);
0254 
0255     cacheInterface->typeOverride = KisDabRenderingJob::Dab;
0256     KisDabRenderingJobSP job1 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0257 
0258     QVERIFY(job1);
0259     QCOMPARE(job1->seqNo, 1);
0260     QCOMPARE(job1->generationInfo.info.pos(), request2.info.pos());
0261     QCOMPARE(job1->type, KisDabRenderingJob::Dab);
0262     QVERIFY(!job1->originalDevice);
0263     QVERIFY(!job1->postprocessedDevice);
0264 
0265     cacheInterface->typeOverride = KisDabRenderingJob::Postprocess;
0266     KisDabRenderingJobSP job2 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0267     QVERIFY(!job2);
0268 
0269     cacheInterface->typeOverride = KisDabRenderingJob::Postprocess;
0270     KisDabRenderingJobSP job3 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0271     QVERIFY(!job3);
0272 
0273     // we only added the dabs, but we haven't completed them yet
0274     QVERIFY(!queue.hasPreparedDabs());
0275     QCOMPARE(queue.testingGetQueueSize(), 4);
0276 
0277     QList<KisDabRenderingJobSP > jobs;
0278     QList<KisRenderedDab> renderedDabs;
0279 
0280 
0281     {
0282         // we've completed job0
0283         job0->originalDevice = new KisFixedPaintDevice(cs);
0284         job0->postprocessedDevice = job0->originalDevice;
0285 
0286         jobs = queue.notifyJobFinished(job0->seqNo);
0287         QVERIFY(jobs.isEmpty());
0288 
0289         // now we should have at least one job in prepared state
0290         QVERIFY(queue.hasPreparedDabs());
0291 
0292         // take the prepared dabs
0293         renderedDabs = queue.takeReadyDabs();
0294         QCOMPARE(renderedDabs.size(), 1);
0295 
0296         // the list should be empty again
0297         QVERIFY(!queue.hasPreparedDabs());
0298         QCOMPARE(queue.testingGetQueueSize(), 3);
0299     }
0300 
0301     {
0302         // we've completed job1
0303         job1->originalDevice = new KisFixedPaintDevice(cs);
0304         job1->postprocessedDevice = job1->originalDevice;
0305 
0306         jobs = queue.notifyJobFinished(job1->seqNo);
0307         QCOMPARE(jobs.size(), 2);
0308 
0309         QCOMPARE(jobs[0]->seqNo, 2);
0310         QCOMPARE(jobs[1]->seqNo, 3);
0311 
0312         QVERIFY(jobs[0]->originalDevice);
0313         QVERIFY(!jobs[0]->postprocessedDevice);
0314 
0315         QVERIFY(jobs[1]->originalDevice);
0316         QVERIFY(!jobs[1]->postprocessedDevice);
0317 
0318         // pretend we have created a postprocessed device
0319         jobs[0]->postprocessedDevice = new KisFixedPaintDevice(cs);
0320         jobs[1]->postprocessedDevice = new KisFixedPaintDevice(cs);
0321 
0322         // now we should have at least one job in prepared state
0323         QVERIFY(queue.hasPreparedDabs());
0324 
0325         // take the prepared dabs
0326         renderedDabs = queue.takeReadyDabs();
0327         QCOMPARE(renderedDabs.size(), 1);
0328 
0329         // the list should be empty again
0330         QVERIFY(!queue.hasPreparedDabs());
0331 
0332 
0333         // return back two postprocessed dabs
0334         QList<KisDabRenderingJobSP > emptyJobs;
0335         emptyJobs = queue.notifyJobFinished(jobs[0]->seqNo);
0336         QVERIFY(emptyJobs.isEmpty());
0337 
0338         emptyJobs = queue.notifyJobFinished(jobs[1]->seqNo);
0339         QVERIFY(emptyJobs.isEmpty());
0340 
0341 
0342         // now we should have at least one job in prepared state
0343         QVERIFY(queue.hasPreparedDabs());
0344 
0345         // take the prepared dabs
0346         renderedDabs = queue.takeReadyDabs();
0347         QCOMPARE(renderedDabs.size(), 2);
0348 
0349         // the list should be empty again
0350         QVERIFY(!queue.hasPreparedDabs());
0351 
0352         // we delete all the painted jobs except the latest 'dab' job
0353         QCOMPARE(queue.testingGetQueueSize(), 1);
0354     }
0355 
0356     {
0357         // add one more postprocessed job and take it
0358         cacheInterface->typeOverride = KisDabRenderingJob::Postprocess;
0359         KisDabRenderingJobSP job = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0360 
0361         QVERIFY(job);
0362         QCOMPARE(job->seqNo, 4);
0363         QCOMPARE(job->generationInfo.info.pos(), request2.info.pos());
0364         ENTER_FUNCTION() << ppVar(job->type);
0365 
0366         QCOMPARE(job->type, KisDabRenderingJob::Postprocess);
0367         QVERIFY(job->originalDevice);
0368         QVERIFY(!job->postprocessedDevice);
0369 
0370         // the list should still be empty
0371         QVERIFY(!queue.hasPreparedDabs());
0372 
0373         // pretend we have created a postprocessed device
0374         job->postprocessedDevice = new KisFixedPaintDevice(cs);
0375 
0376         // return back the postprocessed dab
0377         QList<KisDabRenderingJobSP > emptyJobs;
0378         emptyJobs = queue.notifyJobFinished(job->seqNo);
0379         QVERIFY(emptyJobs.isEmpty());
0380 
0381         // now we should have at least one job in prepared state
0382         QVERIFY(queue.hasPreparedDabs());
0383 
0384         // take the prepared dabs
0385         renderedDabs = queue.takeReadyDabs();
0386         QCOMPARE(renderedDabs.size(), 1);
0387 
0388         // the list should be empty again
0389         QVERIFY(!queue.hasPreparedDabs());
0390 
0391         // we delete all the painted jobs except the latest 'dab' job
0392         QCOMPARE(queue.testingGetQueueSize(), 1);
0393     }
0394 
0395     {
0396         // add a 'dab' job and complete it. That will clear the queue!
0397 
0398         cacheInterface->typeOverride = KisDabRenderingJob::Dab;
0399         KisDabRenderingJobSP job = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0400 
0401         QVERIFY(job);
0402         QCOMPARE(job->seqNo, 5);
0403         QCOMPARE(job->generationInfo.info.pos(), request1.info.pos());
0404         QCOMPARE(job->type, KisDabRenderingJob::Dab);
0405         QVERIFY(!job->originalDevice);
0406         QVERIFY(!job->postprocessedDevice);
0407 
0408         // now the queue can be cleared from the completed dabs!
0409         QCOMPARE(queue.testingGetQueueSize(), 1);
0410 
0411         job->originalDevice = new KisFixedPaintDevice(cs);
0412         job->postprocessedDevice = job->originalDevice;
0413 
0414         jobs = queue.notifyJobFinished(job->seqNo);
0415         QVERIFY(jobs.isEmpty());
0416 
0417         // now we should have at least one job in prepared state
0418         QVERIFY(queue.hasPreparedDabs());
0419 
0420         // take the prepared dabs
0421         renderedDabs = queue.takeReadyDabs();
0422         QCOMPARE(renderedDabs.size(), 1);
0423 
0424         // the list should be empty again
0425         QVERIFY(!queue.hasPreparedDabs());
0426 
0427         // we do not delete the queue of jobs until the next 'dab'
0428         // job arrives
0429         QCOMPARE(queue.testingGetQueueSize(), 1);
0430     }
0431 
0432 }
0433 
0434 #include <../KisDabRenderingQueueCache.h>
0435 
0436 void KisDabRenderingQueueTest::testRunningJobs()
0437 {
0438     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0439 
0440     KisDabRenderingQueueCache *cacheInterface = new KisDabRenderingQueueCache();
0441     // we do *not* initialize any options yet!
0442 
0443     KisDabRenderingQueue queue(cs, testResourcesFactory);
0444     queue.setCacheInterface(cacheInterface);
0445 
0446 
0447     KoColor color(Qt::red, cs);
0448     QPointF pos1(10,10);
0449     QPointF pos2(20,20);
0450     KisDabShape shape;
0451     KisPaintInformation pi1(pos1);
0452     KisPaintInformation pi2(pos2);
0453 
0454     KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
0455     KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
0456 
0457     KisDabRenderingJobSP job0 = queue.addDab(request1, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0458 
0459     QVERIFY(job0);
0460     QCOMPARE(job0->seqNo, 0);
0461     QCOMPARE(job0->generationInfo.info.pos(), request1.info.pos());
0462     QCOMPARE(job0->type, KisDabRenderingJob::Dab);
0463 
0464     QVERIFY(!job0->originalDevice);
0465     QVERIFY(!job0->postprocessedDevice);
0466 
0467     KisDabRenderingJobRunner runner(job0, &queue, 0);
0468     runner.run();
0469 
0470     QVERIFY(job0->originalDevice);
0471     QVERIFY(job0->postprocessedDevice);
0472     QCOMPARE(job0->originalDevice, job0->postprocessedDevice);
0473 
0474     QVERIFY(!job0->originalDevice->bounds().isEmpty());
0475 
0476     KisDabRenderingJobSP job1 = queue.addDab(request2, OPACITY_OPAQUE_F, OPACITY_OPAQUE_F);
0477     QVERIFY(!job1);
0478 
0479     QList<KisRenderedDab> renderedDabs = queue.takeReadyDabs();
0480     QCOMPARE(renderedDabs.size(), 2);
0481 
0482     // we did the caching
0483     QVERIFY(renderedDabs[0].device == renderedDabs[1].device);
0484 
0485     QCOMPARE(renderedDabs[0].offset, QPoint(5,5));
0486     QCOMPARE(renderedDabs[1].offset, QPoint(15,15));
0487 }
0488 
0489 #include "../KisDabRenderingExecutor.h"
0490 #include "KisFakeRunnableStrokeJobsExecutor.h"
0491 
0492 void KisDabRenderingQueueTest::testExecutor()
0493 {
0494     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0495 
0496     QScopedPointer<KisRunnableStrokeJobsInterface> runner(new KisFakeRunnableStrokeJobsExecutor());
0497 
0498     KisDabRenderingExecutor executor(cs, testResourcesFactory, runner.data());
0499 
0500     KoColor color(Qt::red, cs);
0501     QPointF pos1(10,10);
0502     QPointF pos2(20,20);
0503     KisDabShape shape;
0504     KisPaintInformation pi1(pos1);
0505     KisPaintInformation pi2(pos2);
0506 
0507     KisDabCacheUtils::DabRequestInfo request1(color, pos1, shape, pi1, 1.0);
0508     KisDabCacheUtils::DabRequestInfo request2(color, pos2, shape, pi2, 1.0);
0509 
0510     executor.addDab(request1, 0.5, 0.25);
0511     executor.addDab(request2, 0.125, 1.0);
0512 
0513     QList<KisRenderedDab> renderedDabs = executor.takeReadyDabs();
0514     QCOMPARE(renderedDabs.size(), 2);
0515 
0516     // we did the caching
0517     QVERIFY(renderedDabs[0].device == renderedDabs[1].device);
0518 
0519     QCOMPARE(renderedDabs[0].offset, QPoint(5,5));
0520     QCOMPARE(renderedDabs[1].offset, QPoint(15,15));
0521 
0522     QCOMPARE(renderedDabs[0].opacity, 0.5);
0523     QCOMPARE(renderedDabs[0].flow, 0.25);
0524     QCOMPARE(renderedDabs[1].opacity, 0.125);
0525     QCOMPARE(renderedDabs[1].flow, 1.0);
0526 
0527 }
0528 
0529 SIMPLE_TEST_MAIN(KisDabRenderingQueueTest)