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)