Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_warptransform_worker.cc
Go to the documentation of this file.
1/*
2 * kis_warptransform_worker.cc -- part of Krita
3 *
4 * SPDX-FileCopyrightText: 2010 Marc Pegon <pe.marc@free.fr>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
11#include "kis_iterator_ng.h"
12#include "kis_datamanager.h"
13
14#include <QVector2D>
15#include <QPainter>
16#include <QVarLengthArray>
17
18#include <KoColorSpace.h>
19#include <KoColor.h>
20
21#include <math.h>
22
24
26{
27 int nbPoints = p.size();
28 QVarLengthArray<qreal> w(nbPoints);
29 qreal sumWi = 0;
30 QPointF pStar(0, 0), qStar(0, 0);
31 QVarLengthArray<QPointF> pHat(nbPoints), qHat(nbPoints);
32
33 for (int i = 0; i < nbPoints; ++i) {
34 if (v == p[i])
35 return q[i];
36
37 QVector2D tmp(p[i] - v);
38 w[i] = 1. / pow(tmp.lengthSquared(), alpha);
39 pStar += w[i] * p[i];
40 qStar += w[i] * q[i];
41 sumWi += w[i];
42 }
43 pStar /= sumWi;
44 qStar /= sumWi;
45
46 qreal A_tmp[4] = {0, 0, 0, 0};
47 for (int i = 0; i < nbPoints; ++i) {
48 pHat[i] = p[i] - pStar;
49 qHat[i] = q[i] - qStar;
50
51 A_tmp[0] += w[i] * pow(pHat[i].x(), 2);
52 A_tmp[3] += w[i] * pow(pHat[i].y(), 2);
53 A_tmp[1] += w[i] * pHat[i].x() * pHat[i].y();
54 }
55 A_tmp[2] = A_tmp[1];
56 qreal det_A_tmp = A_tmp[0] * A_tmp[3] - A_tmp[1] * A_tmp[2];
57
58 qreal A_tmp_inv[4];
59
60 if (det_A_tmp == 0)
61 return v;
62
63 A_tmp_inv[0] = A_tmp[3] / det_A_tmp;
64 A_tmp_inv[1] = - A_tmp[1] / det_A_tmp;
65 A_tmp_inv[2] = A_tmp_inv[1];
66 A_tmp_inv[3] = A_tmp[0] / det_A_tmp;
67
68 QPointF t = v - pStar;
69 QPointF A_precalc(t.x() * A_tmp_inv[0] + t.y() * A_tmp_inv[1], t.x() * A_tmp_inv[2] + t.y() * A_tmp_inv[3]);
70 qreal A_j;
71
72 QPointF res = qStar;
73 for (int j = 0; j < nbPoints; ++j) {
74 A_j = A_precalc.x() * pHat[j].x() + A_precalc.y() * pHat[j].y();
75
76 res += w[j] * A_j * qHat[j];
77 }
78
79 return res;
80}
81
83{
84 int nbPoints = p.size();
85 QVarLengthArray<qreal> w(nbPoints);
86 qreal sumWi = 0;
87 QPointF pStar(0, 0), qStar(0, 0);
88 QVarLengthArray<QPointF> pHat(nbPoints), qHat(nbPoints);
89
90 for (int i = 0; i < nbPoints; ++i) {
91 if (v == p[i])
92 return q[i];
93
94 QVector2D tmp(p[i] - v);
95 w[i] = 1. / pow(tmp.lengthSquared(), alpha);
96 pStar += w[i] * p[i];
97 qStar += w[i] * q[i];
98 sumWi += w[i];
99 }
100 pStar /= sumWi;
101 qStar /= sumWi;
102
103 qreal mu_s = 0;
104 QPointF res_tmp(0, 0);
105 qreal qx, qy, px, py;
106 for (int i = 0; i < nbPoints; ++i) {
107 pHat[i] = p[i] - pStar;
108 qHat[i] = q[i] - qStar;
109
110 QVector2D tmp(pHat[i]);
111 mu_s += w[i] * tmp.lengthSquared();
112
113 qx = w[i] * qHat[i].x();
114 qy = w[i] * qHat[i].y();
115 px = pHat[i].x();
116 py = pHat[i].y();
117
118 res_tmp += QPointF(qx * px + qy * py, qx * py - qy * px);
119 }
120
121 res_tmp /= mu_s;
122 QPointF v_m_pStar(v - pStar);
123 QPointF res(res_tmp.x() * v_m_pStar.x() + res_tmp.y() * v_m_pStar.y(), res_tmp.x() * v_m_pStar.y() - res_tmp.y() * v_m_pStar.x());
124 res += qStar;
125
126 return res;
127}
128
130{
131 int nbPoints = p.size();
132 QVarLengthArray<qreal> w(nbPoints);
133 qreal sumWi = 0;
134 QPointF pStar(0, 0), qStar(0, 0);
135 QVarLengthArray<QPointF> pHat(nbPoints), qHat(nbPoints);
136
137 for (int i = 0; i < nbPoints; ++i) {
138 if (v == p[i])
139 return q[i];
140
141 QVector2D tmp(p[i] - v);
142 w[i] = 1. / pow(tmp.lengthSquared(), alpha);
143 pStar += w[i] * p[i];
144 qStar += w[i] * q[i];
145 sumWi += w[i];
146 }
147 pStar /= sumWi;
148 qStar /= sumWi;
149
150 QVector2D res_tmp(0, 0);
151 qreal qx, qy, px, py;
152 for (int i = 0; i < nbPoints; ++i) {
153 pHat[i] = p[i] - pStar;
154 qHat[i] = q[i] - qStar;
155
156 qx = w[i] * qHat[i].x();
157 qy = w[i] * qHat[i].y();
158 px = pHat[i].x();
159 py = pHat[i].y();
160
161 res_tmp += QVector2D(qx * px + qy * py, qx * py - qy * px);
162 }
163
164 QPointF f_arrow(res_tmp.normalized().toPointF());
165 QVector2D v_m_pStar(v - pStar);
166 QPointF res(f_arrow.x() * v_m_pStar.x() + f_arrow.y() * v_m_pStar.y(), f_arrow.x() * v_m_pStar.y() - f_arrow.y() * v_m_pStar.x());
167 res += qStar;
168
169 return res;
170}
171
173 : m_progress(progress)
174{
175 m_origPoint = origPoint;
176 m_transfPoint = transfPoint;
177 m_alpha = alpha;
178
179 switch(warpType) {
180 case AFFINE_TRANSFORM:
182 break;
185 break;
186 case RIGID_TRANSFORM:
188 break;
189 default:
191 break;
192 }
193}
194
198
200{
202 const QVector<QPointF> &p,
203 const QVector<QPointF> &q,
204 qreal alpha)
205 : m_function(function),
206 m_p(p),
207 m_q(q),
208 m_alpha(alpha)
209 {
210 }
211
212 QPointF operator() (const QPointF &pt) const {
213 return m_function(pt, m_p, m_q, m_alpha);
214 }
215
219 qreal m_alpha;
220};
221
223{
224 KIS_SAFE_ASSERT_RECOVER_RETURN(*srcDev->colorSpace() == *dstDev->colorSpace());
225
226 if (!m_warpMathFunction ||
227 m_origPoint.isEmpty() ||
228 m_origPoint.size() != m_transfPoint.size()) {
229
230 return;
231 }
232
233 if (m_origPoint.size() == 1) {
234 dstDev->makeCloneFromRough(srcDev, srcDev->extent());
235 QPointF translate(QPointF(srcDev->x(), srcDev->y()) + m_transfPoint[0] - m_origPoint[0]);
236 dstDev->moveTo(translate.toPoint());
237 return;
238 }
239
240 const QRect srcBounds = srcDev->region().boundingRect();
241
242 dstDev->clear();
243
244 const int pixelPrecision = 8;
245
247 GridIterationTools::PaintDevicePolygonOp polygonOp(srcDev, dstDev);
248 GridIterationTools::processGrid(polygonOp, functionOp,
249 srcBounds, pixelPrecision);
250}
251
252#include "krita_utils.h"
253
255{
256 const qreal margin = 0.05;
257
259 QRect resultRect = KisAlgebra2D::approximateRectWithPointTransform(rc, functionOp);
260
261 return KisAlgebra2D::blowRect(resultRect, margin);
262}
263
264QRect KisWarpTransformWorker::approxNeedRect(const QRect &rc, const QRect &fullBounds)
265{
266 Q_UNUSED(rc);
267 return fullBounds;
268}
269
271 const QVector<QPointF> &origPoint,
272 const QVector<QPointF> &transfPoint,
273 qreal alpha,
274 const QImage& srcImage,
275 const QPointF &srcQImageOffset,
276 QPointF *newOffset)
277{
278 KIS_ASSERT_RECOVER(srcImage.format() == QImage::Format_ARGB32) {
279 return QImage();
280 }
281
282 WarpMathFunction warpMathFunction = &rigidTransformMath;
283
284 switch (warpType) {
285 case AFFINE_TRANSFORM:
286 warpMathFunction = &affineTransformMath;
287 break;
289 warpMathFunction = &similitudeTransformMath;
290 break;
291 case RIGID_TRANSFORM:
292 warpMathFunction = &rigidTransformMath;
293 break;
294 default:
295 KIS_ASSERT_RECOVER(0 && "Unknown warp mode") { return QImage(); }
296 }
297
298 if (!warpMathFunction ||
299 origPoint.isEmpty() ||
300 origPoint.size() != transfPoint.size()) {
301
302 return srcImage;
303 }
304
305 if (origPoint.size() == 1) {
306 *newOffset = srcQImageOffset + (transfPoint[0] - origPoint[0]).toPoint();
307 return srcImage;
308 }
309
310 FunctionTransformOp functionOp(warpMathFunction, origPoint, transfPoint, alpha);
311
312 const QRectF srcBounds = QRectF(srcQImageOffset, srcImage.size());
313 QRectF dstBounds;
314
315 {
316 QPolygonF testPoints;
317 testPoints << srcBounds.topLeft();
318 testPoints << srcBounds.topRight();
319 testPoints << srcBounds.bottomRight();
320 testPoints << srcBounds.bottomLeft();
321 testPoints << srcBounds.topLeft();
322
323 QPolygonF::iterator it = testPoints.begin() + 1;
324
325 while (it != testPoints.end()) {
326 it = testPoints.insert(it, 0.5 * (*it + *(it - 1)));
327 it += 2;
328 }
329
330 it = testPoints.begin();
331
332 while (it != testPoints.end()) {
333 *it = functionOp(*it);
334 ++it;
335 }
336
337 dstBounds = testPoints.boundingRect();
338 }
339
340 QPointF dstQImageOffset = dstBounds.topLeft();
341 *newOffset = dstQImageOffset;
342
343 QRect dstBoundsI = dstBounds.toAlignedRect();
344 QImage dstImage(dstBoundsI.size(), srcImage.format());
345 dstImage.fill(0);
346
347 const int pixelPrecision = 32;
348 GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcQImageOffset, dstQImageOffset);
349 GridIterationTools::processGrid(polygonOp, functionOp, srcBounds.toAlignedRect(), pixelPrecision);
350
351 return dstImage;
352}
const Params2D p
qreal v
virtual void clear()
KisRegion region() const
void makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
QRect extent() const
const KoColorSpace * colorSpace() const
void moveTo(qint32 x, qint32 y)
QRect boundingRect() const
void run(KisPaintDeviceSP srcDev, KisPaintDeviceSP dstDev)
enum KisWarpTransformWorker::WarpType_ WarpType
QRect approxChangeRect(const QRect &rc)
QRect approxNeedRect(const QRect &rc, const QRect &fullBounds)
static QPointF affineTransformMath(QPointF v, QVector< QPointF > p, QVector< QPointF > q, qreal alpha)
QPointF(* WarpMathFunction)(QPointF, QVector< QPointF >, QVector< QPointF >, qreal)
static QPointF rigidTransformMath(QPointF v, QVector< QPointF > p, QVector< QPointF > q, qreal alpha)
KisWarpTransformWorker(WarpType warpType, QVector< QPointF > origPoint, QVector< QPointF > transfPoint, qreal alpha, KoUpdater *progress)
static QImage transformQImage(WarpType warpType, const QVector< QPointF > &origPoint, const QVector< QPointF > &transfPoint, qreal alpha, const QImage &srcImage, const QPointF &srcQImageOffset, QPointF *newOffset)
static QPointF similitudeTransformMath(QPointF v, QVector< QPointF > p, QVector< QPointF > q, qreal alpha)
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
void processGrid(ProcessCell &cellOp, const QRect &srcBounds, const int pixelPrecision)
Rect blowRect(const Rect &rect, qreal coeff)
QRect approximateRectWithPointTransform(const QRect &rect, std::function< QPointF(QPointF)> func)
KisWarpTransformWorker::WarpMathFunction m_function
FunctionTransformOp(KisWarpTransformWorker::WarpMathFunction function, const QVector< QPointF > &p, const QVector< QPointF > &q, qreal alpha)