File indexing completed on 2024-11-10 04:00:29
0001 /* 0002 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý lukast.dev @gmail.com 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <stdlib.h> 0008 0009 #if defined(_WIN32) || defined(_WIN64) 0010 #define srand48 srand 0011 inline double drand48() 0012 { 0013 return double(rand()) / RAND_MAX; 0014 } 0015 #endif 0016 0017 #include <QPainterPath> 0018 #include <simpletest.h> 0019 0020 #include "kis_stroke_benchmark.h" 0021 #include "kis_benchmark_values.h" 0022 0023 #include "kis_paint_device.h" 0024 0025 #include <KoColorSpace.h> 0026 #include <KoColorSpaceRegistry.h> 0027 #include <KoColor.h> 0028 0029 #include <kis_image.h> 0030 #include <kis_layer.h> 0031 #include <kis_paint_layer.h> 0032 0033 #include <brushengine/kis_paint_information.h> 0034 #include <brushengine/kis_paintop_preset.h> 0035 0036 #define GMP_IMAGE_WIDTH 3274 0037 #define GMP_IMAGE_HEIGHT 2067 0038 #include <kis_painter.h> 0039 #include <brushengine/kis_paintop_registry.h> 0040 0041 #include <KisGlobalResourcesInterface.h> 0042 0043 //#define SAVE_OUTPUT 0044 0045 static const int LINES = 20; 0046 static const int RECTANGLES = 20; 0047 const QString OUTPUT_FORMAT = ".png"; 0048 0049 void KisStrokeBenchmark::initTestCase() 0050 { 0051 m_dataPath = QString(FILES_DATA_DIR) + '/'; 0052 m_outputPath = QString(FILES_OUTPUT_DIR) + '/'; 0053 0054 m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); 0055 m_color = KoColor(m_colorSpace); 0056 0057 int width = TEST_IMAGE_WIDTH; 0058 int height = TEST_IMAGE_HEIGHT; 0059 0060 m_image = new KisImage(0, width, height, m_colorSpace, "stroke sample image"); 0061 m_layer = new KisPaintLayer(m_image, "temporary for stroke sample", OPACITY_OPAQUE_U8, m_colorSpace); 0062 0063 0064 m_painter = new KisPainter(m_layer->paintDevice()); 0065 m_painter->setPaintColor(KoColor(Qt::black, m_colorSpace)); 0066 0067 // for bezier curve test 0068 initCurvePoints(width, height); 0069 // for the lines test 0070 initLines(width,height); 0071 // for the rectangles test 0072 initRectangles(width, height); 0073 } 0074 0075 void KisStrokeBenchmark::init() 0076 { 0077 KoColor white(m_colorSpace); 0078 white.fromQColor(Qt::white); 0079 m_layer->paintDevice()->fill(0,0, m_image->width(), m_image->height(),white.data()); 0080 } 0081 0082 0083 void KisStrokeBenchmark::initCurvePoints(int width, int height) 0084 { 0085 QPointF p1(0 , 7.0 / 12.0 * height); 0086 QPointF p2(1.0 / 2.0 * width , 7.0 / 12.0 * height); 0087 QPointF p3(width - 4.0, height - 4.0); 0088 0089 m_c1 = QPointF(1.0 / 4.0 * width, height - 2.0); 0090 m_c2 = QPointF(3.0 / 4.0 * width, 0); 0091 0092 m_pi1 = KisPaintInformation(p1, 0.0); 0093 m_pi2 = KisPaintInformation(p2, 0.95); 0094 m_pi3 = KisPaintInformation(p3, 0.0); 0095 } 0096 0097 0098 void KisStrokeBenchmark::initLines(int width, int height) 0099 { 0100 srand(12345678); 0101 for (int i = 0; i < LINES; i++){ 0102 qreal sx = rand() / qreal(RAND_MAX - 1); 0103 qreal sy = rand() / qreal(RAND_MAX - 1); 0104 m_startPoints.append(QPointF(sx * width,sy * height)); 0105 qreal ex = rand() / qreal(RAND_MAX - 1); 0106 qreal ey = rand() / qreal(RAND_MAX - 1); 0107 m_endPoints.append(QPointF(ex * width,ey * height)); 0108 } 0109 } 0110 0111 void KisStrokeBenchmark::initRectangles(int width, int height) 0112 { 0113 qreal margin = 0.5; 0114 qreal skip = 0.01; 0115 0116 qreal marginWidth = margin*width; 0117 qreal marginHeight = margin*height; 0118 0119 qreal skipWidth = skip*width >= 1 ? skip*width : 1; 0120 qreal skipHeight = skip*width >= 1 ? skip*width : 1; 0121 0122 // "concentric" rectangles 0123 0124 for (int i = 0; i < RECTANGLES; i++){ 0125 QPoint corner1 = QPoint(marginWidth + i*skipWidth, marginHeight + i*skipHeight); 0126 QPoint corner2 = QPoint(width - marginWidth - i*skipWidth, height - marginHeight - i*skipHeight); 0127 0128 if(corner1.x() < corner2.x() && corner1.y() < corner2.y()) { 0129 // if the rectangle is not empty 0130 m_rectangleLeftLowerCorners.append(corner1); 0131 m_rectangleRightUpperCorners.append(corner2); 0132 } 0133 } 0134 } 0135 0136 0137 0138 void KisStrokeBenchmark::cleanupTestCase() 0139 { 0140 delete m_painter; 0141 } 0142 0143 void KisStrokeBenchmark::deformBrush() 0144 { 0145 QString presetFileName = "deform-default.kpp"; 0146 benchmarkStroke(presetFileName); 0147 } 0148 0149 void KisStrokeBenchmark::deformBrushRL() 0150 { 0151 QString presetFileName = "deform-default.kpp"; 0152 benchmarkRandomLines(presetFileName); 0153 } 0154 0155 void KisStrokeBenchmark::pixelbrush300px() 0156 { 0157 QString presetFileName = "autobrush_300px.kpp"; 0158 benchmarkStroke(presetFileName); 0159 } 0160 0161 void KisStrokeBenchmark::pixelbrush300pxRL() 0162 { 0163 QString presetFileName = "autobrush_300px.kpp"; 0164 benchmarkRandomLines(presetFileName); 0165 } 0166 0167 0168 void KisStrokeBenchmark::sprayPixels() 0169 { 0170 QString presetFileName = "spray_wu_pixels1.kpp"; 0171 benchmarkStroke(presetFileName); 0172 } 0173 0174 void KisStrokeBenchmark::sprayPixelsRL() 0175 { 0176 QString presetFileName = "spray_wu_pixels1.kpp"; 0177 benchmarkRandomLines(presetFileName); 0178 } 0179 0180 0181 void KisStrokeBenchmark::sprayTexture() 0182 { 0183 QString presetFileName = "spray_21_textures1.kpp"; 0184 benchmarkStroke(presetFileName); 0185 } 0186 0187 0188 void KisStrokeBenchmark::sprayTextureRL() 0189 { 0190 QString presetFileName = "spray_21_textures1.kpp"; 0191 benchmarkRandomLines(presetFileName); 0192 } 0193 0194 0195 void KisStrokeBenchmark::spray30px21particles() 0196 { 0197 QString presetFileName = "spray_30px21rasterParticles.kpp"; 0198 benchmarkStroke(presetFileName); 0199 } 0200 0201 void KisStrokeBenchmark::spray30px21particlesRL() 0202 { 0203 QString presetFileName = "spray_30px21rasterParticles.kpp"; 0204 benchmarkRandomLines(presetFileName); 0205 } 0206 0207 void KisStrokeBenchmark::sprayPencil() 0208 { 0209 QString presetFileName = "spray_scaled2rasterParticles.kpp"; 0210 benchmarkStroke(presetFileName); 0211 } 0212 0213 void KisStrokeBenchmark::sprayPencilRL() 0214 { 0215 QString presetFileName = "spray_scaled2rasterParticles.kpp"; 0216 benchmarkRandomLines(presetFileName); 0217 } 0218 0219 void KisStrokeBenchmark::softbrushDefault30() 0220 { 0221 QString presetFileName = "softbrush_30px.kpp"; 0222 benchmarkStroke(presetFileName); 0223 } 0224 0225 0226 void KisStrokeBenchmark::softbrushCircle30() 0227 { 0228 QString presetFileName = "softbrush_30px.kpp"; 0229 benchmarkCircle(presetFileName); 0230 } 0231 0232 0233 void KisStrokeBenchmark::softbrushDefault30RL() 0234 { 0235 QString presetFileName = "softbrush_30px.kpp"; 0236 benchmarkRandomLines(presetFileName);} 0237 0238 0239 void KisStrokeBenchmark::softbrushFullFeatures30() 0240 { 0241 QString presetFileName = "softbrush_30px_full.kpp"; 0242 benchmarkStroke(presetFileName); 0243 } 0244 0245 0246 void KisStrokeBenchmark::softbrushFullFeatures30RL() 0247 { 0248 QString presetFileName = "softbrush_30px_full.kpp"; 0249 benchmarkRandomLines(presetFileName); 0250 } 0251 0252 void KisStrokeBenchmark::hairy30pxDefault() 0253 { 0254 QString presetFileName = "hairybrush_thesis30px1.kpp"; 0255 benchmarkStroke(presetFileName); 0256 } 0257 0258 void KisStrokeBenchmark::hairy30pxDefaultRL() 0259 { 0260 QString presetFileName = "hairybrush_thesis30px1.kpp"; 0261 benchmarkRandomLines(presetFileName); 0262 } 0263 0264 void KisStrokeBenchmark::hairy30pxAntiAlias() 0265 { 0266 QString presetFileName = "hairybrush_thesis30px_antialiasing1.kpp"; 0267 benchmarkStroke(presetFileName); 0268 } 0269 0270 void KisStrokeBenchmark::hairy30pxAntiAliasRL() 0271 { 0272 QString presetFileName = "hairybrush_thesis30px_antialiasing1.kpp"; 0273 benchmarkRandomLines(presetFileName); 0274 } 0275 0276 void KisStrokeBenchmark::hairy30px30density() 0277 { 0278 QString presetFileName = "hairybrush_thesis30px_density301.kpp"; 0279 benchmarkStroke(presetFileName); 0280 } 0281 0282 void KisStrokeBenchmark::hairy30px30densityRL() 0283 { 0284 QString presetFileName = "hairybrush_thesis30px_density301.kpp"; 0285 benchmarkRandomLines(presetFileName); 0286 } 0287 0288 void KisStrokeBenchmark::hairy30InkDepletion() 0289 { 0290 QString presetFileName = "hairy30inkDepletion1.kpp"; 0291 benchmarkStroke(presetFileName); 0292 0293 } 0294 0295 void KisStrokeBenchmark::hairy30InkDepletionRL() 0296 { 0297 QString presetFileName = "hairy30inkDepletion1.kpp"; 0298 benchmarkRandomLines(presetFileName); 0299 } 0300 0301 0302 void KisStrokeBenchmark::softbrushOpacity() 0303 { 0304 QString presetFileName = "softbrush_opacity1.kpp"; 0305 benchmarkLine(presetFileName); 0306 } 0307 0308 void KisStrokeBenchmark::softbrushSoftness() 0309 { 0310 QString presetFileName = "softbrush_softness1.kpp"; 0311 benchmarkLine(presetFileName); 0312 } 0313 0314 void KisStrokeBenchmark::dynabrush() 0315 { 0316 QString presetFileName = "dyna301.kpp"; 0317 benchmarkLine(presetFileName); 0318 } 0319 0320 void KisStrokeBenchmark::dynabrushRL() 0321 { 0322 QString presetFileName = "dyna301.kpp"; 0323 benchmarkRandomLines(presetFileName); 0324 } 0325 0326 void KisStrokeBenchmark::experimental() 0327 { 0328 QString presetFileName = "experimental.kpp"; 0329 benchmarkStroke(presetFileName); 0330 } 0331 0332 void KisStrokeBenchmark::experimentalCircle() 0333 { 0334 QString presetFileName = "experimental.kpp"; 0335 benchmarkCircle(presetFileName); 0336 } 0337 0338 void KisStrokeBenchmark::colorsmudge() 0339 { 0340 QString presetFileName = "colorsmudge.kpp"; 0341 benchmarkStroke(presetFileName); 0342 } 0343 0344 void KisStrokeBenchmark::colorsmudgeRL() 0345 { 0346 QString presetFileName = "colorsmudge.kpp"; 0347 benchmarkStroke(presetFileName); 0348 } 0349 0350 0351 void KisStrokeBenchmark::roundMarker() 0352 { 0353 // Quick Brush engine ( b) Basic - 1 brush, size 40px) 0354 QString presetFileName = "roundmarker40px.kpp"; 0355 benchmarkStroke(presetFileName); 0356 } 0357 0358 void KisStrokeBenchmark::roundMarkerRandomLines() 0359 { 0360 // Quick Brush engine ( b) Basic - 1 brush, size 40px) 0361 QString presetFileName = "roundmarker40px.kpp"; 0362 benchmarkRandomLines(presetFileName); 0363 } 0364 0365 void KisStrokeBenchmark::roundMarkerRectangle() 0366 { 0367 // Quick Brush engine ( b) Basic - 1 brush, size 40px) 0368 QString presetFileName = "roundmarker40px.kpp"; 0369 benchmarkStroke(presetFileName); 0370 } 0371 0372 0373 void KisStrokeBenchmark::roundMarkerHalfPixel() 0374 { 0375 // Quick Brush engine ( b) Basic - 1 brush, size 0.5px) 0376 QString presetFileName = "roundmarker05px.kpp"; 0377 benchmarkStroke(presetFileName); 0378 } 0379 0380 void KisStrokeBenchmark::roundMarkerRandomLinesHalfPixel() 0381 { 0382 // Quick Brush engine ( b) Basic - 1 brush, size 0.5px) 0383 QString presetFileName = "roundmarker05px.kpp"; 0384 benchmarkRandomLines(presetFileName); 0385 } 0386 0387 void KisStrokeBenchmark::roundMarkerRectangleHalfPixel() 0388 { 0389 // Quick Brush engine ( b) Basic - 1 brush, size 0.5px) 0390 QString presetFileName = "roundmarker05px.kpp"; 0391 benchmarkStroke(presetFileName); 0392 } 0393 0394 0395 0396 /* 0397 void KisStrokeBenchmark::predefinedBrush() 0398 { 0399 QString presetFileName = "deevad-slow-brush1.kpp"; 0400 benchmarkLine(presetFileName); 0401 } 0402 0403 void KisStrokeBenchmark::predefinedBrushRL() 0404 { 0405 0406 QString presetFileName = "deevad-slow-brush1.kpp"; 0407 0408 KisPaintOpPresetSP preset = new KisPaintOpPreset(m_dataPath + presetFileName); 0409 preset->load(); 0410 preset->settings()->setNode(m_layer); 0411 m_painter->setPaintOpPreset(preset, m_image); 0412 0413 sleep(3); 0414 0415 QBENCHMARK{ 0416 for (int i = 0; i < LINES; i++){ 0417 KisPaintInformation pi1(m_startPoints[i], 0.0); 0418 KisPaintInformation pi2(m_endPoints[i], 1.0); 0419 m_painter->paintLine(pi1, pi2); 0420 } 0421 } 0422 0423 //m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_randomLines" + OUTPUT_FORMAT); 0424 0425 } 0426 */ 0427 0428 inline void KisStrokeBenchmark::benchmarkLine(QString presetFileName) 0429 { 0430 KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName)); 0431 preset->load(KisGlobalResourcesInterface::instance()); 0432 m_painter->setPaintOpPreset(preset, m_layer, m_image); 0433 0434 QPointF startPoint(0.10 * TEST_IMAGE_WIDTH, 0.5 * TEST_IMAGE_HEIGHT); 0435 QPointF endPoint(0.90 * TEST_IMAGE_WIDTH, 0.5 * TEST_IMAGE_HEIGHT); 0436 0437 KisDistanceInformation currentDistance; 0438 KisPaintInformation pi1(startPoint, 0.0); 0439 KisPaintInformation pi2(endPoint, 1.0); 0440 0441 QBENCHMARK{ 0442 m_painter->paintLine(pi1, pi2, ¤tDistance); 0443 } 0444 0445 #ifdef SAVE_OUTPUT 0446 m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_line" + OUTPUT_FORMAT); 0447 #endif 0448 0449 } 0450 0451 void KisStrokeBenchmark::benchmarkCircle(QString presetFileName) 0452 { 0453 dbgKrita << "(circle)preset : " << presetFileName; 0454 0455 KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName)); 0456 if (!preset->load(KisGlobalResourcesInterface::instance())){ 0457 dbgKrita << "Preset was not loaded"; 0458 return; 0459 } 0460 0461 m_painter->setPaintOpPreset(preset, m_layer, m_image); 0462 0463 QBENCHMARK{ 0464 0465 qreal radius = 300; 0466 qreal randomOffset = 300 * 0.4; 0467 int rounds = 20; 0468 int steps = 20; 0469 qreal step = 1.0 / steps; 0470 0471 QPointF center(m_image->width() * 0.5, m_image->height() * 0.5); 0472 QPointF first(center.x()+radius,center.y()); 0473 0474 srand48(0); 0475 for (int k = 0; k < rounds; k++){ 0476 KisDistanceInformation currentDistance; 0477 m_painter->paintLine(center, first, ¤tDistance); 0478 QPointF prev = first; 0479 for (int i = 1; i < steps; i++) { 0480 qreal cx = cos(i * step * 2 * M_PI); 0481 qreal cy = sin(i * step * 2 * M_PI); 0482 0483 cx *= (radius + drand48() * randomOffset); 0484 cy *= (radius + drand48() * randomOffset); 0485 0486 cx += center.x(); 0487 cy += center.y(); 0488 0489 m_painter->paintLine(prev, QPointF(cx,cy), ¤tDistance); 0490 prev = QPointF(cx,cy); 0491 } 0492 m_painter->paintLine(prev, first, ¤tDistance); 0493 } 0494 } 0495 0496 #ifdef SAVE_OUTPUT 0497 m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_circle" + OUTPUT_FORMAT); 0498 #endif 0499 0500 } 0501 0502 0503 0504 void KisStrokeBenchmark::benchmarkRandomLines(QString presetFileName) 0505 { 0506 KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName)); 0507 bool loadedOk = preset->load(KisGlobalResourcesInterface::instance()); 0508 if (!loadedOk){ 0509 dbgKrita << "The preset was not loaded correctly. Done."; 0510 return; 0511 }else{ 0512 dbgKrita << "preset : " << presetFileName; 0513 } 0514 0515 m_painter->setPaintOpPreset(preset, m_layer, m_image); 0516 0517 QBENCHMARK{ 0518 KisDistanceInformation currentDistance; 0519 for (int i = 0; i < LINES; i++){ 0520 KisPaintInformation pi1(m_startPoints[i], 0.0); 0521 KisPaintInformation pi2(m_endPoints[i], 1.0); 0522 m_painter->paintLine(pi1, pi2, ¤tDistance); 0523 } 0524 } 0525 0526 #ifdef SAVE_OUTPUT 0527 m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_randomLines" + OUTPUT_FORMAT); 0528 #endif 0529 } 0530 0531 0532 0533 void KisStrokeBenchmark::benchmarkRectangle(QString presetFileName) 0534 { 0535 KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName)); 0536 bool loadedOk = preset->load(KisGlobalResourcesInterface::instance()); 0537 if (!loadedOk){ 0538 dbgKrita << "The preset was not loaded correctly. Done."; 0539 return; 0540 }else{ 0541 dbgKrita << "preset : " << presetFileName; 0542 } 0543 m_painter->setPaintOpPreset(preset, m_layer, m_image); 0544 0545 int rectangleNumber = m_rectangleLeftLowerCorners.size(); // see initRectangles 0546 0547 QBENCHMARK{ 0548 KisDistanceInformation currentDistance; 0549 for (int i = 0; i < rectangleNumber; i++){ 0550 QPainterPath path; 0551 QRect rect = QRect(m_rectangleLeftLowerCorners[i], m_rectangleRightUpperCorners[i]); 0552 path.addRect(rect); 0553 m_painter->paintPainterPath(path); 0554 } 0555 } 0556 0557 #ifdef SAVE_OUTPUT 0558 m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + "_rectangle" + OUTPUT_FORMAT); 0559 #endif 0560 } 0561 0562 void KisStrokeBenchmark::benchmarkStroke(QString presetFileName) 0563 { 0564 KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName)); 0565 bool loadedOk = preset->load(KisGlobalResourcesInterface::instance()); 0566 if (!loadedOk){ 0567 dbgKrita << "The preset was not loaded correctly. Done."; 0568 return; 0569 } else { 0570 dbgKrita << "preset : " << presetFileName; 0571 } 0572 0573 m_painter->setPaintOpPreset(preset, m_layer, m_image); 0574 0575 QBENCHMARK{ 0576 KisDistanceInformation currentDistance; 0577 m_painter->paintBezierCurve(m_pi1, m_c1, m_c1, m_pi2, ¤tDistance); 0578 m_painter->paintBezierCurve(m_pi2, m_c2, m_c2, m_pi3, ¤tDistance); 0579 } 0580 0581 #ifdef SAVE_OUTPUT 0582 dbgKrita << "Saving output " << m_outputPath + presetFileName + ".png"; 0583 m_layer->paintDevice()->convertToQImage(0).save(m_outputPath + presetFileName + OUTPUT_FORMAT); 0584 #endif 0585 } 0586 0587 static const int COUNT = 1000000; 0588 void KisStrokeBenchmark::benchmarkRand48() 0589 { 0590 QBENCHMARK 0591 { 0592 for (int i = 0 ; i < COUNT; i++){ 0593 double result = drand48(); 0594 Q_UNUSED(result); 0595 } 0596 } 0597 } 0598 0599 void KisStrokeBenchmark::benchmarkRand() 0600 { 0601 float j; 0602 QBENCHMARK{ 0603 for (int i = 0 ; i < COUNT; i++){ 0604 j = rand() / (float)RAND_MAX; 0605 } 0606 } 0607 Q_UNUSED(j); 0608 } 0609 0610 void KisStrokeBenchmark::benchmarkPresetCloning() 0611 { 0612 QString presetFileName = "spray_21_textures1.kpp"; 0613 KisPaintOpPresetSP preset(new KisPaintOpPreset(m_dataPath + presetFileName)); 0614 bool loadedOk = preset->load(KisGlobalResourcesInterface::instance()); 0615 KIS_ASSERT_RECOVER_RETURN(loadedOk); 0616 KIS_ASSERT_RECOVER_RETURN(preset->settings()); 0617 0618 QBENCHMARK { 0619 KisPaintOpPresetSP other = preset->clone().dynamicCast<KisPaintOpPreset>(); 0620 other->settings()->setPaintOpOpacity(0.3); 0621 } 0622 } 0623 0624 0625 SIMPLE_TEST_MAIN(KisStrokeBenchmark)