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, &currentDistance);
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, &currentDistance);
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), &currentDistance);
0490             prev = QPointF(cx,cy);
0491         }
0492         m_painter->paintLine(prev, first, &currentDistance);
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, &currentDistance);
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, &currentDistance);
0578         m_painter->paintBezierCurve(m_pi2, m_c2, m_c2, m_pi3, &currentDistance);
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)