File indexing completed on 2024-12-22 04:12:54
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_zoom_and_pan_test.h" 0008 0009 #include <cmath> 0010 #include <simpletest.h> 0011 0012 #include <kis_filter_configuration.h> 0013 #include <testutil.h> 0014 #include "qimage_based_test.h" 0015 0016 #include <kactioncollection.h> 0017 0018 #include "kis_config.h" 0019 0020 #include "KisMainWindow.h" 0021 #include "KoZoomController.h" 0022 #include "KisDocument.h" 0023 #include "KisPart.h" 0024 #include "KisViewManager.h" 0025 #include "KisView.h" 0026 #include "kis_canvas2.h" 0027 #include "kis_canvas_controller.h" 0028 #include "kis_coordinates_converter.h" 0029 #include "kis_filter_strategy.h" 0030 0031 #include "testui.h" 0032 0033 class ZoomAndPanTester : public TestUtil::QImageBasedTest 0034 { 0035 public: 0036 ZoomAndPanTester() 0037 // we are not going to use our own QImage sets,so 0038 // just exploit the set of the selection manager test 0039 : QImageBasedTest("selection_manager_test") 0040 { 0041 m_undoStore = new KisSurrogateUndoStore(); 0042 m_image = createImage(m_undoStore); 0043 m_image->initialRefreshGraph(); 0044 QVERIFY(checkLayersInitial(m_image)); 0045 0046 m_doc = KisPart::instance()->createDocument(); 0047 0048 m_doc->setCurrentImage(m_image); 0049 0050 m_mainWindow = KisPart::instance()->createMainWindow(); 0051 m_view = new KisView(m_doc, m_mainWindow->viewManager(), m_mainWindow); 0052 0053 m_image->refreshGraph(); 0054 0055 m_mainWindow->show(); 0056 } 0057 0058 ~ZoomAndPanTester() { 0059 m_image->waitForDone(); 0060 QApplication::processEvents(); 0061 0062 delete m_mainWindow; 0063 delete m_doc; 0064 0065 /** 0066 * The event queue may have up to 200k events 0067 * by the time all the tests are finished. Removing 0068 * all of them may last forever, so clear them after 0069 * every single test is finished 0070 */ 0071 QApplication::removePostedEvents(0); 0072 } 0073 0074 QPointer<KisView> view() { 0075 return m_view; 0076 } 0077 0078 KisMainWindow* mainWindow() { 0079 return m_mainWindow; 0080 } 0081 0082 KisImageWSP image() { 0083 return m_image; 0084 } 0085 0086 KisCanvas2* canvas() { 0087 return m_view->canvasBase(); 0088 } 0089 0090 QWidget* canvasWidget() { 0091 return m_view->canvasBase()->canvasWidget(); 0092 } 0093 0094 KoZoomController* zoomController() { 0095 return m_view->zoomController(); 0096 } 0097 0098 KisCanvasController* canvasController() { 0099 return dynamic_cast<KisCanvasController*>(m_view->canvasController()); 0100 } 0101 0102 const KisCoordinatesConverter* coordinatesConverter() { 0103 return m_view->canvasBase()->coordinatesConverter(); 0104 } 0105 0106 private: 0107 KisSurrogateUndoStore *m_undoStore; 0108 KisImageSP m_image; 0109 KisDocument *m_doc; 0110 QPointer<KisView>m_view; 0111 KisMainWindow *m_mainWindow; 0112 }; 0113 0114 template<class P, class T> 0115 inline bool compareWithRounding(const P &pt0, const P &pt1, T tolerance) 0116 { 0117 return qAbs(pt0.x() - pt1.x()) <= tolerance && 0118 qAbs(pt0.y() - pt1.y()) <= tolerance; 0119 } 0120 0121 bool verifyOffset(ZoomAndPanTester &t, const QPoint &offset) { 0122 0123 if (t.coordinatesConverter()->documentOffset() != offset) { 0124 dbgKrita << "########################"; 0125 dbgKrita << "Expected Offset:" << offset; 0126 dbgKrita << "Actual values:"; 0127 dbgKrita << "Offset:" << t.coordinatesConverter()->documentOffset(); 0128 dbgKrita << "wsize:" << t.canvasWidget()->size(); 0129 dbgKrita << "vport:" << t.canvasController()->viewportSize(); 0130 dbgKrita << "pref:" << t.canvasController()->preferredCenter(); 0131 dbgKrita << "########################"; 0132 } 0133 0134 return t.coordinatesConverter()->documentOffset() == offset; 0135 } 0136 0137 bool KisZoomAndPanTest::checkPan(ZoomAndPanTester &t, QPoint shift) 0138 { 0139 QPoint oldOffset = t.coordinatesConverter()->documentOffset(); 0140 QPointF oldPrefCenter = t.canvasController()->preferredCenter(); 0141 0142 t.canvasController()->pan(shift); 0143 0144 QPoint newOffset = t.coordinatesConverter()->documentOffset(); 0145 QPointF newPrefCenter = t.canvasController()->preferredCenter(); 0146 QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft(); 0147 0148 QPoint expectedOffset = oldOffset + shift; 0149 QPointF expectedPrefCenter = oldPrefCenter + shift; 0150 0151 0152 // no tolerance accepted for pan 0153 bool offsetAsExpected = newOffset == expectedOffset; 0154 0155 // rounding can happen due to the scroll bars being the main 0156 // source of the offset 0157 bool preferredCenterAsExpected = 0158 compareWithRounding(expectedPrefCenter, newPrefCenter, 1.0); 0159 0160 bool topLeftAsExpected = newTopLeft.toPoint() == -newOffset; 0161 0162 if (!offsetAsExpected || 0163 !preferredCenterAsExpected || 0164 !topLeftAsExpected) { 0165 0166 dbgKrita << "***** PAN *****************"; 0167 0168 if(!offsetAsExpected) { 0169 dbgKrita << " ### Offset invariant broken"; 0170 } 0171 0172 if(!preferredCenterAsExpected) { 0173 dbgKrita << " ### Preferred center invariant broken"; 0174 } 0175 0176 if(!topLeftAsExpected) { 0177 dbgKrita << " ### TopLeft invariant broken"; 0178 } 0179 0180 dbgKrita << ppVar(expectedOffset); 0181 dbgKrita << ppVar(expectedPrefCenter); 0182 dbgKrita << ppVar(oldOffset) << ppVar(newOffset); 0183 dbgKrita << ppVar(oldPrefCenter) << ppVar(newPrefCenter); 0184 dbgKrita << ppVar(newTopLeft); 0185 dbgKrita << "***************************"; 0186 } 0187 0188 return offsetAsExpected && preferredCenterAsExpected && topLeftAsExpected; 0189 } 0190 0191 bool KisZoomAndPanTest::checkInvariants(const QPointF &baseFlakePoint, 0192 const QPoint &oldOffset, 0193 const QPointF &oldPreferredCenter, 0194 qreal oldZoom, 0195 const QPoint &newOffset, 0196 const QPointF &newPreferredCenter, 0197 qreal newZoom, 0198 const QPointF &newTopLeft, 0199 const QSize &oldDocumentSize) 0200 { 0201 qreal k = newZoom / oldZoom; 0202 0203 QPointF expectedOffset = oldOffset + (k - 1) * baseFlakePoint; 0204 QPointF expectedPreferredCenter = oldPreferredCenter + (k - 1) * baseFlakePoint; 0205 0206 qreal oldPreferredCenterFractionX = 1.0 * oldPreferredCenter.x() / oldDocumentSize.width(); 0207 qreal oldPreferredCenterFractionY = 1.0 * oldPreferredCenter.y() / oldDocumentSize.height(); 0208 0209 qreal roundingTolerance = 0210 qMax(qreal(1.0), qMax(oldPreferredCenterFractionX, oldPreferredCenterFractionY) / k); 0211 0212 /** 0213 * In the computation of the offset two roundings happen: 0214 * first for the computation of oldOffset and the second 0215 * for the computation of newOffset. So the maximum tolerance 0216 * should equal 2. 0217 */ 0218 bool offsetAsExpected = 0219 compareWithRounding(expectedOffset, QPointF(newOffset), 2 * roundingTolerance); 0220 0221 /** 0222 * Rounding for the preferred center happens due to the rounding 0223 * of the document size while zooming. The wider the step of the 0224 * zooming, the bigger tolerance should be 0225 */ 0226 bool preferredCenterAsExpected = 0227 compareWithRounding(expectedPreferredCenter, newPreferredCenter, 0228 roundingTolerance); 0229 0230 bool topLeftAsExpected = newTopLeft.toPoint() == -newOffset; 0231 0232 if (!offsetAsExpected || 0233 !preferredCenterAsExpected || 0234 !topLeftAsExpected) { 0235 0236 dbgKrita << "***** ZOOM ****************"; 0237 0238 if(!offsetAsExpected) { 0239 dbgKrita << " ### Offset invariant broken"; 0240 } 0241 0242 if(!preferredCenterAsExpected) { 0243 dbgKrita << " ### Preferred center invariant broken"; 0244 } 0245 0246 if(!topLeftAsExpected) { 0247 dbgKrita << " ### TopLeft invariant broken"; 0248 } 0249 0250 dbgKrita << ppVar(expectedOffset); 0251 dbgKrita << ppVar(expectedPreferredCenter); 0252 dbgKrita << ppVar(oldOffset) << ppVar(newOffset); 0253 dbgKrita << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter); 0254 dbgKrita << ppVar(oldPreferredCenterFractionX); 0255 dbgKrita << ppVar(oldPreferredCenterFractionY); 0256 dbgKrita << ppVar(oldZoom) << ppVar(newZoom); 0257 dbgKrita << ppVar(baseFlakePoint); 0258 dbgKrita << ppVar(newTopLeft); 0259 dbgKrita << ppVar(roundingTolerance); 0260 dbgKrita << "***************************"; 0261 } 0262 0263 return offsetAsExpected && preferredCenterAsExpected && topLeftAsExpected; 0264 } 0265 0266 bool KisZoomAndPanTest::checkZoomWithAction(ZoomAndPanTester &t, qreal newZoom, bool limitedZoom) 0267 { 0268 QPoint oldOffset = t.coordinatesConverter()->documentOffset(); 0269 QPointF oldPrefCenter = t.canvasController()->preferredCenter(); 0270 qreal oldZoom = t.zoomController()->zoomAction()->effectiveZoom(); 0271 QSize oldDocumentSize = t.canvasController()->documentSize().toSize(); 0272 0273 t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, newZoom); 0274 0275 QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft(); 0276 0277 return checkInvariants(oldPrefCenter, 0278 oldOffset, 0279 oldPrefCenter, 0280 oldZoom, 0281 t.coordinatesConverter()->documentOffset(), 0282 t.canvasController()->preferredCenter(), 0283 limitedZoom ? oldZoom : newZoom, 0284 newTopLeft, 0285 oldDocumentSize); 0286 } 0287 0288 bool KisZoomAndPanTest::checkZoomWithWheel(ZoomAndPanTester &t, const QPoint &widgetPoint, qreal zoomCoeff, bool limitedZoom) 0289 { 0290 QPoint oldOffset = t.coordinatesConverter()->documentOffset(); 0291 QPointF oldPrefCenter = t.canvasController()->preferredCenter(); 0292 qreal oldZoom = t.zoomController()->zoomAction()->effectiveZoom(); 0293 QSize oldDocumentSize = t.canvasController()->documentSize().toSize(); 0294 0295 t.canvasController()->zoomRelativeToPoint(widgetPoint, zoomCoeff); 0296 0297 QPointF newTopLeft = t.coordinatesConverter()->imageRectInWidgetPixels().topLeft(); 0298 0299 return checkInvariants(oldOffset + widgetPoint, 0300 oldOffset, 0301 oldPrefCenter, 0302 oldZoom, 0303 t.coordinatesConverter()->documentOffset(), 0304 t.canvasController()->preferredCenter(), 0305 limitedZoom ? oldZoom : zoomCoeff * oldZoom, 0306 newTopLeft, 0307 oldDocumentSize); 0308 } 0309 0310 void KisZoomAndPanTest::testZoom100ChangingWidgetSize() 0311 { 0312 ZoomAndPanTester t; 0313 0314 QCOMPARE(t.image()->size(), QSize(640,441)); 0315 QCOMPARE(t.image()->xRes(), 1.0); 0316 QCOMPARE(t.image()->yRes(), 1.0); 0317 0318 t.canvasController()->resize(QSize(1000,1000)); 0319 t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0); 0320 t.canvasController()->setPreferredCenter(QPoint(320,220)); 0321 0322 QCOMPARE(t.canvasWidget()->size(), QSize(983,983)); 0323 QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize().toSize()); 0324 QVERIFY(verifyOffset(t, QPoint(-171,-271))); 0325 0326 t.canvasController()->resize(QSize(700,700)); 0327 0328 QCOMPARE(t.canvasWidget()->size(), QSize(683,683)); 0329 QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize().toSize()); 0330 QVERIFY(verifyOffset(t, QPoint(-171,-271))); 0331 0332 t.canvasController()->setPreferredCenter(QPoint(320,220)); 0333 0334 QVERIFY(verifyOffset(t, QPoint(-21,-121))); 0335 0336 t.canvasController()->resize(QSize(400,400)); 0337 0338 QCOMPARE(t.canvasWidget()->size(), QSize(383,383)); 0339 QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize().toSize()); 0340 QVERIFY(verifyOffset(t, QPoint(-21,-121))); 0341 0342 t.canvasController()->setPreferredCenter(QPoint(320,220)); 0343 0344 QVERIFY(verifyOffset(t, QPoint(129,29))); 0345 0346 t.canvasController()->pan(QPoint(100,100)); 0347 0348 QVERIFY(verifyOffset(t, QPoint(229,129))); 0349 } 0350 0351 void KisZoomAndPanTest::initializeViewport(ZoomAndPanTester &t, bool fullscreenMode, bool rotate, bool mirror) 0352 { 0353 QCOMPARE(t.image()->size(), QSize(640,441)); 0354 QCOMPARE(t.image()->xRes(), 1.0); 0355 QCOMPARE(t.image()->yRes(), 1.0); 0356 0357 t.canvasController()->resize(QSize(500,500)); 0358 t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0); 0359 t.canvasController()->setPreferredCenter(QPoint(320,220)); 0360 0361 QCOMPARE(t.canvasWidget()->size(), QSize(483,483)); 0362 QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize().toSize()); 0363 QVERIFY(verifyOffset(t, QPoint(79,-21))); 0364 0365 if (fullscreenMode) { 0366 QCOMPARE(t.canvasController()->preferredCenter(), QPointF(320,220)); 0367 0368 QAction *action = t.view()->viewManager()->actionCollection()->action("view_show_canvas_only"); 0369 action->setChecked(true); 0370 0371 QVERIFY(verifyOffset(t, QPoint(79,-21))); 0372 QCOMPARE(t.canvasController()->preferredCenter(), QPointF(329,220)); 0373 0374 0375 t.canvasController()->resize(QSize(483,483)); 0376 QCOMPARE(t.canvasWidget()->size(), QSize(483,483)); 0377 QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize().toSize()); 0378 QVERIFY(verifyOffset(t, QPoint(79,-21))); 0379 0380 0381 /** 0382 * FIXME: here is a small flaw in KoCanvasControllerWidget 0383 * We cannot set the center point explicitly, because it'll be rounded 0384 * up by recenterPreferred function, so real center point will be 0385 * different. Make the preferredCenter() return real center of the 0386 * image instead of the set value 0387 */ 0388 QCOMPARE(t.canvasController()->preferredCenter(), QPointF(320.5,220)); 0389 } 0390 0391 if (rotate) { 0392 t.canvasController()->rotateCanvas(90); 0393 QVERIFY(verifyOffset(t, QPoint(-21,79))); 0394 QVERIFY(compareWithRounding(QPointF(220,320), t.canvasController()->preferredCenter(), 2)); 0395 QCOMPARE(t.coordinatesConverter()->imageRectInWidgetPixels().topLeft().toPoint(), -t.coordinatesConverter()->documentOffset()); 0396 } 0397 0398 if (mirror) { 0399 t.canvasController()->mirrorCanvas(true); 0400 QVERIFY(verifyOffset(t, QPoint(78, -21))); 0401 QVERIFY(compareWithRounding(QPointF(320,220), t.canvasController()->preferredCenter(), 2)); 0402 QCOMPARE(t.coordinatesConverter()->imageRectInWidgetPixels().topLeft().toPoint(), -t.coordinatesConverter()->documentOffset()); 0403 } 0404 } 0405 0406 void KisZoomAndPanTest::testSequentialActionZoomAndPan(bool fullscreenMode, bool rotate, bool mirror) 0407 { 0408 ZoomAndPanTester t; 0409 initializeViewport(t, fullscreenMode, rotate, mirror); 0410 0411 QVERIFY(checkZoomWithAction(t, 0.5)); 0412 QVERIFY(checkPan(t, QPoint(100,100))); 0413 0414 QVERIFY(checkZoomWithAction(t, 0.25)); 0415 QVERIFY(checkPan(t, QPoint(-100,-100))); 0416 0417 QVERIFY(checkZoomWithAction(t, 0.35)); 0418 QVERIFY(checkPan(t, QPoint(100,100))); 0419 0420 QVERIFY(checkZoomWithAction(t, 0.45)); 0421 QVERIFY(checkPan(t, QPoint(100,100))); 0422 0423 QVERIFY(checkZoomWithAction(t, 0.85)); 0424 QVERIFY(checkPan(t, QPoint(-100,-100))); 0425 0426 QVERIFY(checkZoomWithAction(t, 2.35)); 0427 QVERIFY(checkPan(t, QPoint(100,100))); 0428 } 0429 0430 void KisZoomAndPanTest::testSequentialWheelZoomAndPan(bool fullscreenMode, bool rotate, bool mirror) 0431 { 0432 ZoomAndPanTester t; 0433 initializeViewport(t, fullscreenMode, rotate, mirror); 0434 0435 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5)); 0436 QVERIFY(checkPan(t, QPoint(100,100))); 0437 0438 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5)); 0439 QVERIFY(checkPan(t, QPoint(-100,-100))); 0440 0441 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 1.25)); 0442 QVERIFY(checkPan(t, QPoint(100,100))); 0443 0444 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 1.5)); 0445 QVERIFY(checkPan(t, QPoint(100,100))); 0446 0447 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 2.5)); 0448 QVERIFY(checkPan(t, QPoint(-100,-100))); 0449 0450 // check one point which is outside the widget 0451 QVERIFY(checkZoomWithWheel(t, QPoint(-100,100), 2.5)); 0452 QVERIFY(checkPan(t, QPoint(-100,-100))); 0453 0454 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5)); 0455 QVERIFY(checkPan(t, QPoint(-100,-100))); 0456 } 0457 0458 void KisZoomAndPanTest::testSequentialActionZoomAndPan() 0459 { 0460 testSequentialActionZoomAndPan(false, false, false); 0461 } 0462 0463 void KisZoomAndPanTest::testSequentialActionZoomAndPanFullscreen() 0464 { 0465 testSequentialActionZoomAndPan(true, false, false); 0466 } 0467 0468 void KisZoomAndPanTest::testSequentialActionZoomAndPanRotate() 0469 { 0470 testSequentialActionZoomAndPan(false, true, false); 0471 } 0472 0473 void KisZoomAndPanTest::testSequentialActionZoomAndPanRotateFullscreen() 0474 { 0475 testSequentialActionZoomAndPan(true, true, false); 0476 } 0477 0478 void KisZoomAndPanTest::testSequentialActionZoomAndPanMirror() 0479 { 0480 testSequentialActionZoomAndPan(false, false, true); 0481 } 0482 0483 void KisZoomAndPanTest::testSequentialWheelZoomAndPan() 0484 { 0485 testSequentialWheelZoomAndPan(false, false, false); 0486 } 0487 0488 void KisZoomAndPanTest::testSequentialWheelZoomAndPanFullscreen() 0489 { 0490 testSequentialWheelZoomAndPan(true, false, false); 0491 } 0492 0493 void KisZoomAndPanTest::testSequentialWheelZoomAndPanRotate() 0494 { 0495 testSequentialWheelZoomAndPan(false, true, false); 0496 } 0497 0498 void KisZoomAndPanTest::testSequentialWheelZoomAndPanRotateFullscreen() 0499 { 0500 testSequentialWheelZoomAndPan(true, true, false); 0501 } 0502 0503 void KisZoomAndPanTest::testSequentialWheelZoomAndPanMirror() 0504 { 0505 testSequentialWheelZoomAndPan(false, false, true); 0506 } 0507 0508 void KisZoomAndPanTest::testZoomOnBorderZoomLevels() 0509 { 0510 ZoomAndPanTester t; 0511 initializeViewport(t, false, false, false); 0512 0513 // QPoint widgetPoint(100,100); 0514 0515 warnKrita << "WARNING: testZoomOnBorderZoomLevels() is disabled due to some changes in KoZoomMode::minimum/maximumZoom()"; 0516 return; 0517 0518 // test min zoom level 0519 t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, KoZoomMode::minimumZoom()); 0520 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 0.5, true)); 0521 QVERIFY(checkZoomWithAction(t, KoZoomMode::minimumZoom() * 0.5, true)); 0522 0523 // test max zoom level 0524 t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, KoZoomMode::maximumZoom()); 0525 QVERIFY(checkZoomWithWheel(t, QPoint(100,100), 2.0, true)); 0526 QVERIFY(checkZoomWithAction(t, KoZoomMode::maximumZoom() * 2.0, true)); 0527 } 0528 0529 inline QTransform correctionMatrix(qreal angle) 0530 { 0531 return QTransform(0,0,0,sin(M_PI * angle / 180),0,0,0,0,1); 0532 } 0533 0534 bool KisZoomAndPanTest::checkRotation(ZoomAndPanTester &t, qreal angle) 0535 { 0536 // save old values 0537 QPoint oldOffset = t.coordinatesConverter()->documentOffset(); 0538 QPointF oldCenteringCorrection = t.coordinatesConverter()->centeringCorrection(); 0539 QPointF oldPreferredCenter = t.canvasController()->preferredCenter(); 0540 QPointF oldRealCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint()); 0541 QSize oldDocumentSize = t.canvasController()->documentSize().toSize(); 0542 0543 qreal baseAngle = t.coordinatesConverter()->rotationAngle(); 0544 t.canvasController()->rotateCanvas(angle); 0545 0546 // save result values 0547 QPoint newOffset = t.coordinatesConverter()->documentOffset(); 0548 QPointF newCenteringCorrection = t.coordinatesConverter()->centeringCorrection(); 0549 QPointF newPreferredCenter = t.canvasController()->preferredCenter(); 0550 QPointF newRealCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint()); 0551 QSize newDocumentSize = t.canvasController()->documentSize().toSize(); 0552 0553 0554 // calculate theoretical preferred center 0555 QTransform rot; 0556 rot.rotate(angle); 0557 0558 QSizeF dSize = t.coordinatesConverter()->imageSizeInFlakePixels(); 0559 QPointF dPoint(dSize.width(), dSize.height()); 0560 0561 QPointF expectedPreferredCenter = 0562 (oldPreferredCenter - dPoint * correctionMatrix(baseAngle)) * rot + 0563 dPoint * correctionMatrix(baseAngle + angle); 0564 0565 // calculate theoretical offset based on the real preferred center 0566 QPointF wPoint(t.canvasWidget()->size().width(), t.canvasWidget()->size().height()); 0567 QPointF expectedOldOffset = oldPreferredCenter - 0.5 * wPoint; 0568 QPointF expectedNewOffset = newPreferredCenter - 0.5 * wPoint; 0569 0570 bool preferredCenterAsExpected = 0571 compareWithRounding(expectedPreferredCenter, newPreferredCenter, 2); 0572 bool oldOffsetAsExpected = 0573 compareWithRounding(expectedOldOffset + oldCenteringCorrection, QPointF(oldOffset), 2); 0574 bool newOffsetAsExpected = 0575 compareWithRounding(expectedNewOffset + newCenteringCorrection, QPointF(newOffset), 3); 0576 0577 qreal zoom = t.zoomController()->zoomAction()->effectiveZoom(); 0578 bool realCenterPointAsExpected = 0579 compareWithRounding(oldRealCenterPoint, newRealCenterPoint, 2/zoom); 0580 0581 0582 if (!oldOffsetAsExpected || 0583 !newOffsetAsExpected || 0584 !preferredCenterAsExpected || 0585 !realCenterPointAsExpected) { 0586 0587 dbgKrita << "***** ROTATE **************"; 0588 0589 if(!oldOffsetAsExpected) { 0590 dbgKrita << " ### Old offset invariant broken"; 0591 } 0592 0593 if(!newOffsetAsExpected) { 0594 dbgKrita << " ### New offset invariant broken"; 0595 } 0596 0597 if(!preferredCenterAsExpected) { 0598 dbgKrita << " ### Preferred center invariant broken"; 0599 } 0600 0601 if(!realCenterPointAsExpected) { 0602 dbgKrita << " ### *Real* center invariant broken"; 0603 } 0604 0605 dbgKrita << ppVar(expectedOldOffset); 0606 dbgKrita << ppVar(expectedNewOffset); 0607 dbgKrita << ppVar(expectedPreferredCenter); 0608 dbgKrita << ppVar(oldOffset) << ppVar(newOffset); 0609 dbgKrita << ppVar(oldCenteringCorrection) << ppVar(newCenteringCorrection); 0610 dbgKrita << ppVar(oldPreferredCenter) << ppVar(newPreferredCenter); 0611 dbgKrita << ppVar(oldRealCenterPoint) << ppVar(newRealCenterPoint); 0612 dbgKrita << ppVar(oldDocumentSize) << ppVar(newDocumentSize); 0613 dbgKrita << ppVar(baseAngle) << "deg"; 0614 dbgKrita << ppVar(angle) << "deg"; 0615 dbgKrita << "***************************"; 0616 } 0617 0618 return preferredCenterAsExpected && oldOffsetAsExpected && newOffsetAsExpected && realCenterPointAsExpected; 0619 } 0620 0621 void KisZoomAndPanTest::testRotation(qreal vastScrolling, qreal zoom) 0622 { 0623 KisConfig cfg(false); 0624 cfg.setVastScrolling(vastScrolling); 0625 0626 ZoomAndPanTester t; 0627 0628 QCOMPARE(t.image()->size(), QSize(640,441)); 0629 QCOMPARE(t.image()->xRes(), 1.0); 0630 QCOMPARE(t.image()->yRes(), 1.0); 0631 0632 QPointF preferredCenter = zoom * t.image()->bounds().center(); 0633 0634 t.canvasController()->resize(QSize(500,500)); 0635 t.zoomController()->setZoom(KoZoomMode::ZOOM_CONSTANT, zoom); 0636 t.canvasController()->setPreferredCenter(preferredCenter.toPoint()); 0637 0638 QCOMPARE(t.canvasWidget()->size(), QSize(483,483)); 0639 QCOMPARE(t.canvasWidget()->size(), t.canvasController()->viewportSize().toSize()); 0640 0641 QPointF realCenterPoint = t.coordinatesConverter()->widgetToImage(t.coordinatesConverter()->widgetCenterPoint()); 0642 QPointF expectedCenterPoint = QPointF(t.image()->bounds().center()); 0643 0644 if(!compareWithRounding(realCenterPoint, expectedCenterPoint, 2/zoom)) { 0645 dbgKrita << "Failed to set initial center point"; 0646 dbgKrita << ppVar(expectedCenterPoint) << ppVar(realCenterPoint); 0647 QFAIL("FAIL: Failed to set initial center point"); 0648 } 0649 0650 QVERIFY(checkRotation(t, 30)); 0651 QVERIFY(checkRotation(t, 20)); 0652 QVERIFY(checkRotation(t, 10)); 0653 QVERIFY(checkRotation(t, 5)); 0654 QVERIFY(checkRotation(t, 5)); 0655 QVERIFY(checkRotation(t, 5)); 0656 0657 if(vastScrolling < 0.5 && zoom < 1) { 0658 warnKrita << "Disabling a few tests for vast scrolling =" 0659 << vastScrolling << ". See comment for more"; 0660 /** 0661 * We have to disable a couple of tests here for the case when 0662 * vastScrolling value is 0.2. The problem is that the centering 0663 * correction applied to the offset in 0664 * KisCanvasController::rotateCanvas pollutes the preferredCenter 0665 * value, because KoCanvasControllerWidget has no access to this 0666 * correction and cannot calculate the real value of the center of 0667 * the image. To fix this bug the calculation of correction 0668 * (aka "origin") should be moved to the KoCanvasControllerWidget 0669 * itself which would cause quite huge changes (including the change 0670 * of the external interface of it). Namely, we would have to 0671 * *calculate* offset from the value of the scroll bars, but not 0672 * use their values directly: 0673 * 0674 * offset = scrollBarValue - origin 0675 * 0676 * So now we just disable these unittests and allow a couple 0677 * of "jumping" bugs appear in vastScrolling < 0.5 modes, which 0678 * is, actually, not the default case. 0679 */ 0680 0681 } else { 0682 QVERIFY(checkRotation(t, 5)); 0683 QVERIFY(checkRotation(t, 5)); 0684 QVERIFY(checkRotation(t, 5)); 0685 } 0686 } 0687 0688 void KisZoomAndPanTest::testRotation_VastScrolling_1_0() 0689 { 0690 testRotation(0.9, 1.0); 0691 } 0692 0693 void KisZoomAndPanTest::testRotation_VastScrolling_0_5() 0694 { 0695 testRotation(0.9, 0.5); 0696 } 0697 0698 void KisZoomAndPanTest::testRotation_NoVastScrolling_1_0() 0699 { 0700 testRotation(0.2, 1.0); 0701 } 0702 0703 void KisZoomAndPanTest::testRotation_NoVastScrolling_0_5() 0704 { 0705 testRotation(0.2, 0.5); 0706 } 0707 0708 void KisZoomAndPanTest::testImageRescaled_0_5() 0709 { 0710 ZoomAndPanTester t; 0711 QApplication::processEvents(); 0712 initializeViewport(t, false, false, false); 0713 QApplication::processEvents(); 0714 QVERIFY(checkPan(t, QPoint(200,200))); 0715 QApplication::processEvents(); 0716 0717 QPointF oldStillPoint = 0718 t.coordinatesConverter()->imageRectInWidgetPixels().center(); 0719 0720 KisFilterStrategy *strategy = new KisBilinearFilterStrategy(); 0721 t.image()->scaleImage(QSize(320, 220), t.image()->xRes(), t.image()->yRes(), strategy); 0722 t.image()->waitForDone(); 0723 QApplication::processEvents(); 0724 delete strategy; 0725 0726 QPointF newStillPoint = 0727 t.coordinatesConverter()->imageRectInWidgetPixels().center(); 0728 0729 QVERIFY(compareWithRounding(oldStillPoint, newStillPoint, 1.0)); 0730 } 0731 0732 void KisZoomAndPanTest::testImageCropped() 0733 { 0734 ZoomAndPanTester t; 0735 QApplication::processEvents(); 0736 initializeViewport(t, false, false, false); 0737 QApplication::processEvents(); 0738 QVERIFY(checkPan(t, QPoint(-150,-150))); 0739 QApplication::processEvents(); 0740 0741 QPointF oldStillPoint = 0742 t.coordinatesConverter()->imageToWidget(QPointF(150,150)); 0743 0744 t.image()->cropImage(QRect(100,100,100,100)); 0745 t.image()->waitForDone(); 0746 QApplication::processEvents(); 0747 0748 QPointF newStillPoint = 0749 t.coordinatesConverter()->imageToWidget(QPointF(50,50)); 0750 0751 QVERIFY(compareWithRounding(oldStillPoint, newStillPoint, 1.0)); 0752 } 0753 0754 KISTEST_MAIN(KisZoomAndPanTest)