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) {
152 QPointF pt(col, row);
164void KisLiquifyTransformWorker::Private::preparePoints()
172 const int numPoints = pointsOp.
m_points.size();
177 transformedPoints = pointsOp.
m_points;
179 originalPointsContainer.initializeWithGridPoints(srcBounds, pixelPrecision);
180 transformedPointsContainer.initializeWithGridPoints(srcBounds, pixelPrecision);
187 m_d->transformedPoints.size());
191 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
192 m_d->originalPointsContainer.movePoint(i,
m_d->originalPoints[i],
m_d->originalPoints[i] + offset);
193 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i],
m_d->transformedPoints[i] + offset);
195 m_d->originalPoints[i] += offset;
196 m_d->transformedPoints[i] += offset;
199 m_d->accumulatedBrushStrokes.translate(offset);
206 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
207 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i],
m_d->transformedPoints[i] + offset);
208 m_d->transformedPoints[i] += offset;
216 const qreal maxDistCoeff = 3.0;
217 const qreal maxDist = maxDistCoeff * sigma;
220 m_d->transformedPoints.size());
223 m_d->transformedPointsContainer.findAllInRange(indexes, base, maxDist);
224 for (
int i = 0; i < indexes.count(); i++) {
226 QPointF diff =
m_d->transformedPoints[indexes[i]] - base;
228 qreal lambda = exp(-0.5 *
pow2(dist / sigma));
231 QPointF oldPosition =
m_d->transformedPoints[indexes[i]];
232 m_d->transformedPoints[indexes[i]] =
m_d->originalPoints[indexes[i]] * lambda +
m_d->transformedPoints[indexes[i]] * (1.0 - lambda);
234 m_d->transformedPointsContainer.movePoint(indexes[i], oldPosition,
m_d->transformedPoints[indexes[i]]);
238template <
class ProcessOp>
239void KisLiquifyTransformWorker::Private::
240processTransformedPixelsBuildUp(ProcessOp op,
244 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
245 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
246 2 * maxDist, 2 * maxDist);
248 accumulatedBrushStrokes |= clipRect;
251 transformedPointsContainer.findAllInRange(indexes, base, maxDist);
253 for (
int i = 0; i < indexes.count(); i++) {
255 QPointF diff = transformedPoints[indexes[i]] - base;
257 if (dist > maxDist)
continue;
259 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
260 QPointF oldPosition = transformedPoints[indexes[i]];
261 transformedPoints[indexes[i]] = op(transformedPoints[indexes[i]], base, diff, lambda);
264 transformedPointsContainer.movePoint(indexes[i], oldPosition, transformedPoints[indexes[i]]);
269template <
class ProcessOp>
270void KisLiquifyTransformWorker::Private::
271processTransformedPixelsWash(ProcessOp op,
276 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
277 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
278 2 * maxDist, 2 * maxDist);
280 accumulatedBrushStrokes |= clipRect;
283 transformedPoints.size());
289 originalPointsContainer.findAllInRange(indexes, base, maxDist);
290 for (
int i = 0; i < indexes.count(); i++) {
292 QPointF diff = originalPoints[indexes[i]] - base;
295 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
296 QPointF dstPt = op(originalPoints[indexes[i]], base, diff, lambda);
298 if (
kisDistance(dstPt, originalPoints[indexes[i]]) >
kisDistance(transformedPoints[indexes[i]], originalPoints[indexes[i]])) {
299 QPointF oldPosition = transformedPoints[indexes[i]];
300 transformedPoints[indexes[i]] = (1.0 - flow) * transformedPoints[indexes[i]] + flow * dstPt;
302 transformedPointsContainer.movePoint(indexes[i], oldPosition, transformedPoints[indexes[i]]);
307template <
class ProcessOp>
308void KisLiquifyTransformWorker::Private::
309processTransformedPixels(ProcessOp op,
316 processTransformedPixelsWash(op, base, sigma, flow);
318 processTransformedPixelsBuildUp(op, base, sigma);
354 return base + (1.0 +
m_scale * lambda) * diff;
375 const qreal angle =
m_angle * lambda;
376 const qreal sinA = std::sin(angle);
377 const qreal cosA = std::cos(angle);
379 qreal x = cosA * diff.x() + sinA * diff.y();
380 qreal y = -sinA * diff.x() + cosA * diff.y();
382 return base + QPointF(x, y);
393 const QPointF &offset,
399 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
409 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
419 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
429 QRectF accumulatedBrushStrokesWithMargin =
kisGrowRect(
m_d->accumulatedBrushStrokes, 2*
m_d->pixelPrecision);
430 QRect correctSubGrid = calculateCorrectSubGrid(
m_d->srcBounds,
m_d->pixelPrecision, accumulatedBrushStrokesWithMargin,
m_d->gridSize);
438#ifdef DEBUG_PAINTING_POLYGONS
439 polygonOp.setDebugColor(Qt::red);
442 iterateThroughGrid<AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
445 m_d->transformedPoints,
447 QList<QRectF> areasToCopy = cutOutSubgridFromBounds(correctSubGrid,
m_d->srcBounds,
m_d->gridSize,
m_d->originalPoints);
448#ifdef DEBUG_PAINTING_POLYGONS
449 QList<QColor> colors = {Qt::blue, Qt::green, Qt::yellow, Qt::black};
451 for (
int i = 0; i < areasToCopy.length(); i++) {
452#ifdef DEBUG_PAINTING_POLYGONS
453 polygonOp.setDebugColor(colors[i]);
461 const qreal margin = 0.05;
462 QRect resultRect =
m_d->transformedPointsContainer.exactBounds().toRect();
474 return m_d->accumulatedBrushStrokes;
481 m_d->srcBounds = t.mapRect(
m_d->srcBounds);
484 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
485 m_d->originalPointsContainer.movePoint(i,
m_d->originalPoints[i], t.map(
m_d->originalPoints[i]));
486 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i], t.map(
m_d->transformedPoints[i]));
488 m_d->originalPoints[i] = t.map(
m_d->originalPoints[i]);
489 m_d->transformedPoints[i] = t.map(
m_d->transformedPoints[i]);
491 m_d->accumulatedBrushStrokes = t.map(
m_d->accumulatedBrushStrokes).boundingRect();
492 if (t == QTransform::fromScale(t.m11(), t.m22()) && t.m11() == t.m22()) {
493 m_d->pixelPrecision *= t.m11();
507 using namespace std::placeholders;
509 typedef QPointF (QTransform::*MapFuncType)(
const QPointF&)
const;
510 return std::bind(
static_cast<MapFuncType
>(&QTransform::map), &transform, _1);
514 const QPointF &srcImageOffset,
515 const QTransform &imageToThumbTransform,
535 std::transform(originalPointsLocal.begin(), originalPointsLocal.end(),
536 originalPointsLocal.begin(), mapFunc);
538 std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(),
539 transformedPointsLocal.begin(), mapFunc);
542 Q_FOREACH (
const QPointF &pt, transformedPointsLocal) {
546 const QRectF
srcBounds(srcImageOffset, srcImage.size());
549 QPointF dstQImageOffset = dstBounds.topLeft();
550 *newOffset = dstQImageOffset;
552 QRect dstBoundsI = dstBounds.toAlignedRect();
554 QImage dstImage(dstBoundsI.size(), srcImage.format());
561 QRectF accumulatedBrushStrokesWithMargin =
kisGrowRect(
m_d->accumulatedBrushStrokes, 3*
m_d->pixelPrecision);
567 GridIterationTools::iterateThroughGrid<GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
570 transformedPointsLocal,
576 for (
int i = 0; i < areasToCopy.length(); i++) {
577 polygonOp.
fastCopyArea(imageToThumbTransform.map(QPolygonF(areasToCopy[i])));
584 QDomDocument doc = e->ownerDocument();
585 QDomElement liqEl = doc.createElement(
"liquify_points");
586 e->appendChild(liqEl);
597 QDomElement liquifyEl;
618 warnKrita <<
"WARNING: Failed to load liquify worker from XML";
628 numPoints != worker->
m_d->originalPoints.size() ||
630 warnKrita <<
"WARNING: Inconsistent number of points!";
641 QRectF changedRect = QRectF();
643 for (
int i = 0; i < numPoints; i++) {
651 worker->
m_d->transformedPointsContainer.initializeWith(worker->
m_d->transformedPoints);
652 worker->
m_d->originalPointsContainer.initializeWith(worker->
m_d->originalPoints);
654 worker->
m_d->accumulatedBrushStrokes = changedRect;
const KoColorSpace * colorSpace() const
#define KIS_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_ASSERT_RECOVER_RETURN(cond)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
T kisGrowRect(const T &rect, U offset)
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
void processPoint(int col, int row, int prevCol, int prevRow, int colIndex, int rowIndex)
AllPointsFetcherOp(QRectF srcRect, QSize gridSize)
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