Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_perspectivetransform_worker.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Krita
3 *
4 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
5 * SPDX-FileCopyrightText: 2009 Edward Apap <schumifer@hotmail.com>
6 * SPDX-FileCopyrightText: 2010 Marc Pegon <pe.marc@free.fr>
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11
13
14#include <QMatrix4x4>
15#include <QTransform>
16#include <QVector3D>
17#include <QPolygonF>
18
19#include <KoUpdater.h>
20#include <KoColor.h>
22
23#include "kis_paint_device.h"
27#include "kis_selection.h"
28#include <kis_iterator_ng.h>
29#include "krita_utils.h"
31#include "kis_painter.h"
32#include "kis_image.h"
33#include "kis_algebra_2d.h"
34
35
36KisPerspectiveTransformWorker::KisPerspectiveTransformWorker(KisPaintDeviceSP dev, QPointF center, double aX, double aY, double distance, bool cropDst, KoUpdaterPtr progress)
37 : m_dev(dev), m_progressUpdater(progress), m_cropDst(cropDst)
38
39{
40 QMatrix4x4 m;
41 m.rotate(180. * aX / M_PI, QVector3D(1, 0, 0));
42 m.rotate(180. * aY / M_PI, QVector3D(0, 1, 0));
43
44 QTransform project = m.toTransform(distance);
45 QTransform t = QTransform::fromTranslate(center.x(), center.y());
46
47 QTransform forwardTransform = t.inverted() * project * t;
48
50}
51
53 : m_dev(dev), m_progressUpdater(progress), m_cropDst(cropDst)
54{
55 init(transform);
56}
57
59 const QRect &dstBaseClipRect,
60 KisRegion *dstRegion,
61 QPolygonF *dstClipPolygon)
62{
63 QPolygonF bounds = srcRect;
64 QPolygonF newBounds = m_forwardTransform.map(bounds);
65
66 QRectF clipRect = dstBaseClipRect;
67
68 if (!m_cropDst) {
69 clipRect |= srcRect;
70 clipRect = KisAlgebra2D::blowRect(clipRect, 3.0);
71 }
72
73 newBounds = newBounds.intersected(clipRect);
74 QPainterPath path;
75 path.addPolygon(newBounds);
76 *dstRegion = KritaUtils::splitPath(path);
77 *dstClipPolygon = newBounds;
78}
79
80void KisPerspectiveTransformWorker::init(const QTransform &transform)
81{
82 m_isIdentity = transform.isIdentity();
83 m_isTranslating = transform.type() == QTransform::TxTranslate;
84
85 m_forwardTransform = transform;
86 m_backwardTransform = transform.inverted();
87
88 if (m_dev) {
90
91 QPolygonF dstClipPolygonUnused;
92
96 &dstClipPolygonUnused);
97 }
98}
99
103
105{
106 init(transform);
107}
108
109
111{
113
115 : m_accessor(device->createRandomSubAccessor())
116 {
117 }
118
119 void samplePixel(const QPointF &pt, quint8 *dst) {
120 m_accessor->moveTo(pt.x(), pt.y());
122 }
123
125};
126
128{
130
132 : m_accessor(device->createRandomConstAccessorNG()),
133 m_pixelSize(device->pixelSize())
134 {
135 }
136
137 void samplePixel(const QPointF &pt, quint8 *dst) {
138 m_accessor->moveTo(qRound(pt.x()), qRound(pt.y()));
139 memcpy(dst, m_accessor->oldRawData(), m_pixelSize);
140 }
141
144};
145
146template <class SrcAccessorWrapper>
148{
150
151 if (m_isIdentity) return;
152
153 // TODO: check if this optimization is possible. The only blocking issue might be if
154 // some other thread also accesses this device (which should not be the case,
155 // theoretically
156 //
157 // if (m_isTranslating) {
158 // m_dev->moveTo(m_dev->offset() + QPoint(qRound(m_forwardTransform.dx()), qRound(m_forwardTransform.dy())));
159 // return;
160 // }
161
162 KisPaintDeviceSP cloneDevice = new KisPaintDevice(*m_dev.data());
163
164 // Clear the destination device, since all the tiles are already
165 // shared with cloneDevice
166 m_dev->clear();
167
169
171
172 SrcAccessorWrapper srcAcc(cloneDevice);
174
175 Q_FOREACH (const QRect &rect, m_dstRegion.rects()) {
176 for (int y = rect.y(); y < rect.y() + rect.height(); ++y) {
177 for (int x = rect.x(); x < rect.x() + rect.width(); ++x) {
178
179 QPointF dstPoint(x, y);
180 QPointF srcPoint = m_backwardTransform.map(dstPoint);
181
182 if (m_srcRect.contains(srcPoint)) {
183 accessor->moveTo(dstPoint.x(), dstPoint.y());
184 srcAcc.samplePixel(srcPoint, accessor->rawData());
185 }
186 }
187 }
188 progressHelper.step();
189 }
190}
191
193{
194 if (sampleType == Bilinear) {
195 runImpl<BilinearWrapper>();
196 } else {
197 runImpl<NearestNeighbourWrapper>();
198 }
199}
200
202 KisPaintDeviceSP dstDev,
203 const QRect &dstRect)
204{
205 KIS_SAFE_ASSERT_RECOVER_RETURN(srcDev->pixelSize() == dstDev->pixelSize());
206 KIS_SAFE_ASSERT_RECOVER_NOOP(*srcDev->colorSpace() == *dstDev->colorSpace());
207
208 QRectF srcClipRect = kisGrowRect(srcDev->exactBounds(), 1) | srcDev->defaultBounds()->imageBorderRect();
209 if (srcClipRect.isEmpty()) return;
210
212 KisPainter gc(dstDev);
214 gc.bitBlt(dstRect.topLeft(), srcDev, m_backwardTransform.mapRect(dstRect));
215 } else {
216 KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height());
217
219 KisRandomAccessorSP accessor = dstDev->createRandomAccessorNG();
220
221 for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); ++y) {
222 for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); ++x) {
223
224 QPointF dstPoint(x, y);
225 QPointF srcPoint = m_backwardTransform.map(dstPoint);
226
227 if (srcClipRect.contains(srcPoint) || srcDev->defaultBounds()->wrapAroundMode()) {
228 accessor->moveTo(dstPoint.x(), dstPoint.y());
229 srcAcc->moveTo(srcPoint.x(), srcPoint.y());
230 srcAcc->sampledOldRawData(accessor->rawData());
231 }
232 }
233 progressHelper.step();
234 }
235 }
236}
237
242
247
252
float value(const T *src, size_t ch)
QPointF dstPoint
const QString COMPOSITE_COPY
qreal distance(const QPointF &p1, const QPointF &p2)
virtual quint8 * rawData()=0
virtual const quint8 * oldRawData() const =0
virtual bool wrapAroundMode() const =0
virtual QRect imageBorderRect() const
virtual QRect bounds() const =0
quint32 pixelSize() const
virtual void clear()
QRect exactBounds() const
const KoColorSpace * colorSpace() const
KisDefaultBoundsBaseSP defaultBounds() const
KisRandomSubAccessorSP createRandomSubAccessor() const
KisRandomAccessorSP createRandomAccessorNG()
void bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setCompositeOpId(const KoCompositeOp *op)
KisPerspectiveTransformWorker(KisPaintDeviceSP dev, QPointF center, double aX, double aY, double distance, bool cropDst, KoUpdaterPtr progress)
void run(SampleType sampleType=Bilinear)
void fillParams(const QRectF &srcRect, const QRect &dstBaseClipRect, KisRegion *dstRegion, QPolygonF *dstClipPolygon)
void runPartialDst(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev, const QRect &dstRect)
void init(const QTransform &transform)
void setForwardTransform(const QTransform &transform)
virtual void moveTo(qint32 x, qint32 y)=0
void sampledOldRawData(quint8 *dst)
void moveTo(qreal x, qreal y)
int rectCount() const
QVector< QRect > rects() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define bounds(x, a, b)
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
#define M_PI
Definition kis_global.h:111
KisSharedPtr< KisRandomSubAccessor > KisRandomSubAccessorSP
Definition kis_types.h:228
KisSharedPtr< KisRandomAccessorNG > KisRandomAccessorSP
Definition kis_types.h:225
Rect blowRect(const Rect &rect, qreal coeff)
KisRegion splitPath(const QPainterPath &path)
KisRandomSubAccessorSP m_accessor
BilinearWrapper(KisPaintDeviceSP device)
void samplePixel(const QPointF &pt, quint8 *dst)
void samplePixel(const QPointF &pt, quint8 *dst)
NearestNeighbourWrapper(KisPaintDeviceSP device)