2
* Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
3
* Copyright (c) 2011 Silvio Heinrich <plassy@web.de>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#include "kis_coordinates_converter.h"
25
#include <KoViewConverter.h>
27
#include <kis_config.h>
28
#include <kis_image.h>
31
struct KisCoordinatesConverter::Private {
33
isXAxisMirrored(false), isYAxisMirrored(false), rotationAngle(0.0) { }
40
QSizeF canvasWidgetSize;
41
QPointF documentOffset;
43
QTransform flakeToWidget;
44
QTransform imageToDocument;
45
QTransform documentToFlake;
46
QTransform widgetToViewport;
50
* When vastScrolling value is less than 0.5 it is possible
51
* that the whole scrolling area (viewport) will be smaller than
52
* the size of the widget. In such cases the image should be
53
* centered in the widget. Previously we used a special parameter
54
* documentOrigin for this purpose, now the value for this
55
* centering is calculated dynamically, helping the offset to
56
* center the image inside the widget
58
* Note that the correction is null when the size of the document
59
* plus vast scrolling reserve is larger than the widget. This
60
* is always true for vastScrolling parameter > 0.5.
63
QPointF KisCoordinatesConverter::centeringCorrection() const
67
QSize documentSize = imageRectInWidgetPixels().toAlignedRect().size();
68
QPointF dPoint(documentSize.width(), documentSize.height());
69
QPointF wPoint(m_d->canvasWidgetSize.width(), m_d->canvasWidgetSize.height());
71
QPointF minOffset = -cfg.vastScrolling() * wPoint;
72
QPointF maxOffset = dPoint - wPoint + cfg.vastScrolling() * wPoint;
74
QPointF range = maxOffset - minOffset;
76
range.rx() = qMin(range.x(), 0.0);
77
range.ry() = qMin(range.y(), 0.0);
85
* The document offset and the position of the top left corner of the
86
* image must always coincide, that is why we need to correct them to
89
* When we change zoom level, the calculation of the new offset is
90
* done by KoCanvasControllerWidget, that is why we just passively fix
91
* the flakeToWidget transform to conform the offset and wait until
92
* the canvas controller will recenter us.
94
* But when we do our own transformations of the canvas, like rotation
95
* and mirroring, we cannot rely on the centering of the canvas
96
* controller and we do it ourselves. Then we just set new offset and
97
* return its value to be set in the canvas controller explicitly.
100
void KisCoordinatesConverter::correctOffsetToTransformation()
102
m_d->documentOffset = -(imageRectInWidgetPixels().topLeft() -
103
centeringCorrection()).toPoint();
106
void KisCoordinatesConverter::correctTransformationToOffset()
108
QPointF topLeft = imageRectInWidgetPixels().topLeft();
109
QPointF diff = (-topLeft) - m_d->documentOffset;
110
diff += centeringCorrection();
111
m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y());
114
void KisCoordinatesConverter::recalculateTransformations()
116
if(!m_d->image) return;
118
m_d->imageToDocument = QTransform::fromScale(1 / m_d->image->xRes(),
119
1 / m_d->image->yRes());
122
KoZoomHandler::zoom(&zoomX, &zoomY);
123
m_d->documentToFlake = QTransform::fromScale(zoomX, zoomY);
125
correctTransformationToOffset();
127
QRectF irect = imageRectInWidgetPixels();
128
QRectF wrect = QRectF(QPoint(0,0), m_d->canvasWidgetSize);
129
QRectF rrect = irect & wrect;
131
QTransform reversedTransform = flakeToWidgetTransform().inverted();
132
QRectF canvasBounds = reversedTransform.mapRect(rrect);
133
QPointF offset = canvasBounds.topLeft();
135
m_d->widgetToViewport = reversedTransform * QTransform::fromTranslate(-offset.x(), -offset.y());
139
KisCoordinatesConverter::KisCoordinatesConverter()
140
: m_d(new Private) { }
142
KisCoordinatesConverter::~KisCoordinatesConverter()
147
void KisCoordinatesConverter::setCanvasWidgetSize(QSize size)
149
m_d->canvasWidgetSize = size;
150
recalculateTransformations();
153
void KisCoordinatesConverter::setImage(KisImageWSP image)
156
recalculateTransformations();
159
void KisCoordinatesConverter::setDocumentOffset(const QPoint& offset)
161
QPointF diff = m_d->documentOffset - offset;
163
m_d->documentOffset = offset;
164
m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y());
165
recalculateTransformations();
168
QPoint KisCoordinatesConverter::documentOffset() const
170
return QPoint(int(m_d->documentOffset.x()), int(m_d->documentOffset.y()));
173
qreal KisCoordinatesConverter::rotationAngle() const
175
return m_d->rotationAngle;
178
void KisCoordinatesConverter::setZoom(qreal zoom)
180
KoZoomHandler::setZoom(zoom);
181
recalculateTransformations();
184
QPoint KisCoordinatesConverter::rotate(QPointF center, qreal angle)
189
m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(),-center.y());
190
m_d->flakeToWidget *= rot;
191
m_d->flakeToWidget *= QTransform::fromTranslate(center.x(), center.y());
192
m_d->rotationAngle = std::fmod(m_d->rotationAngle + angle, 360.0);
194
correctOffsetToTransformation();
195
recalculateTransformations();
197
return m_d->documentOffset.toPoint();
200
QPoint KisCoordinatesConverter::mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis, bool keepOrientation)
202
bool doXMirroring = m_d->isXAxisMirrored ^ mirrorXAxis;
203
bool doYMirroring = m_d->isYAxisMirrored ^ mirrorYAxis;
204
qreal scaleX = doYMirroring ? -1.0 : 1.0;
205
qreal scaleY = doXMirroring ? -1.0 : 1.0;
206
QTransform mirror = QTransform::fromScale(scaleX, scaleY);
209
rot.rotate(m_d->rotationAngle);
211
m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(),-center.y());
214
m_d->flakeToWidget *= rot.inverted();
216
m_d->flakeToWidget *= mirror;
219
m_d->flakeToWidget *= rot;
221
m_d->flakeToWidget *= QTransform::fromTranslate(center.x(),center.y());
223
if(!keepOrientation && (doXMirroring ^ doYMirroring))
224
m_d->rotationAngle = -m_d->rotationAngle;
226
m_d->isXAxisMirrored = mirrorXAxis;
227
m_d->isYAxisMirrored = mirrorYAxis;
229
correctOffsetToTransformation();
230
recalculateTransformations();
232
return m_d->documentOffset.toPoint();
235
QPoint KisCoordinatesConverter::resetRotation(QPointF center)
238
rot.rotate(-m_d->rotationAngle);
240
m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(), -center.y());
241
m_d->flakeToWidget *= rot;
242
m_d->flakeToWidget *= QTransform::fromTranslate(center.x(), center.y());
243
m_d->rotationAngle = 0.0;
245
correctOffsetToTransformation();
246
recalculateTransformations();
248
return m_d->documentOffset.toPoint();
251
QTransform KisCoordinatesConverter::imageToWidgetTransform() const{
252
return m_d->imageToDocument * m_d->documentToFlake * m_d->flakeToWidget;
255
QTransform KisCoordinatesConverter::imageToDocumentTransform() const {
256
return m_d->imageToDocument;
259
QTransform KisCoordinatesConverter::flakeToWidgetTransform() const {
260
return m_d->flakeToWidget;
263
QTransform KisCoordinatesConverter::documentToWidgetTransform() const
265
return m_d->documentToFlake * m_d->flakeToWidget;
268
QTransform KisCoordinatesConverter::viewportToWidgetTransform() const {
269
return m_d->widgetToViewport.inverted();
272
QTransform KisCoordinatesConverter::imageToViewportTransform() const {
273
return m_d->imageToDocument * m_d->documentToFlake * m_d->flakeToWidget * m_d->widgetToViewport;
276
void KisCoordinatesConverter::getQPainterCheckersInfo(QTransform *transform,
277
QPointF *brushOrigin,
278
QPolygonF *polygon) const
281
* Qt has different rounding for QPainter::drawRect/drawImage.
282
* The image is rounded mathematically, while rect in aligned
283
* to the next integer. That causes transparent line appear on
286
* See: https://bugreports.qt.nokia.com/browse/QTBUG-22827
289
QRectF imageRect = imageRectInViewportPixels();
290
imageRect.adjust(0,0,-0.5,-0.5);
293
if (cfg.scrollCheckers()) {
294
*transform = viewportToWidgetTransform();
295
*polygon = imageRect;
296
*brushOrigin = imageToViewport(QPointF(0,0));
299
*transform = QTransform();
300
*polygon = viewportToWidgetTransform().map(imageRect);
301
*brushOrigin = QPoint(0,0);
305
void KisCoordinatesConverter::getOpenGLCheckersInfo(QTransform *textureTransform,
306
QTransform *modelTransform,
308
QRectF *modelRect) const
311
QRectF viewportRect = imageRectInViewportPixels();
313
if(cfg.scrollCheckers()) {
314
*textureTransform = QTransform();
315
*textureRect = QRectF(0, 0, viewportRect.width(),viewportRect.height());
318
*textureTransform = viewportToWidgetTransform();
319
*textureRect = viewportRect;
322
*modelTransform = viewportToWidgetTransform();
323
*modelRect = viewportRect;
326
QPointF KisCoordinatesConverter::imageCenterInWidgetPixel() const
331
QPolygonF poly = imageToWidget(QPolygon(m_d->image->bounds()));
332
return (poly[0] + poly[1] + poly[2] + poly[3]) / 4.0;
336
// these functions return a bounding rect if the canvas is rotated
338
QRectF KisCoordinatesConverter::imageRectInWidgetPixels() const
340
if(!m_d->image) return QRectF();
341
return imageToWidget(m_d->image->bounds());
344
QRectF KisCoordinatesConverter::imageRectInViewportPixels() const
346
if(!m_d->image) return QRectF();
347
return imageToViewport(m_d->image->bounds());
350
QSizeF KisCoordinatesConverter::imageSizeInFlakePixels() const
352
if(!m_d->image) return QSizeF();
354
qreal scaleX, scaleY;
355
imageScale(&scaleX, &scaleY);
356
QSize imageSize = m_d->image->size();
358
return QSizeF(imageSize.width() * scaleX, imageSize.height() * scaleY);
361
QRectF KisCoordinatesConverter::widgetRectInFlakePixels() const
363
return widgetToFlake(QRectF(QPoint(0,0), m_d->canvasWidgetSize));
366
QPointF KisCoordinatesConverter::flakeCenterPoint() const
368
QRectF widgetRect = widgetRectInFlakePixels();
369
return QPointF(widgetRect.left() + widgetRect.width() / 2,
370
widgetRect.top() + widgetRect.height() / 2);
373
QPointF KisCoordinatesConverter::widgetCenterPoint() const
375
return QPointF(m_d->canvasWidgetSize.width() / 2.0, m_d->canvasWidgetSize.height() / 2.0);
378
void KisCoordinatesConverter::imageScale(qreal *scaleX, qreal *scaleY) const
380
// get the x and y zoom level of the canvas
382
KoZoomHandler::zoom(&zoomX, &zoomY);
384
// Get the KisImage resolution
385
qreal resX = m_d->image->xRes();
386
qreal resY = m_d->image->yRes();
388
// Compute the scale factors
389
*scaleX = zoomX / resX;
390
*scaleY = zoomY / resY;