21 : srcBounds(_srcBounds)
22 , originalPointsContainer(_srcBounds)
23 , transformedPointsContainer(_srcBounds)
25 , pixelPrecision(_pixelPrecision)
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;
191 m_d->accumulatedBrushStrokes.translate(offset);
198 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
199 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i],
m_d->transformedPoints[i] + offset);
200 m_d->transformedPoints[i] += offset;
208 const qreal maxDistCoeff = 3.0;
209 const qreal maxDist = maxDistCoeff * sigma;
212 m_d->transformedPoints.size());
215 m_d->transformedPointsContainer.findAllInRange(indexes, base, maxDist);
216 for (
int i = 0; i < indexes.count(); i++) {
218 QPointF diff =
m_d->transformedPoints[indexes[i]] - base;
220 qreal lambda = exp(-0.5 *
pow2(dist / sigma));
223 QPointF oldPosition =
m_d->transformedPoints[indexes[i]];
224 m_d->transformedPoints[indexes[i]] =
m_d->originalPoints[indexes[i]] * lambda +
m_d->transformedPoints[indexes[i]] * (1.0 - lambda);
226 m_d->transformedPointsContainer.movePoint(indexes[i], oldPosition,
m_d->transformedPoints[indexes[i]]);
230template <
class ProcessOp>
231void KisLiquifyTransformWorker::Private::
232processTransformedPixelsBuildUp(ProcessOp op,
236 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
237 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
238 2 * maxDist, 2 * maxDist);
240 accumulatedBrushStrokes |=
kisGrowRect(clipRect, pixelPrecision);
243 transformedPointsContainer.findAllInRange(indexes, base, maxDist);
245 for (
int i = 0; i < indexes.count(); i++) {
247 QPointF diff = transformedPoints[indexes[i]] - base;
249 if (dist > maxDist)
continue;
251 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
252 QPointF oldPosition = transformedPoints[indexes[i]];
253 transformedPoints[indexes[i]] = op(transformedPoints[indexes[i]], base, diff, lambda);
256 transformedPointsContainer.movePoint(indexes[i], oldPosition, transformedPoints[indexes[i]]);
261template <
class ProcessOp>
262void KisLiquifyTransformWorker::Private::
263processTransformedPixelsWash(ProcessOp op,
268 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
269 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
270 2 * maxDist, 2 * maxDist);
272 accumulatedBrushStrokes |=
kisGrowRect(clipRect, pixelPrecision);
275 transformedPoints.size());
281 originalPointsContainer.findAllInRange(indexes, base, maxDist);
282 for (
int i = 0; i < indexes.count(); i++) {
284 QPointF diff = originalPoints[indexes[i]] - base;
287 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
288 QPointF dstPt = op(originalPoints[indexes[i]], base, diff, lambda);
290 if (
kisDistance(dstPt, originalPoints[indexes[i]]) >
kisDistance(transformedPoints[indexes[i]], originalPoints[indexes[i]])) {
291 QPointF oldPosition = transformedPoints[indexes[i]];
292 transformedPoints[indexes[i]] = (1.0 - flow) * transformedPoints[indexes[i]] + flow * dstPt;
294 transformedPointsContainer.movePoint(indexes[i], oldPosition, transformedPoints[indexes[i]]);
299template <
class ProcessOp>
300void KisLiquifyTransformWorker::Private::
301processTransformedPixels(ProcessOp op,
308 processTransformedPixelsWash(op, base, sigma, flow);
310 processTransformedPixelsBuildUp(op, base, sigma);
346 return base + (1.0 +
m_scale * lambda) * diff;
367 const qreal angle =
m_angle * lambda;
368 const qreal sinA = std::sin(angle);
369 const qreal cosA = std::cos(angle);
371 qreal x = cosA * diff.x() + sinA * diff.y();
372 qreal y = -sinA * diff.x() + cosA * diff.y();
374 return base + QPointF(x, y);
385 const QPointF &offset,
391 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
401 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
411 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
421 QRect correctSubGrid = calculateCorrectSubGrid(
m_d->srcBounds,
m_d->pixelPrecision,
m_d->accumulatedBrushStrokes,
m_d->gridSize);
429#ifdef DEBUG_PAINTING_POLYGONS
430 polygonOp.setDebugColor(Qt::red);
433 iterateThroughGrid<AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
436 m_d->transformedPoints,
438 QList<QRectF> areasToCopy = cutOutSubgridFromBounds(correctSubGrid,
m_d->srcBounds,
m_d->gridSize,
m_d->originalPoints);
439#ifdef DEBUG_PAINTING_POLYGONS
440 QList<QColor> colors = {Qt::blue, Qt::green, Qt::yellow, Qt::black};
442 for (
int i = 0; i < areasToCopy.length(); i++) {
443#ifdef DEBUG_PAINTING_POLYGONS
444 polygonOp.setDebugColor(colors[i]);
452 const qreal margin = 0.05;
453 QRect resultRect =
m_d->transformedPointsContainer.exactBounds().toRect();
465 return m_d->accumulatedBrushStrokes;
472 m_d->srcBounds = t.mapRect(
m_d->srcBounds);
475 for (
int i = 0; i <
m_d->transformedPoints.count(); i++) {
476 m_d->originalPointsContainer.movePoint(i,
m_d->originalPoints[i], t.map(
m_d->originalPoints[i]));
477 m_d->transformedPointsContainer.movePoint(i,
m_d->transformedPoints[i], t.map(
m_d->transformedPoints[i]));
479 m_d->originalPoints[i] = t.map(
m_d->originalPoints[i]);
480 m_d->transformedPoints[i] = t.map(
m_d->transformedPoints[i]);
482 m_d->accumulatedBrushStrokes = t.map(
m_d->accumulatedBrushStrokes).boundingRect();
483 if (t == QTransform::fromScale(t.m11(), t.m22()) && t.m11() == t.m22()) {
484 m_d->pixelPrecision *= t.m11();
498 using namespace std::placeholders;
500 typedef QPointF (QTransform::*MapFuncType)(
const QPointF&)
const;
501 return std::bind(
static_cast<MapFuncType
>(&QTransform::map), &transform, _1);
505 const QPointF &srcImageOffset,
506 const QTransform &imageToThumbTransform,
526 std::transform(originalPointsLocal.begin(), originalPointsLocal.end(),
527 originalPointsLocal.begin(), mapFunc);
529 std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(),
530 transformedPointsLocal.begin(), mapFunc);
533 Q_FOREACH (
const QPointF &pt, transformedPointsLocal) {
537 const QRectF
srcBounds(srcImageOffset, srcImage.size());
540 QPointF dstQImageOffset = dstBounds.topLeft();
541 *newOffset = dstQImageOffset;
543 QRect dstBoundsI = dstBounds.toAlignedRect();
545 QImage dstImage(dstBoundsI.size(), srcImage.format());
557 GridIterationTools::iterateThroughGrid<GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
560 transformedPointsLocal,
566 const qreal
eps = 0.001;
567 for (
int i = 0; i < areasToCopy.length(); i++) {
568 QPolygonF transformed = imageToThumbTransform.map(QPolygonF(areasToCopy[i]));
570 polygonOp.
fastCopyArea(transformed.boundingRect().toRect());
572 polygonOp.operator()(transformed, transformed);
581 QDomDocument doc = e->ownerDocument();
582 QDomElement liqEl = doc.createElement(
"liquify_points");
583 e->appendChild(liqEl);
594 QDomElement liquifyEl;
615 warnKrita <<
"WARNING: Failed to load liquify worker from XML";
625 numPoints != worker->
m_d->originalPoints.size() ||
627 warnKrita <<
"WARNING: Inconsistent number of points!";
638 QRectF changedRect = QRectF();
640 for (
int i = 0; i < numPoints; i++) {
650 worker->
m_d->transformedPointsContainer.initializeWith(worker->
m_d->transformedPoints);
651 worker->
m_d->originalPointsContainer.initializeWith(worker->
m_d->originalPoints);
653 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)
bool isPolygonPixelAlignedRect(const Polygon &poly, Difference tolerance)
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