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);
255 polygonOp.setCanMergeRects(false);
256 GridIterationTools::processGrid(polygonOp, functionOp,
257 srcBounds, pixelPrecision);
258 polygonOp.finalize();
259}
260
261#include "krita_utils.h"
262
264{
265 const qreal margin = 0.05;
266
268 QRect resultRect = KisAlgebra2D::approximateRectWithPointTransform(rc, functionOp);
269
270 return KisAlgebra2D::blowRect(resultRect, margin);
271}
272
273QRect KisWarpTransformWorker::approxNeedRect(const QRect &rc, const QRect &fullBounds)
274{
275 Q_UNUSED(rc);
276 return fullBounds;
277}
278
280 const QVector<QPointF> &origPoint,
281 const QVector<QPointF> &transfPoint,
282 qreal alpha,
283 const QImage& srcImage,
284 const QPointF &srcQImageOffset,
285 QPointF *newOffset)
286{
287 KIS_ASSERT_RECOVER(srcImage.format() == QImage::Format_ARGB32) {
288 return QImage();
289 }
290
291 WarpMathFunction warpMathFunction = &rigidTransformMath;
292
293 switch (warpType) {
294 case AFFINE_TRANSFORM:
295 warpMathFunction = &affineTransformMath;
296 break;
298 warpMathFunction = &similitudeTransformMath;
299 break;
300 case RIGID_TRANSFORM:
301 warpMathFunction = &rigidTransformMath;
302 break;
303 default:
304 KIS_ASSERT_RECOVER(0 && "Unknown warp mode") { return QImage(); }
305 }
306
307 if (!warpMathFunction ||
308 origPoint.isEmpty() ||
309 origPoint.size() != transfPoint.size()) {
310
311 return srcImage;
312 }
313
314 if (origPoint.size() == 1) {
315 *newOffset = srcQImageOffset + (transfPoint[0] - origPoint[0]).toPoint();
316 return srcImage;
317 }
318
319 FunctionTransformOp functionOp(warpMathFunction, origPoint, transfPoint, alpha);
320
321 const QRectF srcBounds = QRectF(srcQImageOffset, srcImage.size());
322 QRectF dstBounds;
323
324 {
325 QPolygonF testPoints;
326 testPoints << srcBounds.topLeft();
327 testPoints << srcBounds.topRight();
328 testPoints << srcBounds.bottomRight();
329 testPoints << srcBounds.bottomLeft();
330 testPoints << srcBounds.topLeft();
331
332 QPolygonF::iterator it = testPoints.begin() + 1;
333
334 while (it != testPoints.end()) {
335 it = testPoints.insert(it, 0.5 * (*it + *(it - 1)));
336 it += 2;
337 }
338
339 it = testPoints.begin();
340
341 while (it != testPoints.end()) {
342 *it = functionOp(*it);
343 ++it;
344 }
345
346 dstBounds = testPoints.boundingRect();
347 }
348
349 QPointF dstQImageOffset = dstBounds.topLeft();
350 *newOffset = dstQImageOffset;
351
352 QRect dstBoundsI = dstBounds.toAlignedRect();
353 QImage dstImage(dstBoundsI.size(), srcImage.format());
354 dstImage.fill(0);
355
356 const int pixelPrecision = 32;
357 GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcQImageOffset, dstQImageOffset);
365 polygonOp.setCanMergeRects(false);
366 GridIterationTools::processGrid(polygonOp, functionOp, srcBounds.toAlignedRect(), pixelPrecision);
367 polygonOp.finalize();
368
369 return dstImage;
370}
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)