Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_coordinates_converter.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <cmath>
9
12
13#include <QtMath>
14#include <QTransform>
15#include <KoViewConverter.h>
16
17#include <kis_config.h>
18#include <kis_image.h>
19#include <kis_algebra_2d.h>
20#include <kis_assert.h>
21#include <KisValueCache.h>
22#include <KisPortingUtils.h>
23
24
81
97{
98 KisConfig cfg(true);
99
100 QSize documentSize = imageRectInWidgetPixels().toAlignedRect().size();
101 QPointF dPoint(documentSize.width(), documentSize.height());
102 QPointF wPoint(m_d->canvasWidgetSize.width(), m_d->canvasWidgetSize.height());
103
104 QPointF minOffset = -cfg.vastScrolling() * wPoint;
105 QPointF maxOffset = dPoint - wPoint + cfg.vastScrolling() * wPoint;
106
107 QPointF range = maxOffset - minOffset;
108
109 range.rx() = qMin(range.x(), (qreal)0.0);
110 range.ry() = qMin(range.y(), (qreal)0.0);
111
112 range /= 2;
113
114 return -range;
115}
116
118{
119 if (!m_d->canvasWidgetSize.isValid()) return;
120
121 KisConfig cfg(true);
122
123 const QRect refRect = imageToWidget(m_d->extraReferencesBounds).toAlignedRect();
124
125 QRect documentRect = imageRectInWidgetPixels().toAlignedRect();
126 QPointF dPointMax(qMax(documentRect.width(), refRect.right() + 1 - documentRect.x()),
127 qMax(documentRect.height(), refRect.bottom() + 1 - documentRect.y()));
128 QPointF dPointMin(qMin(0, refRect.left() - documentRect.x()),
129 qMin(0, refRect.top() - documentRect.y()));
130 QPointF wPoint(m_d->canvasWidgetSize.width(), m_d->canvasWidgetSize.height());
131
132 QPointF minOffset = dPointMin - cfg.vastScrolling() * wPoint;
133 QPointF maxOffset = dPointMax - wPoint + cfg.vastScrolling() * wPoint;
134
135 m_d->minimumOffset = minOffset.toPoint();
136 m_d->maximumOffset = maxOffset.toPoint();
137
138 const QRectF limitRect(m_d->minimumOffset, m_d->maximumOffset);
139
140 if (!limitRect.contains(m_d->documentOffset)) {
142 qDebug() << " corrected offset:" << m_d->documentOffset;
144 }
145}
146
148{
149 return m_d->minimumOffset;
150}
151
153{
154 return m_d->maximumOffset;
155}
156
178
180{
181 QPointF topLeft = imageRectInWidgetPixels().topLeft();
182 QPointF diff = (-topLeft) - m_d->documentOffset;
183 diff += centeringCorrection();
184 m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y());
185}
186
191
193{
194 m_d->imageToDocument = QTransform::fromScale(1 / m_d->imageXRes,
195 1 / m_d->imageYRes);
196
197 qreal zoomX, zoomY;
198 KoZoomHandler::zoom(&zoomX, &zoomY);
199 m_d->documentToFlake = QTransform::fromScale(zoomX, zoomY);
200
203
204 QRectF irect = imageRectInWidgetPixels();
205 QRectF wrect = QRectF(QPoint(0,0), m_d->canvasWidgetSize);
206 QRectF rrect = irect & wrect;
207
208 QTransform reversedTransform = flakeToWidgetTransform().inverted();
209 QRectF canvasBounds = reversedTransform.mapRect(rrect);
210 QPointF offset = canvasBounds.topLeft();
211
212 m_d->widgetToViewport = reversedTransform * QTransform::fromTranslate(-offset.x(), -offset.y());
213}
214
215
218
223
228
230{
233
234 // the widget center has changed, hence the preferred
235 // center changes as well
237}
238
243
245{
246 m_d->imageXRes = image->xRes();
247 m_d->imageYRes = image->yRes();
248
249 // we should **not** call setResolution() here, since
250 // it is a different kind of resolution that is used
251 // to convert the image to the physical size of the display
252
253 m_d->imageBounds = image->bounds();
256
257 if (m_d->canvasWidgetSize.isEmpty()) {
258 // if setImage() comes before setCanvasWidgetSize(), then just remember the
259 // proposed mode and postpone the actual recentering of the image
260 // (this case is supposed to happen in unittests only)
263 } else {
264 // the default mode after initialization is "Zoom Page"
265 setZoom(KoZoomMode::ZOOM_PAGE, 777.7, resolutionX(), resolutionY(), std::nullopt);
266 }
267}
268
270{
271 if (imageRect == m_d->extraReferencesBounds) return;
272
273 // this value affects scroll range only, so no need to do extra
274 // still point tracking
275 m_d->extraReferencesBounds = imageRect;
277}
278
279void KisCoordinatesConverter::setImageBounds(const QRect &rect, const QPointF oldImageStillPoint, const QPointF newImageStillPoint)
280{
281 if (rect == m_d->imageBounds) return;
282
283 const QPointF oldWidgetStillPoint = imageToWidget(oldImageStillPoint);
284
285 // we reset zoom mode to constant to make sure that
286 // the image changes visually for the user
288
292
293 const QPointF newWidgetStillPoint = imageToWidget(newImageStillPoint);
294 m_d->documentOffset = snapToDevicePixel(m_d->documentOffset + newWidgetStillPoint - oldWidgetStillPoint);
296
298}
299
301{
302 // we consiter the center of the image to be the still point
303 // on the canvas
304
305 if (qFuzzyCompare(xRes, m_d->imageXRes) && qFuzzyCompare(yRes, m_d->imageYRes)) return;
306
307 const QPointF oldImageCenter = imageCenterInWidgetPixel();
308
309 // we should **not** call setResolution() here, since
310 // it is a different kind of resolution that is used
311 // to convert the image to the physical size of the display
312
313 // we reset zoom mode to constant to make sure that
314 // the image changes visually for the user
316
317 m_d->imageXRes = xRes;
318 m_d->imageYRes = yRes;
321
322 const QPointF newImageCenter = imageCenterInWidgetPixel();
323 m_d->documentOffset = snapToDevicePixel(m_d->documentOffset + newImageCenter - oldImageCenter);
325
327}
328
330{
331 // when changing the offset manually, the mode is explicitly
332 // reset to constant
334
335 // The given offset is in widget logical pixels. In order to prevent fuzzy
336 // canvas rendering at 100% pixel-perfect zoom level when devicePixelRatio
337 // is not integral, we adjusts the offset to map to whole device pixels.
338
339 // Steps to reproduce the issue (when no snapping):
340 // 1) Download an image with 1px vertical black and white stripes
341 // 2) Enable fractional HiDPI support in Krita
342 // 3) Set display scaling to 1.5 or 2.5
343 // 4) Try to change offset of the image. If offset is unaligned, then
344 // the image will disappear on the canvas.
345
348
350}
351
356
358{
359 return QPoint(int(m_d->documentOffset.x()), int(m_d->documentOffset.y()));
360}
361
363{
364 return m_d->documentOffset;
365}
366
371
373{
374 return m_d->rotationAngle;
375}
376
378{
379 // when changing the offset manually, the mode is explicitly
380 // reset to constant, this method is used in unittests mostly
382
386}
387
389{
391
393 // in constant mode we just preserve the document offset
394 // (as much as we can in relation to the vast scroll factor)
395 } else {
403 setZoom(zoomMode(), 777.0, resolutionX(), resolutionY(), std::nullopt);
404 }
405}
406
408{
409 if (qFuzzyCompare(m_d->devicePixelRatio, 1.0)) return size;
410
411 // This is how QOpenGLCanvas sets the FBO and the viewport size. If
412 // devicePixelRatioF() is non-integral, the result is truncated.
413 // *Correction*: The FBO size is actually rounded, but the glViewport call
414 // uses integer truncation and that's what really matters.
415 const int viewportWidth = static_cast<int>(size.width() * m_d->devicePixelRatio);
416 const int viewportHeight = static_cast<int>(size.height() * m_d->devicePixelRatio);
417
418 // The widget size may be an integer but here we actually want to give
419 // KisCoordinatesConverter the logical viewport size aligned to device
420 // pixels.
421 return QSizeF(viewportWidth, viewportHeight) / m_d->devicePixelRatio;
422}
423
425{
426 // TODO: add an assert and a unittest to verify that there is no
427 // actual rounding happens, only intolerances!
428 return qFuzzyCompare(m_d->devicePixelRatio, 1.0) ?
429 m_d->canvasWidgetSize.toSize() :
431}
432
433void KisCoordinatesConverter::setZoom(KoZoomMode::Mode mode, qreal zoom, qreal resolutionX, qreal resolutionY, const std::optional<KoViewTransformStillPoint> &stillPoint)
434{
435 const int cfgMargin = zoomMarginSize();
436
437 auto updateDisplayResolution = [&]() {
438 if (!qFuzzyCompare(resolutionX, this->resolutionX()) || !qFuzzyCompare(resolutionY, this->resolutionY())) {
439 setResolution(resolutionX, resolutionY);
441 }
442 };
443
444 if(mode == KoZoomMode::ZOOM_CONSTANT) {
445 if(qFuzzyIsNull(zoom)) return;
446
450
451 KoViewTransformStillPoint effectiveStillPoint =
452 stillPoint ? *stillPoint :
454
455 updateDisplayResolution();
459
460 const QPointF newStillPoint = documentToWidget(effectiveStillPoint.docPoint());
461 const QPointF offset = newStillPoint - effectiveStillPoint.viewPoint();
464
465 if (stillPoint) {
467 }
468
469 } else if (mode == KoZoomMode::ZOOM_PAGE || mode == KoZoomMode::ZOOM_WIDTH || mode == KoZoomMode::ZOOM_HEIGHT) {
470 updateDisplayResolution();
472
474
475 const QSizeF documentSize = imageRectInWidgetPixels().size();
476 const qreal zoomCoeffX = (m_d->canvasWidgetSize.width() - 2 * cfgMargin) / documentSize.width();
477 const qreal zoomCoeffY = (m_d->canvasWidgetSize.height() - 2 * cfgMargin) / documentSize.height();
478
479 const bool fitToWidth = [&]() {
480 if (mode == KoZoomMode::ZOOM_PAGE) {
481 return zoomCoeffX < zoomCoeffY;
482 } else if (mode == KoZoomMode::ZOOM_HEIGHT) {
483 return false;
484 } else if (mode == KoZoomMode::ZOOM_WIDTH) {
485 return true;
486 }
487 Q_UNREACHABLE_RETURN(true);
488 }();
489
490 KoZoomHandler::setZoom(this->zoom() * (fitToWidth ? zoomCoeffX : zoomCoeffY));
493
494 const QPointF offset = imageCenterInWidgetPixel() - widgetCenterPoint();
495
496 QPointF newDocumentOffset = m_d->documentOffset + offset;
497
498 // just explicitly set minimal axis offset to zero to
499 // avoid imperfections of floating point numbers
500 if (fitToWidth) {
501 newDocumentOffset.setX(-cfgMargin);
502 } else {
503 newDocumentOffset.setY(-cfgMargin);
504 }
505
506 m_d->documentOffset = snapToDevicePixel(newDocumentOffset);
508
510 }
511}
512
513void KisCoordinatesConverter::zoomTo(const QRectF &zoomRectWidget)
514{
515 KIS_SAFE_ASSERT_RECOVER_RETURN(!zoomRectWidget.isEmpty());
516
517 const QPointF zoomPortionCenterInImagePixels = widgetToImage(zoomRectWidget.center());
518
519 const qreal zoomCoeffX = m_d->canvasWidgetSize.width() / zoomRectWidget.width();
520 const qreal zoomCoeffY = m_d->canvasWidgetSize.height() / zoomRectWidget.height();
521
522 const bool fitToWidth = zoomCoeffX < zoomCoeffY;
523
524 KoZoomHandler::setZoom(this->zoom() * (fitToWidth ? zoomCoeffX : zoomCoeffY));
527
528 const QPointF offset = imageToWidget(zoomPortionCenterInImagePixels) - widgetCenterPoint();
529 QPointF newDocumentOffset = m_d->documentOffset + offset;
530
531 m_d->documentOffset = snapToDevicePixel(newDocumentOffset);
533
535}
536
538{
539 qreal scaleX, scaleY;
540 this->imageScale(&scaleX, &scaleY);
541
542 if (scaleX != scaleY) {
543 qWarning() << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY));
544 }
545
546 // zoom by average of x and y
547 return 0.5 * (scaleX + scaleY);
548}
549
551{
552 qreal scaleX, scaleY;
553 this->imagePhysicalScale(&scaleX, &scaleY);
554
555 if (scaleX != scaleY) {
556 qWarning() << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY));
557 }
558
559 // zoom by average of x and y
560 return 0.5 * (scaleX + scaleY);
561}
562
567
569{
571
572 // Save the current transformation and angle to use as the base of the ongoing rotation.
575 m_d->isRotating = true;
576}
577
585
586void KisCoordinatesConverter::rotate(const std::optional<KoViewTransformStillPoint> &stillPoint, qreal angle)
587{
589
590 KoViewTransformStillPoint effectiveStillPoint =
591 stillPoint ? *stillPoint :
593
594 QTransform rot;
595 rot.rotate(angle);
596
598 {
599 // Modal (begin/end) rotation. Transform from the stable base.
601 m_d->rotationAngle = std::fmod(m_d->rotationBaseAngle + angle, 360.0);
602 }
603 else
604 {
605 // Immediate rotation, directly applied to the canvas transformation.
606 m_d->rotationAngle = std::fmod(m_d->rotationAngle + angle, 360.0);
607 }
608
609 {
610 const qreal numQuadrants = m_d->rotationAngle / 90.0;
611 m_d->rotationIsOrthogonal = std::floor(numQuadrants) == numQuadrants;
612 }
613
614 m_d->flakeToWidget *= rot;
617
618 const QPointF newStillPoint = documentToWidget(effectiveStillPoint.docPoint());
619 const QPointF offset = newStillPoint - effectiveStillPoint.viewPoint();
622
623 if (stillPoint) {
625 }
626}
627
628void KisCoordinatesConverter::mirror(const std::optional<KoViewTransformStillPoint> &stillPoint, bool mirrorXAxis, bool mirrorYAxis)
629{
630 bool keepOrientation = false; // XXX: Keep here for now, maybe some day we can restore the parameter again.
631
632 KoViewTransformStillPoint effectiveStillPoint =
633 stillPoint ? *stillPoint :
635
636
637 if (kisSquareDistance(effectiveStillPoint.viewPoint(), widgetCenterPoint()) > 2.0) {
638 // when mirroring not against the center, reset the zoom mode
640 }
641
642 const QPointF oldDocumentOffset = m_d->documentOffset;
643
644 bool doXMirroring = m_d->isXAxisMirrored ^ mirrorXAxis;
645 bool doYMirroring = m_d->isYAxisMirrored ^ mirrorYAxis;
646 qreal scaleX = doXMirroring ? -1.0 : 1.0;
647 qreal scaleY = doYMirroring ? -1.0 : 1.0;
648 QTransform mirror = QTransform::fromScale(scaleX, scaleY);
649
650 QTransform rot;
651 rot.rotate(m_d->rotationAngle);
652
653 m_d->flakeToWidget *= QTransform::fromTranslate(-effectiveStillPoint.viewPoint().x(),-effectiveStillPoint.viewPoint().y());
654
655 if (keepOrientation) {
656 m_d->flakeToWidget *= rot.inverted();
657 }
658
660
661 if (keepOrientation) {
662 m_d->flakeToWidget *= rot;
663 }
664
665 m_d->flakeToWidget *= QTransform::fromTranslate(effectiveStillPoint.viewPoint().x(),effectiveStillPoint.viewPoint().y());
666
667
668 if (!keepOrientation && (doXMirroring ^ doYMirroring)) {
670 }
671
672 m_d->isXAxisMirrored = mirrorXAxis;
673 m_d->isYAxisMirrored = mirrorYAxis;
674
676
678 // we were "centered", so let's try to keep the offset as before
679 m_d->documentOffset = oldDocumentOffset;
680 } else {
682 const QPointF newStillPoint = documentToWidget(effectiveStillPoint.docPoint());
683 const QPointF offset = newStillPoint - effectiveStillPoint.viewPoint();
685 }
686
688
689 if (stillPoint) {
691 }
692}
693
698
703
704void KisCoordinatesConverter::resetRotation(const std::optional<KoViewTransformStillPoint> &stillPoint)
705{
706 KoViewTransformStillPoint effectiveStillPoint =
707 stillPoint ? *stillPoint :
709
710 QTransform rot;
711 rot.rotate(-m_d->rotationAngle);
712
713 m_d->flakeToWidget *= rot;
714 m_d->rotationAngle = 0.0;
716
719
720 const QPointF newStillPoint = documentToWidget(effectiveStillPoint.docPoint());
721 const QPointF offset = newStillPoint - effectiveStillPoint.viewPoint();
724
725 if (stillPoint) {
727 }
728}
729
733
737
741
745
749
751 return m_d->widgetToViewport.inverted();
752}
753
757
759 QPointF *brushOrigin,
760 QPolygonF *polygon,
761 const bool scrollCheckers) const
762{
772 QRectF imageRect = imageRectInViewportPixels();
773 imageRect.adjust(0,0,-0.5,-0.5);
774
775 if (scrollCheckers) {
776 *transform = viewportToWidgetTransform();
777 *polygon = imageRect;
778 *brushOrigin = imageToViewport(QPointF(0,0));
779 }
780 else {
781 *transform = QTransform();
782 *polygon = viewportToWidgetTransform().map(imageRect);
783 *brushOrigin = QPoint(0,0);
784 }
785}
786
788 QTransform *textureTransform,
789 QTransform *modelTransform,
790 QRectF *textureRect,
791 QRectF *modelRect,
792 const bool scrollCheckers) const
793{
794 if(scrollCheckers) {
795 *textureTransform = QTransform();
796 *textureRect = QRectF(0, 0, viewportRect.width(),viewportRect.height());
797 }
798 else {
799 *textureTransform = viewportToWidgetTransform();
800 *textureRect = viewportRect;
801 }
802
803 *modelTransform = viewportToWidgetTransform();
804 *modelRect = viewportRect;
805}
806
808{
809 QPolygonF poly = imageToWidget(QPolygon(m_d->imageBounds));
810 return (poly[0] + poly[1] + poly[2] + poly[3]) / 4.0;
811}
812
813
814// these functions return a bounding rect if the canvas is rotated
815
820
825
830
835
837{
838 qreal scaleX, scaleY;
839 imageScale(&scaleX, &scaleY);
840 QSize imageSize = m_d->imageBounds.size();
841
842 return QSizeF(imageSize.width() * scaleX, imageSize.height() * scaleY);
843}
844
846{
847 return widgetToFlake(QRectF(QPoint(0,0), m_d->canvasWidgetSize));
848}
849
851{
852 return widgetToImage(QRectF(QPoint(0,0), m_d->canvasWidgetSize));
853}
854
856{
857 QRectF widgetRect = widgetRectInFlakePixels();
858 return QPointF(widgetRect.left() + widgetRect.width() / 2,
859 widgetRect.top() + widgetRect.height() / 2);
860}
861
863{
864 return QPointF(m_d->canvasWidgetSize.width() / 2.0, m_d->canvasWidgetSize.height() / 2.0);
865}
866
867void KisCoordinatesConverter::imageScale(qreal *scaleX, qreal *scaleY) const
868{
869 // get the x and y zoom level of the canvas
870 qreal zoomX, zoomY;
871 KoZoomHandler::zoom(&zoomX, &zoomY);
872
873 // Get the KisImage resolution
874 qreal resX = m_d->imageXRes;
875 qreal resY = m_d->imageYRes;
876
877 // Compute the scale factors
878 *scaleX = zoomX / resX;
879 *scaleY = zoomY / resY;
880}
881
882void KisCoordinatesConverter::imagePhysicalScale(qreal *scaleX, qreal *scaleY) const
883{
884 imageScale(scaleX, scaleY);
885 *scaleX *= m_d->devicePixelRatio;
886 *scaleY *= m_d->devicePixelRatio;
887}
888
897QPointF KisCoordinatesConverter::snapToDevicePixel(const QPointF &point) const
898{
900 return point;
901 }
902
903 QPoint devicePixel = (point * m_d->devicePixelRatio).toPoint();
904 // These adjusted coords will be in logical pixel but is aligned in device
905 // pixel space for pixel-perfect rendering.
906 return QPointF(devicePixel) / m_d->devicePixelRatio;
907}
908
910{
911 return flakeToWidgetTransform();
912}
913
915{
916 return flakeToWidgetTransform().inverted();
917}
918
920{
921 return m_d->minZoom;
922}
924{
925 return m_d->maxZoom;
926}
928{
929 return std::clamp(zoom, minZoom(), maxZoom());
930}
931
936
941
943{
944 qreal minDimension = 0.0;
945
946 if (m_d->imageBounds.width() < m_d->imageBounds.height()) {
947 minDimension = m_d->imageBounds.width() * resolutionX() / m_d->imageXRes;
948 } else {
949 minDimension = m_d->imageBounds.height() * resolutionY() / m_d->imageYRes;
950 }
951
952 m_d->minZoom = qMin(100.0 / minDimension, 0.1);
953 m_d->maxZoom = 90.0;
954 m_d->standardZoomLevels.clear(); // TODO: reset only on real change!
955}
956
957qreal KisCoordinatesConverter::findNextZoom(qreal currentZoom, const QVector<qreal> &zoomLevels)
958{
959 return KoZoomMode::findNextZoom(currentZoom, zoomLevels);
960}
961
962qreal KisCoordinatesConverter::findPrevZoom(qreal currentZoom, const QVector<qreal> &zoomLevels)
963{
964 return KoZoomMode::findPrevZoom(currentZoom, zoomLevels);
965}
966
968{
969 return {widgetToDocument(viewPoint), viewPoint};
970}
971
973{
974 return {docPoint, documentToWidget(docPoint)};
975}
float value(const T *src, size_t ch)
qreal vastScrolling(bool defaultValue=false) const
QSizeF snapWidgetSizeToDevicePixel(const QSizeF &size) const
QPointF preferredTransformationCenter() const
returns the point in image coordinates which is supposed to be the default still point for the transf...
void imageScale(qreal *scaleX, qreal *scaleY) const
_Private::Traits< T >::Result widgetToDocument(const T &obj) const
void setZoom(qreal zoom) override
void resetRotation(const std::optional< KoViewTransformStillPoint > &stillPoint)
resets canvas rotation
QPointF snapToDevicePixel(const QPointF &point) const
Adjust a given pair of coordinates to the nearest device pixel according to the value of devicePixelR...
static qreal findNextZoom(qreal currentZoom, const QVector< qreal > &zoomLevels)
_Private::Traits< T >::Result documentToWidget(const T &obj) const
void setCanvasWidgetSizeKeepZoom(const QSizeF &size)
_Private::Traits< T >::Result widgetToFlake(const T &obj) const
_Private::Traits< T >::Result imageToWidget(const T &obj) const
void imagePhysicalScale(qreal *scaleX, qreal *scaleY) const
void getOpenGLCheckersInfo(const QRectF &viewportRect, QTransform *textureTransform, QTransform *modelTransform, QRectF *textureRect, QRectF *modelRect, const bool scrollCheckers) const
void getQPainterCheckersInfo(QTransform *transform, QPointF *brushOrigin, QPolygonF *polygon, const bool scrollCheckers) const
void setExtraReferencesBounds(const QRect &imageRect)
void setImageResolution(qreal xRes, qreal yRes)
void setImageBounds(const QRect &rect, const QPointF oldImageStillPoint, const QPointF newImageStillPoint)
void setDocumentOffset(const QPointF &offset)
QVector< qreal > standardZoomLevels() const
_Private::Traits< T >::Result widgetToImage(const T &obj) const
void rotate(const std::optional< KoViewTransformStillPoint > &stillPoint, qreal angle)
rotates the canvas
KoViewTransformStillPoint makeWidgetStillPoint(const QPointF &viewPoint) const override
Creates a still point that links the viewPoint of the widget to the corresponding point of the image.
QTransform viewToWidget() const override
_Private::Traits< T >::Result imageToDocument(const T &obj) const
KoViewTransformStillPoint makeDocStillPoint(const QPointF &docPoint) const override
Creates a still point that links the docPoint of the image (in document pixels!) to the corresponding...
static qreal findPrevZoom(qreal currentZoom, const QVector< qreal > &zoomLevels)
void mirror(const std::optional< KoViewTransformStillPoint > &stillPoint, bool mirrorXAxis, bool mirrorYAxis)
mirrors the canvas
void zoomTo(const QRectF &widgetRect)
_Private::Traits< T >::Result imageToViewport(const T &obj) const
QTransform widgetToView() const override
double xRes() const
double yRes() const
QRect bounds() const override
KoZoomMode::Mode zoomMode() const
int zoomMarginSize() const
void setZoom(qreal zoom) override
void setZoomMode(KoZoomMode::Mode zoomMode)
qreal resolutionX() const
qreal zoom() const
qreal resolutionY() const
void setResolution(qreal resolutionX, qreal resolutionY)
@ ZOOM_CONSTANT
zoom x %
Definition KoZoomMode.h:24
@ ZOOM_PAGE
zoom page
Definition KoZoomMode.h:25
@ ZOOM_WIDTH
zoom pagewidth
Definition KoZoomMode.h:26
@ ZOOM_HEIGHT
zoom pageheight
Definition KoZoomMode.h:27
static qreal findNextZoom(qreal currentZoom, const QVector< qreal > &zoomLevels)
static QVector< qreal > generateStandardZoomLevels(qreal minZoom, qreal maxZoom)
static qreal findPrevZoom(qreal currentZoom, const QVector< qreal > &zoomLevels)
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define ppVar(var)
Definition kis_debug.h:155
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:194
Point clampPoint(Point pt, const Rect &bounds)
KisValueCache< StandardZoomLevelsInitializer > standardZoomLevels