21 : srcBounds(_srcBounds),
23 pixelPrecision(_pixelPrecision)
24 , originalPointsContainer(_srcBounds)
25 , transformedPointsContainer(_srcBounds)
47 template <
class ProcessOp>
52 template <
class ProcessOp>
58 template <
class ProcessOp>
69 : m_d(new
Private(srcBounds, progress, pixelPrecision))
78 : m_d(new
Private(*rhs.m_d.data()))
89 m_d->srcBounds == other.
m_d->srcBounds &&
90 m_d->pixelPrecision == other.
m_d->pixelPrecision &&
91 m_d->gridSize == other.
m_d->gridSize &&
92 m_d->originalPoints.size() == other.
m_d->originalPoints.size() &&
93 m_d->transformedPoints.size() == other.
m_d->transformedPoints.size();
95 if (!result)
return false;
97 const qreal
eps = 1e-6;
108 const qreal
eps = 1e-6;
119 return m_d->gridSize;
124 return m_d->originalPoints;
129 return m_d->transformedPoints;
137 int prevCol,
int prevRow,
138 int colIndex,
int rowIndex) {
145 QPointF pt(col, row);
156void KisLiquifyTransformWorker::Private::preparePoints()
164 const int numPoints = pointsOp.
m_points.size();
169 transformedPoints = pointsOp.
m_points;
171 originalPointsContainer.initializeWithGridPoints(srcBounds, pixelPrecision);
172 transformedPointsContainer.initializeWithGridPoints(srcBounds, pixelPrecision);
179 m_d->transformedPoints.size());
183 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
184 m_d->originalPointsContainer.movePoint(i,
m_d->originalPoints[i],
m_d->originalPoints[i] + offset);
185 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i],
m_d->transformedPoints[i] + offset);
187 m_d->originalPoints[i] += offset;
188 m_d->transformedPoints[i] += offset;
196 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
197 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i],
m_d->transformedPoints[i] + offset);
198 m_d->transformedPoints[i] += offset;
206 const qreal maxDistCoeff = 3.0;
207 const qreal maxDist = maxDistCoeff * sigma;
210 m_d->transformedPoints.size());
213 m_d->transformedPointsContainer.findAllInRange(indexes, base, maxDist);
214 for (
int i = 0; i < indexes.count(); i++) {
216 QPointF diff =
m_d->transformedPoints[indexes[i]] - base;
218 qreal lambda = exp(-0.5 *
pow2(dist / sigma));
221 QPointF oldPosition =
m_d->transformedPoints[indexes[i]];
222 m_d->transformedPoints[indexes[i]] =
m_d->originalPoints[indexes[i]] * lambda +
m_d->transformedPoints[indexes[i]] * (1.0 - lambda);
224 m_d->transformedPointsContainer.movePoint(indexes[i], oldPosition,
m_d->transformedPoints[indexes[i]]);
228template <
class ProcessOp>
229void KisLiquifyTransformWorker::Private::
230processTransformedPixelsBuildUp(ProcessOp op,
234 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
235 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
236 2 * maxDist, 2 * maxDist);
238 accumulatedBrushStrokes |= clipRect;
241 transformedPointsContainer.findAllInRange(indexes, base, maxDist);
243 for (
int i = 0; i < indexes.count(); i++) {
245 QPointF diff = transformedPoints[indexes[i]] - base;
247 if (dist > maxDist)
continue;
249 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
250 QPointF oldPosition = transformedPoints[indexes[i]];
251 transformedPoints[indexes[i]] = op(transformedPoints[indexes[i]], base, diff, lambda);
254 transformedPointsContainer.movePoint(indexes[i], oldPosition, transformedPoints[indexes[i]]);
259template <
class ProcessOp>
260void KisLiquifyTransformWorker::Private::
261processTransformedPixelsWash(ProcessOp op,
266 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
267 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
268 2 * maxDist, 2 * maxDist);
270 accumulatedBrushStrokes |= clipRect;
273 transformedPoints.size());
279 originalPointsContainer.findAllInRange(indexes, base, maxDist);
280 for (
int i = 0; i < indexes.count(); i++) {
282 QPointF diff = originalPoints[indexes[i]] - base;
285 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
286 QPointF dstPt = op(originalPoints[indexes[i]], base, diff, lambda);
288 if (
kisDistance(dstPt, originalPoints[indexes[i]]) >
kisDistance(transformedPoints[indexes[i]], originalPoints[indexes[i]])) {
289 QPointF oldPosition = transformedPoints[indexes[i]];
290 transformedPoints[indexes[i]] = (1.0 - flow) * transformedPoints[indexes[i]] + flow * dstPt;
292 transformedPointsContainer.movePoint(indexes[i], oldPosition, transformedPoints[indexes[i]]);
297template <
class ProcessOp>
298void KisLiquifyTransformWorker::Private::
299processTransformedPixels(ProcessOp op,
306 processTransformedPixelsWash(op, base, sigma, flow);
308 processTransformedPixelsBuildUp(op, base, sigma);
344 return base + (1.0 +
m_scale * lambda) * diff;
365 const qreal angle =
m_angle * lambda;
366 const qreal sinA = std::sin(angle);
367 const qreal cosA = std::cos(angle);
369 qreal x = cosA * diff.x() + sinA * diff.y();
370 qreal y = -sinA * diff.x() + cosA * diff.y();
372 return base + QPointF(x, y);
383 const QPointF &offset,
389 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
399 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
409 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
419 QRect correctSubGrid = calculateCorrectSubGrid(
m_d->srcBounds,
m_d->pixelPrecision,
m_d->accumulatedBrushStrokes,
m_d->gridSize);
427 iterateThroughGrid<AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
430 m_d->transformedPoints,
432 QList<QRectF> areasToCopy = cutOutSubgridFromBounds(correctSubGrid,
m_d->srcBounds,
m_d->gridSize,
m_d->originalPoints);
433 for (
int i = 0; i < areasToCopy.length(); i++) {
440 const qreal margin = 0.05;
441 QRect resultRect =
m_d->transformedPointsContainer.exactBounds().toRect();
453 return m_d->accumulatedBrushStrokes;
460 m_d->srcBounds = t.mapRect(
m_d->srcBounds);
463 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
464 m_d->originalPointsContainer.movePoint(i,
m_d->originalPoints[i], t.map(
m_d->originalPoints[i]));
465 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i], t.map(
m_d->transformedPoints[i]));
467 m_d->originalPoints[i] = t.map(
m_d->originalPoints[i]);
468 m_d->transformedPoints[i] = t.map(
m_d->transformedPoints[i]);
479 using namespace std::placeholders;
481 typedef QPointF (QTransform::*MapFuncType)(
const QPointF&)
const;
482 return std::bind(
static_cast<MapFuncType
>(&QTransform::map), &transform, _1);
486 const QPointF &srcImageOffset,
487 const QTransform &imageToThumbTransform,
507 std::transform(originalPointsLocal.begin(), originalPointsLocal.end(),
508 originalPointsLocal.begin(), mapFunc);
510 std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(),
511 transformedPointsLocal.begin(), mapFunc);
514 Q_FOREACH (
const QPointF &pt, transformedPointsLocal) {
518 const QRectF
srcBounds(srcImageOffset, srcImage.size());
521 QPointF dstQImageOffset = dstBounds.topLeft();
522 *newOffset = dstQImageOffset;
524 QRect dstBoundsI = dstBounds.toAlignedRect();
526 QImage dstImage(dstBoundsI.size(), srcImage.format());
538 GridIterationTools::iterateThroughGrid<GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
541 transformedPointsLocal,
547 for (
int i = 0; i < areasToCopy.length(); i++) {
548 polygonOp.
fastCopyArea(imageToThumbTransform.map(QPolygonF(areasToCopy[i])));
555 QDomDocument doc = e->ownerDocument();
556 QDomElement liqEl = doc.createElement(
"liquify_points");
557 e->appendChild(liqEl);
568 QDomElement liquifyEl;
589 warnKrita <<
"WARNING: Failed to load liquify worker from XML";
599 numPoints != worker->
m_d->originalPoints.size() ||
601 warnKrita <<
"WARNING: Inconsistent number of points!";
612 QRectF changedRect = QRectF();
614 for (
int i = 0; i < numPoints; i++) {
622 worker->
m_d->transformedPointsContainer.initializeWith(worker->
m_d->transformedPoints);
623 worker->
m_d->originalPointsContainer.initializeWith(worker->
m_d->originalPoints);
625 worker->
m_d->accumulatedBrushStrokes = changedRect;
const KoColorSpace * colorSpace() const
#define KIS_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_ASSERT_RECOVER_RETURN(cond)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Rect blowRect(const Rect &rect, qreal coeff)
void accumulateBounds(const Point &pt, Rect *bounds)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
bool findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages)
bool loadValue(const QDomElement &e, float *v)
QVector< QPointF > m_points
AllPointsFetcherOp(QRectF srcRect)
void processPoint(int col, int row, int prevCol, int prevRow, int colIndex, int rowIndex)
QPointF operator()(const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda)
static const qreal maxDistCoeff
static const qreal maxDistCoeff
QPointF operator()(const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda)
TranslateOp(const QPointF &offset)
QPointF operator()(const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda)
static const qreal maxDistCoeff