File indexing completed on 2024-12-22 04:10:13

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_cage_transform_worker_test.h"
0008 
0009 #include <simpletest.h>
0010 
0011 #include <KoProgressUpdater.h>
0012 #include <KoUpdater.h>
0013 
0014 #include <testutil.h>
0015 #include <kistest.h>
0016 
0017 #include <kis_cage_transform_worker.h>
0018 #include <algorithm>
0019 
0020 void testCage(bool clockwise, bool unityTransform, bool benchmarkPrepareOnly = false, int pixelPrecision = 8, bool testQImage = false)
0021 {
0022     TestUtil::TestProgressBar bar;
0023     KoProgressUpdater pu(&bar);
0024     KoUpdaterPtr updater = pu.startSubtask();
0025 
0026     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0027     QImage image(TestUtil::fetchDataFileLazy("test_cage_transform.png"));
0028 
0029     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0030     dev->convertFromQImage(image, 0);
0031 
0032     KisPaintDeviceSP srcDev = new KisPaintDevice(*dev);
0033 
0034     QVector<QPointF> origPoints;
0035     QVector<QPointF> transfPoints;
0036 
0037     QRectF bounds(dev->exactBounds());
0038 
0039     origPoints << bounds.topLeft();
0040     origPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
0041     origPoints << 0.5 * (bounds.topLeft() + bounds.bottomRight());
0042     origPoints << 0.5 * (bounds.topRight() + bounds.bottomRight());
0043     origPoints << bounds.bottomRight();
0044     origPoints << bounds.bottomLeft();
0045 
0046     if (!clockwise) {
0047         std::reverse(origPoints.begin(), origPoints.end());
0048     }
0049 
0050     if (unityTransform) {
0051         transfPoints = origPoints;
0052     } else {
0053         transfPoints << bounds.topLeft();
0054         transfPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
0055         transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight());
0056         transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) +
0057             (bounds.bottomLeft() - bounds.topLeft());
0058         transfPoints << bounds.bottomLeft() +
0059             (bounds.bottomLeft() - bounds.topLeft());
0060         transfPoints << bounds.bottomLeft();
0061 
0062         if (!clockwise) {
0063             std::reverse(transfPoints.begin(), transfPoints.end());
0064         }
0065     }
0066 
0067     KisCageTransformWorker worker(dev->region().boundingRect(),
0068                                   origPoints,
0069                                   updater,
0070                                   pixelPrecision);
0071 
0072     QImage result;
0073     QPointF srcQImageOffset(0, 0);
0074     QPointF dstQImageOffset;
0075 
0076     QBENCHMARK_ONCE {
0077         if (!testQImage) {
0078             worker.prepareTransform();
0079             if (!benchmarkPrepareOnly) {
0080                 worker.setTransformedCage(transfPoints);
0081                 worker.run(srcDev, dev);
0082 
0083             }
0084         } else {
0085             QImage srcImage = image;
0086             QImage image2 = QImage(image.size(), QImage::Format_ARGB32);
0087             QPainter gc(&image2);
0088             gc.drawImage(QPoint(), srcImage);
0089             gc.end();
0090             image = image2;
0091 
0092             KisCageTransformWorker qimageWorker(image,
0093                                                 srcQImageOffset,
0094                                                 origPoints,
0095                                                 updater,
0096                                                 pixelPrecision);
0097             qimageWorker.prepareTransform();
0098             qimageWorker.setTransformedCage(transfPoints);
0099             result = qimageWorker.runOnQImage(&dstQImageOffset);
0100         }
0101     }
0102 
0103     QString testName = QString("%1_%2")
0104         .arg(clockwise ? "clk" : "cclk")
0105         .arg(unityTransform ? "unity" : "normal");
0106 
0107     if (testQImage) {
0108         QVERIFY(TestUtil::checkQImage(result, "cage_transform_test", "cage_qimage", testName, 1, 1));
0109     } else if (!benchmarkPrepareOnly && pixelPrecision == 8) {
0110 
0111         result = dev->convertToQImage(0);
0112         QVERIFY(TestUtil::checkQImage(result, "cage_transform_test", "cage", testName, 1, 1));
0113     }
0114 }
0115 
0116 void KisCageTransformWorkerTest::testCageClockwise()
0117 {
0118     testCage(true, false);
0119 }
0120 
0121 void KisCageTransformWorkerTest::testCageClockwisePrepareOnly()
0122 {
0123     testCage(true, false, true);
0124 }
0125 
0126 void KisCageTransformWorkerTest::testCageClockwisePixelPrecision4()
0127 {
0128     testCage(true, false, false, 4);
0129 }
0130 
0131 void KisCageTransformWorkerTest::testCageClockwisePixelPrecision8QImage()
0132 {
0133     testCage(true, false, false, 8, true);
0134 }
0135 
0136 void KisCageTransformWorkerTest::testCageCounterclockwise()
0137 {
0138     testCage(false, false);
0139 }
0140 
0141 void KisCageTransformWorkerTest::testCageClockwiseUnity()
0142 {
0143     testCage(true, true);
0144 }
0145 
0146 void KisCageTransformWorkerTest::testCageCounterclockwiseUnity()
0147 {
0148     testCage(false, true);
0149 }
0150 
0151 #include <QtGlobal>
0152 
0153 
0154 QPointF generatePoint(const QRectF &rc)
0155 {
0156     qreal cx = qreal(qrand()) / RAND_MAX;
0157     qreal cy = qreal(qrand()) / RAND_MAX;
0158 
0159     QPointF diff = rc.bottomRight() - rc.topLeft();
0160 
0161     QPointF pt = rc.topLeft() + QPointF(cx * diff.x(), cy * diff.y());
0162     return pt;
0163 }
0164 
0165 void KisCageTransformWorkerTest::stressTestRandomCages()
0166 {
0167     TestUtil::TestProgressBar bar;
0168     KoProgressUpdater pu(&bar);
0169     KoUpdaterPtr updater = pu.startSubtask();
0170 
0171     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
0172     QImage image(TestUtil::fetchDataFileLazy("test_cage_transform.png"));
0173 
0174     KisPaintDeviceSP dev = new KisPaintDevice(cs);
0175     dev->convertFromQImage(image, 0);
0176 
0177     KisPaintDeviceSP dstDev = new KisPaintDevice(cs);
0178 
0179     const int pixelPrecision = 8;
0180     QRectF bounds(dev->exactBounds());
0181 
0182     qsrand(1);
0183 
0184     for (int numPoints = 4; numPoints < 15; numPoints+=5) {
0185         for (int j = 0; j < 200; j++) {
0186             QVector<QPointF> origPoints;
0187             QVector<QPointF> transfPoints;
0188 
0189             dbgKrita << ppVar(j);
0190 
0191             for (int i = 0; i < numPoints; i++) {
0192                 origPoints << generatePoint(bounds);
0193                 transfPoints << generatePoint(bounds);
0194             }
0195 
0196             // no just hope it doesn't crash ;)
0197             KisCageTransformWorker worker(dev->region().boundingRect(),
0198                                           origPoints,
0199                                           updater,
0200                                           pixelPrecision);
0201             worker.prepareTransform();
0202             worker.setTransformedCage(transfPoints);
0203             worker.run(dev, dstDev);
0204         }
0205     }
0206 }
0207 
0208 #include "kis_green_coordinates_math.h"
0209 
0210 void KisCageTransformWorkerTest::testUnityGreenCoordinates()
0211 {
0212     QVector<QPointF> origPoints;
0213     QVector<QPointF> transfPoints;
0214 
0215     QRectF bounds(0,0,300,300);
0216 
0217     origPoints << bounds.topLeft();
0218     origPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
0219     origPoints << 0.5 * (bounds.topLeft() + bounds.bottomRight());
0220     origPoints << 0.5 * (bounds.topRight() + bounds.bottomRight());
0221     origPoints << bounds.bottomRight();
0222     origPoints << bounds.bottomLeft();
0223 
0224     transfPoints = origPoints;
0225 
0226     QVector<QPointF> points;
0227     points << QPointF(10,10);
0228     points << QPointF(140,10);
0229     points << QPointF(140,140);
0230     points << QPointF(10,140);
0231 
0232     points << QPointF(10,160);
0233     points << QPointF(140,160);
0234     points << QPointF(140,290);
0235     points << QPointF(10,290);
0236 
0237     points << QPointF(160,160);
0238     points << QPointF(290,160);
0239     points << QPointF(290,290);
0240     points << QPointF(160,290);
0241 
0242     KisGreenCoordinatesMath cage;
0243 
0244     cage.precalculateGreenCoordinates(origPoints, points);
0245     cage.generateTransformedCageNormals(transfPoints);
0246 
0247     QVector<QPointF> newPoints;
0248 
0249     for (int i = 0; i < points.size(); i++) {
0250         newPoints << cage.transformedPoint(i, transfPoints);
0251         QCOMPARE(points[i], newPoints.last());
0252     }
0253 }
0254 
0255 #include "kis_algebra_2d.h"
0256 
0257 void KisCageTransformWorkerTest::testTransformAsBase()
0258 {
0259     QPointF t(1.0, 0.0);
0260     QPointF b1(1.0, 0.0);
0261     QPointF b2(2.0, 0.0);
0262     QPointF result;
0263 
0264 
0265     t = QPointF(1.0, 0.0);
0266     b1 = QPointF(1.0, 0.0);
0267     b2 = QPointF(2.0, 0.0);
0268     result = KisAlgebra2D::transformAsBase(t, b1, b2);
0269     QCOMPARE(result, QPointF(2.0, 0.0));
0270 
0271     t = QPointF(1.0, 0.0);
0272     b1 = QPointF(1.0, 0.0);
0273     b2 = QPointF(0.0, 1.0);
0274     result = KisAlgebra2D::transformAsBase(t, b1, b2);
0275     QCOMPARE(result, QPointF(0.0, 1.0));
0276 
0277     t = QPointF(1.0, 0.0);
0278     b1 = QPointF(1.0, 0.0);
0279     b2 = QPointF(0.0, 2.0);
0280     result = KisAlgebra2D::transformAsBase(t, b1, b2);
0281     QCOMPARE(result, QPointF(0.0, 2.0));
0282 
0283     t = QPointF(0.0, 1.0);
0284     b1 = QPointF(1.0, 0.0);
0285     b2 = QPointF(2.0, 0.0);
0286     result = KisAlgebra2D::transformAsBase(t, b1, b2);
0287     QCOMPARE(result, QPointF(0.0, 2.0));
0288 
0289     t = QPointF(0.0, 1.0);
0290     b1 = QPointF(1.0, 0.0);
0291     b2 = QPointF(0.0, 1.0);
0292     result = KisAlgebra2D::transformAsBase(t, b1, b2);
0293     QCOMPARE(result, QPointF(-1.0, 0.0));
0294 
0295     t = QPointF(0.0, 1.0);
0296     b1 = QPointF(1.0, 0.0);
0297     b2 = QPointF(0.0, 2.0);
0298     result = KisAlgebra2D::transformAsBase(t, b1, b2);
0299     QCOMPARE(result, QPointF(-2.0, 0.0));
0300 }
0301 
0302 void KisCageTransformWorkerTest::testAngleBetweenVectors()
0303 {
0304     QPointF b1(1.0, 0.0);
0305     QPointF b2(2.0, 0.0);
0306     qreal result;
0307 
0308     b1 = QPointF(1.0, 0.0);
0309     b2 = QPointF(0.0, 1.0);
0310     result = KisAlgebra2D::angleBetweenVectors(b1, b2);
0311     QCOMPARE(result, M_PI_2);
0312 
0313     b1 = QPointF(1.0, 0.0);
0314     b2 = QPointF(std::sqrt(0.5), std::sqrt(0.5));
0315     result = KisAlgebra2D::angleBetweenVectors(b1, b2);
0316     QCOMPARE(result, M_PI / 4);
0317 
0318     QTransform t;
0319     t.rotateRadians(M_PI / 4);
0320     QCOMPARE(t.map(b1), b2);
0321 }
0322 
0323 KISTEST_MAIN(KisCageTransformWorkerTest)