Krita Source Code Documentation
Loading...
Searching...
No Matches
KisLiquifyTransformWorker Class Reference

#include <kis_liquify_transform_worker.h>

+ Inheritance diagram for KisLiquifyTransformWorker:

Public Member Functions

QRectF accumulatedStrokesBounds () const
 
QRect approxChangeRect (const QRect &rc)
 
QRect approxNeedRect (const QRect &rc, const QRect &fullBounds)
 
QSize gridSize () const
 
bool isIdentity () const
 
 KisLiquifyTransformWorker (const KisLiquifyTransformWorker &rhs)
 
 KisLiquifyTransformWorker (const QRect &srcBounds, KoUpdater *progress, int pixelPrecision=8)
 
bool operator== (const KisLiquifyTransformWorker &other) const
 
const QVector< QPointF > & originalPoints () const
 
int pointToIndex (const QPoint &cellPt)
 
void preparePoints ()
 
 Private (const QRect &_srcBounds, KoUpdater *_progress, int _pixelPrecision)
 
template<class ProcessOp >
void processTransformedPixels (ProcessOp op, const QPointF &base, qreal sigma, bool useWashMode, qreal flow)
 
template<class ProcessOp >
void processTransformedPixelsBuildUp (ProcessOp op, const QPointF &base, qreal sigma)
 
template<class ProcessOp >
void processTransformedPixelsWash (ProcessOp op, const QPointF &base, qreal sigma, qreal flow)
 
void restoreShapePoints (const QPointF &base, qreal amount, qreal sigma, bool preserveRotation, bool preserveScale, bool preserveStretch)
 
void rotatePoints (const QPointF &base, qreal angle, qreal sigma, bool useWashMode, qreal flow)
 
void run (KisPaintDeviceSP srcDevice, KisPaintDeviceSP dstDevice)
 
QImage runOnQImage (const QImage &srcImage, const QPointF &srcImageOffset, const QTransform &imageToThumbTransform, QPointF *newOffset)
 
void scalePoints (const QPointF &base, qreal scale, qreal sigma, bool useWashMode, qreal flow)
 
void toXML (QDomElement *e) const
 
QVector< QPointF > & transformedPoints ()
 
void transformSrcAndDst (const QTransform &t)
 
void translate (const QPointF &offset)
 
void translateDstSpace (const QPointF &offset)
 
void translatePoints (const QPointF &base, const QPointF &offset, qreal sigma, bool useWashMode, qreal flow)
 
void undoPoints (const QPointF &base, qreal amount, qreal sigma)
 
 ~KisLiquifyTransformWorker ()
 
- Public Member Functions inherited from Private
 Private (KisCanvas2 *c)
 

Static Public Member Functions

static KisLiquifyTransformWorkerfromXML (const QDomElement &e)
 

Public Attributes

QRectF accumulatedBrushStrokes
 
QSize gridSize
 
QVector< QPointF > originalPoints
 
KisSpatialContainer originalPointsContainer
 
int pixelPrecision
 
KoUpdaterprogress
 
QRect srcBounds
 
QVector< QPointF > transformedPoints
 
KisSpatialContainer transformedPointsContainer
 
- Public Attributes inherited from Private
KisCanvas2canvas
 
int displayedFrame
 
int intendedFrame
 

Private Attributes

const QScopedPointer< Privatem_d
 

Detailed Description

Definition at line 18 of file kis_liquify_transform_worker.cpp.

Constructor & Destructor Documentation

◆ KisLiquifyTransformWorker() [1/2]

KisLiquifyTransformWorker::KisLiquifyTransformWorker ( const QRect & srcBounds,
KoUpdater * progress,
int pixelPrecision = 8 )

Definition at line 68 of file kis_liquify_transform_worker.cpp.

72{
74
75 // TODO: implement 'progress' stuff
76 m_d->preparePoints();
77}
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
const QScopedPointer< Private > m_d

References KIS_ASSERT_RECOVER_RETURN, m_d, and srcBounds.

◆ KisLiquifyTransformWorker() [2/2]

KisLiquifyTransformWorker::KisLiquifyTransformWorker ( const KisLiquifyTransformWorker & rhs)

Definition at line 79 of file kis_liquify_transform_worker.cpp.

80 : m_d(new Private(*rhs.m_d.data()))
81{
82}

◆ ~KisLiquifyTransformWorker()

KisLiquifyTransformWorker::~KisLiquifyTransformWorker ( )

Definition at line 84 of file kis_liquify_transform_worker.cpp.

85{
86}

Member Function Documentation

◆ accumulatedStrokesBounds()

QRectF KisLiquifyTransformWorker::accumulatedStrokesBounds ( ) const

Definition at line 650 of file kis_liquify_transform_worker.cpp.

651{
652 return m_d->accumulatedBrushStrokes;
653}

References m_d.

◆ approxChangeRect()

QRect KisLiquifyTransformWorker::approxChangeRect ( const QRect & rc)

Definition at line 637 of file kis_liquify_transform_worker.cpp.

638{
639 const qreal margin = 0.05;
640 QRect resultRect = m_d->transformedPointsContainer.exactBounds().toRect();
641 return KisAlgebra2D::blowRect(resultRect | rc, margin);
642}
Rect blowRect(const Rect &rect, qreal coeff)

References KisAlgebra2D::blowRect(), and m_d.

◆ approxNeedRect()

QRect KisLiquifyTransformWorker::approxNeedRect ( const QRect & rc,
const QRect & fullBounds )

Definition at line 644 of file kis_liquify_transform_worker.cpp.

645{
646 Q_UNUSED(rc);
647 return fullBounds;
648}

◆ fromXML()

KisLiquifyTransformWorker * KisLiquifyTransformWorker::fromXML ( const QDomElement & e)
static

Definition at line 779 of file kis_liquify_transform_worker.cpp.

780{
781 QDomElement liquifyEl;
782
783 QRect srcBounds;
786 int pixelPrecision;
787 QSize gridSize;
788
789 bool result = false;
790
791
792 result =
793 KisDomUtils::findOnlyElement(e, "liquify_points", &liquifyEl) &&
794
795 KisDomUtils::loadValue(liquifyEl, "srcBounds", &srcBounds) &&
796 KisDomUtils::loadValue(liquifyEl, "originalPoints", &originalPoints) &&
797 KisDomUtils::loadValue(liquifyEl, "transformedPoints", &transformedPoints) &&
798 KisDomUtils::loadValue(liquifyEl, "pixelPrecision", &pixelPrecision) &&
799 KisDomUtils::loadValue(liquifyEl, "gridSize", &gridSize);
800
801 if (!result) {
802 warnKrita << "WARNING: Failed to load liquify worker from XML";
803 return new KisLiquifyTransformWorker(QRect(0,0,1024, 1024), 0, 8);
804 }
805
808
809 const int numPoints = originalPoints.size();
810
811 if (numPoints != transformedPoints.size() ||
812 numPoints != worker->m_d->originalPoints.size() ||
813 gridSize != worker->m_d->gridSize) {
814 warnKrita << "WARNING: Inconsistent number of points!";
815 warnKrita << ppVar(originalPoints.size());
818 warnKrita << ppVar(worker->m_d->originalPoints.size());
819 warnKrita << ppVar(worker->m_d->transformedPoints.size());
820 warnKrita << ppVar(worker->m_d->gridSize);
821
822 return worker;
823 }
824
825 QRectF changedRect = QRectF();
826
827 for (int i = 0; i < numPoints; i++) {
828 worker->m_d->originalPoints[i] = originalPoints[i];
829 worker->m_d->transformedPoints[i] = transformedPoints[i];
833 }
834 }
835 changedRect = kisGrowRect(changedRect, pixelPrecision);
836
837 worker->m_d->transformedPointsContainer.initializeWith(worker->m_d->transformedPoints);
838 worker->m_d->originalPointsContainer.initializeWith(worker->m_d->originalPoints);
839
840 worker->m_d->accumulatedBrushStrokes = changedRect;
841
842
843 return worker;
844}
#define warnKrita
Definition kis_debug.h:91
#define ppVar(var)
Definition kis_debug.h:159
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
void accumulateBounds(const Point &pt, Rect *bounds)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
bool findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages)
bool loadValue(const QDomElement &e, float *v)
KisLiquifyTransformWorker(const QRect &srcBounds, KoUpdater *progress, int pixelPrecision=8)

References KisAlgebra2D::accumulateBounds(), KisDomUtils::findOnlyElement(), KisAlgebra2D::fuzzyPointCompare(), gridSize, kisGrowRect(), KisLiquifyTransformWorker(), KisDomUtils::loadValue(), m_d, originalPoints, pixelPrecision, ppVar, srcBounds, transformedPoints, and warnKrita.

◆ gridSize()

QSize KisLiquifyTransformWorker::gridSize ( ) const

◆ isIdentity()

bool KisLiquifyTransformWorker::isIdentity ( ) const

Definition at line 108 of file kis_liquify_transform_worker.cpp.

109{
110 const qreal eps = 1e-6;
111 return KisAlgebra2D::fuzzyPointCompare(m_d->originalPoints, m_d->transformedPoints, eps);
112}
const qreal eps

References eps, KisAlgebra2D::fuzzyPointCompare(), and m_d.

◆ operator==()

bool KisLiquifyTransformWorker::operator== ( const KisLiquifyTransformWorker & other) const

Definition at line 88 of file kis_liquify_transform_worker.cpp.

89{
90 bool result =
91 m_d->srcBounds == other.m_d->srcBounds &&
92 m_d->pixelPrecision == other.m_d->pixelPrecision &&
93 m_d->gridSize == other.m_d->gridSize &&
94 m_d->originalPoints.size() == other.m_d->originalPoints.size() &&
95 m_d->transformedPoints.size() == other.m_d->transformedPoints.size();
96
97 if (!result) return false;
98
99 const qreal eps = 1e-6;
100
101 result =
102 KisAlgebra2D::fuzzyPointCompare(m_d->originalPoints, other.m_d->originalPoints, eps) &&
103 KisAlgebra2D::fuzzyPointCompare(m_d->transformedPoints, other.m_d->transformedPoints, eps);
104
105 return result;
106}

References eps, KisAlgebra2D::fuzzyPointCompare(), and m_d.

◆ originalPoints()

const QVector< QPointF > & KisLiquifyTransformWorker::originalPoints ( ) const

◆ pointToIndex()

int KisLiquifyTransformWorker::pointToIndex ( const QPoint & cellPt)

Definition at line 114 of file kis_liquify_transform_worker.cpp.

115{
116 return GridIterationTools::pointToIndex(cellPt, m_d->gridSize);
117}
int pointToIndex(const QPoint &cellPt, const QSize &gridSize)

References m_d, and GridIterationTools::pointToIndex().

◆ preparePoints()

void KisLiquifyTransformWorker::preparePoints ( )

◆ Private()

KisLiquifyTransformWorker::Private ( const QRect & _srcBounds,
KoUpdater * _progress,
int _pixelPrecision )
inline

Definition at line 20 of file kis_liquify_transform_worker.cpp.

23 : srcBounds(_srcBounds)
24 , originalPointsContainer(_srcBounds)
25 , transformedPointsContainer(_srcBounds)
26 , progress(_progress)
27 , pixelPrecision(_pixelPrecision)
28 {
29 }

◆ processTransformedPixels()

template<class ProcessOp >
void KisLiquifyTransformWorker::processTransformedPixels ( ProcessOp op,
const QPointF & base,
qreal sigma,
bool useWashMode,
qreal flow )

◆ processTransformedPixelsBuildUp()

template<class ProcessOp >
void KisLiquifyTransformWorker::processTransformedPixelsBuildUp ( ProcessOp op,
const QPointF & base,
qreal sigma )

◆ processTransformedPixelsWash()

template<class ProcessOp >
void KisLiquifyTransformWorker::processTransformedPixelsWash ( ProcessOp op,
const QPointF & base,
qreal sigma,
qreal flow )

◆ restoreShapePoints()

void KisLiquifyTransformWorker::restoreShapePoints ( const QPointF & base,
qreal amount,
qreal sigma,
bool preserveRotation,
bool preserveScale,
bool preserveStretch )

Definition at line 272 of file kis_liquify_transform_worker.cpp.

278{
279 const qreal maxDistCoeff = 3.0;
280 const qreal maxDist = maxDistCoeff * sigma;
281
282 KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() ==
283 m_d->transformedPoints.size());
284
285 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
286 2 * maxDist, 2 * maxDist);
287 m_d->accumulatedBrushStrokes |= kisGrowRect(clipRect, m_d->pixelPrecision);
288
289 QVector<int> indexes;
290 m_d->transformedPointsContainer.findAllInRange(indexes, base, maxDist);
291
293 samples.reserve(indexes.count());
294
295 qreal weightSum = 0.0;
296 Eigen::Vector2d originalCentroid(0.0, 0.0);
297 Eigen::Vector2d transformedCentroid(0.0, 0.0);
298
299 for (int i = 0; i < indexes.count(); i++) {
300 const int index = indexes[i];
301
302 const QPointF diff = m_d->transformedPoints[index] - base;
303 const qreal dist = KisAlgebra2D::norm(diff);
304 if (dist > maxDist) continue;
305
306 const qreal weight = exp(-0.5 * pow2(dist / sigma));
307 const qreal lambda = qBound<qreal>(0.0, weight * amount, 1.0);
308 if (lambda <= 0.0) continue;
309
310 samples << RestoreShapeSample{index, weight, lambda};
311
312 originalCentroid += weight * toEigenPoint(m_d->originalPoints[index]);
313 transformedCentroid += weight * toEigenPoint(m_d->transformedPoints[index]);
314 weightSum += weight;
315 }
316
317 if (samples.count() < 3 || weightSum <= 1e-12) return;
318
319 originalCentroid /= weightSum;
320 transformedCentroid /= weightSum;
321
322 Eigen::Matrix2d originalMoment = Eigen::Matrix2d::Zero();
323 Eigen::Matrix2d transformedOriginalMoment = Eigen::Matrix2d::Zero();
324
325 for (int i = 0; i < samples.count(); i++) {
326 const RestoreShapeSample &sample = samples[i];
327
328 const Eigen::Vector2d originalOffset =
329 toEigenPoint(m_d->originalPoints[sample.index]) - originalCentroid;
330 const Eigen::Vector2d transformedOffset =
331 toEigenPoint(m_d->transformedPoints[sample.index]) - transformedCentroid;
332
333 originalMoment += sample.weight * originalOffset * originalOffset.transpose();
334 transformedOriginalMoment += sample.weight * transformedOffset * originalOffset.transpose();
335 }
336
337 if (qAbs(originalMoment.determinant()) < 1e-12) return;
338
339 const Eigen::Matrix2d affine =
340 transformedOriginalMoment * originalMoment.inverse();
341
342 if (!isFinite(affine)) return;
343
344 Eigen::JacobiSVD<Eigen::Matrix2d> svd(affine, Eigen::ComputeFullU | Eigen::ComputeFullV);
345 Eigen::Matrix2d rotation = svd.matrixU() * svd.matrixV().transpose();
346
347 if (rotation.determinant() < 0.0) {
348 Eigen::Matrix2d u = svd.matrixU();
349 u.col(1) *= -1.0;
350 rotation = u * svd.matrixV().transpose();
351 }
352
353 if (!isFinite(rotation)) return;
354
355 const Eigen::Vector2d singularValues = svd.singularValues();
356 const qreal uniformScale =
357 preserveScale ? qMax<qreal>(1e-6, 0.5 * (singularValues.x() + singularValues.y())) : 1.0;
358
359 Eigen::Matrix2d targetLinear = Eigen::Matrix2d::Identity();
360 if (preserveRotation) {
361 targetLinear = rotation;
362 }
363 targetLinear *= uniformScale;
364
365 if (preserveStretch) {
366 const Eigen::Matrix2d stretchInRotationSpace = rotation.transpose() * affine;
367 qreal stretchX = qMax<qreal>(1e-6, qAbs(stretchInRotationSpace(0, 0)));
368 qreal stretchY = qMax<qreal>(1e-6, qAbs(stretchInRotationSpace(1, 1)));
369 const qreal stretchNormalization =
370 preserveScale ? uniformScale : qMax<qreal>(1e-6, std::sqrt(stretchX * stretchY));
371
372 Eigen::Matrix2d stretch = Eigen::Matrix2d::Identity();
373 stretch(0, 0) = stretchX / stretchNormalization;
374 stretch(1, 1) = stretchY / stretchNormalization;
375 targetLinear *= stretch;
376 }
377
378 QVector<PointUpdate> updates;
379 updates.reserve(samples.count());
380
381 Eigen::Vector2d updatedCentroid(0.0, 0.0);
382 qreal updatedWeightSum = 0.0;
383
384 for (int i = 0; i < samples.count(); i++) {
385 const RestoreShapeSample &sample = samples[i];
386 const Eigen::Vector2d oldPosition = toEigenPoint(m_d->transformedPoints[sample.index]);
387 const Eigen::Vector2d originalOffset =
388 toEigenPoint(m_d->originalPoints[sample.index]) - originalCentroid;
389 const Eigen::Vector2d targetPosition =
390 transformedCentroid + targetLinear * originalOffset;
391 const Eigen::Vector2d newPosition =
392 oldPosition * (1.0 - sample.lambda) + targetPosition * sample.lambda;
393
394 updates << PointUpdate{sample.index,
395 m_d->transformedPoints[sample.index],
396 fromEigenPoint(newPosition)};
397
398 updatedCentroid += sample.weight * newPosition;
399 updatedWeightSum += sample.weight;
400 }
401
402 if (updatedWeightSum <= 1e-12) return;
403 updatedCentroid /= updatedWeightSum;
404
405 const QPointF centroidCorrection = fromEigenPoint(transformedCentroid - updatedCentroid);
406
407 for (int i = 0; i < updates.count(); i++) {
408 PointUpdate &update = updates[i];
409 update.newPosition += centroidCorrection;
410 m_d->transformedPoints[update.index] = update.newPosition;
411 m_d->transformedPointsContainer.movePoint(update.index,
412 update.oldPosition,
413 update.newPosition);
414 }
415}
qreal u
T pow2(const T &x)
Definition kis_global.h:166
qreal norm(const T &a)
bool update(QSpinBox *spinBox)

References KIS_ASSERT_RECOVER_RETURN, kisGrowRect(), m_d, KisAlgebra2D::norm(), pow2(), and u.

◆ rotatePoints()

void KisLiquifyTransformWorker::rotatePoints ( const QPointF & base,
qreal angle,
qreal sigma,
bool useWashMode,
qreal flow )

Definition at line 591 of file kis_liquify_transform_worker.cpp.

596{
597 RotateOp op(angle);
598 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
599}

References m_d.

◆ run()

void KisLiquifyTransformWorker::run ( KisPaintDeviceSP srcDevice,
KisPaintDeviceSP dstDevice )

Definition at line 601 of file kis_liquify_transform_worker.cpp.

602{
603 KIS_SAFE_ASSERT_RECOVER_RETURN(*srcDevice->colorSpace() == *dstDevice->colorSpace());
604
605 dstDevice->clear();
606
607 using namespace GridIterationTools;
608 QRect correctSubGrid = calculateCorrectSubGrid(m_d->srcBounds, m_d->pixelPrecision, m_d->accumulatedBrushStrokes, m_d->gridSize);
609
610 PaintDevicePolygonOp polygonOp(srcDevice, dstDevice);
611 RegularGridIndexesOp indexesOp(m_d->gridSize);
612
613 bool canMergeRects = GridIterationTools::canProcessRectsInRandomOrder(indexesOp, m_d->transformedPoints, correctSubGrid);
614 polygonOp.setCanMergeRects(canMergeRects);
615
616#ifdef DEBUG_PAINTING_POLYGONS
617 polygonOp.setDebugColor(Qt::red);
618#endif
619
620 iterateThroughGrid<AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
621 m_d->gridSize,
622 m_d->originalPoints,
623 m_d->transformedPoints,
624 correctSubGrid);
625 QList<QRectF> areasToCopy = cutOutSubgridFromBounds(correctSubGrid, m_d->srcBounds, m_d->gridSize, m_d->originalPoints);
626#ifdef DEBUG_PAINTING_POLYGONS
627 QList<QColor> colors = {Qt::blue, Qt::green, Qt::yellow, Qt::black};
628#endif
629 for (int i = 0; i < areasToCopy.length(); i++) {
630#ifdef DEBUG_PAINTING_POLYGONS
631 polygonOp.setDebugColor(colors[i]);
632#endif
633 polygonOp.fastCopyArea(areasToCopy[i].toRect(), false);
634 }
635}
virtual void clear()
const KoColorSpace * colorSpace() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
bool canProcessRectsInRandomOrder(IndexesOp &indexesOp, const QVector< QPointF > &transformedPoints, QSize grid)
QList< QRectF > cutOutSubgridFromBounds(QRect subGrid, QRect srcBounds, const QSize &gridSize, const QVector< QPointF > &originalPoints)
QRect calculateCorrectSubGrid(QRect originalBoundsForGrid, int pixelPrecision, QRectF currentBounds, QSize gridSize)

References GridIterationTools::canProcessRectsInRandomOrder(), KisPaintDevice::clear(), KisPaintDevice::colorSpace(), GridIterationTools::PaintDevicePolygonOp::fastCopyArea(), KIS_SAFE_ASSERT_RECOVER_RETURN, m_d, and GridIterationTools::PaintDevicePolygonOp::setCanMergeRects().

◆ runOnQImage()

QImage KisLiquifyTransformWorker::runOnQImage ( const QImage & srcImage,
const QPointF & srcImageOffset,
const QTransform & imageToThumbTransform,
QPointF * newOffset )

Definition at line 691 of file kis_liquify_transform_worker.cpp.

695{
696 KIS_ASSERT_RECOVER(m_d->originalPoints.size() == m_d->transformedPoints.size()) {
697 return QImage();
698 }
699
700 KIS_ASSERT_RECOVER(!srcImage.isNull()) {
701 return QImage();
702 }
703
704 KIS_ASSERT_RECOVER(srcImage.format() == QImage::Format_ARGB32) {
705 return QImage();
706 }
707
708 QVector<QPointF> originalPointsLocal(m_d->originalPoints);
709 QVector<QPointF> transformedPointsLocal(m_d->transformedPoints);
710
711 PointMapFunction mapFunc = bindPointMapTransform(imageToThumbTransform);
712
713 std::transform(originalPointsLocal.begin(), originalPointsLocal.end(),
714 originalPointsLocal.begin(), mapFunc);
715
716 std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(),
717 transformedPointsLocal.begin(), mapFunc);
718
719 QRectF dstBounds;
720 Q_FOREACH (const QPointF &pt, transformedPointsLocal) {
721 KisAlgebra2D::accumulateBounds(pt, &dstBounds);
722 }
723
724 const QRectF srcBounds(srcImageOffset, srcImage.size());
725 dstBounds |= srcBounds;
726
727 QPointF dstQImageOffset = dstBounds.topLeft();
728 *newOffset = dstQImageOffset;
729
730 QRect dstBoundsI = dstBounds.toAlignedRect();
731
732 QImage dstImage(dstBoundsI.size(), srcImage.format());
733 dstImage.fill(0);
734
735 GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcImageOffset, dstQImageOffset);
737
738
739 QRect correctSubGrid = GridIterationTools::calculateCorrectSubGrid(m_d->srcBounds, m_d->pixelPrecision, m_d->accumulatedBrushStrokes, m_d->gridSize);
740 bool canMergeRects = GridIterationTools::canProcessRectsInRandomOrder(indexesOp, m_d->transformedPoints, correctSubGrid);
741 polygonOp.setCanMergeRects(canMergeRects);
742
743
744 GridIterationTools::iterateThroughGrid<GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
745 m_d->gridSize,
746 originalPointsLocal,
747 transformedPointsLocal,
748 correctSubGrid);
749
750
751 QList<QRectF> areasToCopy = GridIterationTools::cutOutSubgridFromBounds(correctSubGrid, m_d->srcBounds, m_d->gridSize, m_d->originalPoints);
752 polygonOp.setCanMergeRects(false);
753 const qreal eps = 0.001;
754 for (int i = 0; i < areasToCopy.length(); i++) {
755 QPolygonF transformed = imageToThumbTransform.map(QPolygonF(areasToCopy[i]));
757 polygonOp.fastCopyArea(transformed.boundingRect().toRect());
758 } else {
759 polygonOp.operator()(transformed, transformed);
760 }
761 }
762 return dstImage;
763}
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
std::function< QPointF(const QPointF &)> PointMapFunction
PointMapFunction bindPointMapTransform(const QTransform &transform)
bool isPolygonPixelAlignedRect(const Polygon &poly, Difference tolerance)

References KisAlgebra2D::accumulateBounds(), bindPointMapTransform(), GridIterationTools::calculateCorrectSubGrid(), GridIterationTools::canProcessRectsInRandomOrder(), GridIterationTools::cutOutSubgridFromBounds(), eps, GridIterationTools::QImagePolygonOp::fastCopyArea(), KisAlgebra2D::isPolygonPixelAlignedRect(), KIS_ASSERT_RECOVER, m_d, GridIterationTools::QImagePolygonOp::setCanMergeRects(), and srcBounds.

◆ scalePoints()

void KisLiquifyTransformWorker::scalePoints ( const QPointF & base,
qreal scale,
qreal sigma,
bool useWashMode,
qreal flow )

Definition at line 581 of file kis_liquify_transform_worker.cpp.

586{
587 ScaleOp op(scale);
588 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
589}

References m_d.

◆ toXML()

void KisLiquifyTransformWorker::toXML ( QDomElement * e) const

Definition at line 766 of file kis_liquify_transform_worker.cpp.

767{
768 QDomDocument doc = e->ownerDocument();
769 QDomElement liqEl = doc.createElement("liquify_points");
770 e->appendChild(liqEl);
771
772 KisDomUtils::saveValue(&liqEl, "srcBounds", m_d->srcBounds);
773 KisDomUtils::saveValue(&liqEl, "originalPoints", m_d->originalPoints);
774 KisDomUtils::saveValue(&liqEl, "transformedPoints", m_d->transformedPoints);
775 KisDomUtils::saveValue(&liqEl, "pixelPrecision", m_d->pixelPrecision);
776 KisDomUtils::saveValue(&liqEl, "gridSize", m_d->gridSize);
777}
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)

References m_d, and KisDomUtils::saveValue().

◆ transformedPoints()

QVector< QPointF > & KisLiquifyTransformWorker::transformedPoints ( )

◆ transformSrcAndDst()

void KisLiquifyTransformWorker::transformSrcAndDst ( const QTransform & t)

Definition at line 655 of file kis_liquify_transform_worker.cpp.

656{
657 KIS_SAFE_ASSERT_RECOVER_RETURN(t.type() <= QTransform::TxScale);
658
659 m_d->srcBounds = t.mapRect(m_d->srcBounds);
660
661 // TODO: do it within Spatial Container
662 for (int i = 0; i < m_d->transformedPoints.count(); i++) {
663 m_d->originalPointsContainer.movePoint(i, m_d->originalPoints[i], t.map(m_d->originalPoints[i]));
664 m_d->transformedPointsContainer.movePoint(i, m_d->transformedPoints[i], t.map(m_d->transformedPoints[i]));
665
666 m_d->originalPoints[i] = t.map(m_d->originalPoints[i]);
667 m_d->transformedPoints[i] = t.map(m_d->transformedPoints[i]);
668 }
669 m_d->accumulatedBrushStrokes = t.map(m_d->accumulatedBrushStrokes).boundingRect();
670 if (t == QTransform::fromScale(t.m11(), t.m22()) && t.m11() == t.m22()) {
671 m_d->pixelPrecision *= t.m11();
672 KIS_SAFE_ASSERT_RECOVER(m_d->pixelPrecision > 0) { m_d->pixelPrecision = 1; }
673 KIS_SAFE_ASSERT_RECOVER(QList<int>({1, 2, 4, 8, 16}).contains(m_d->pixelPrecision) || m_d->pixelPrecision%16 == 0) { m_d->pixelPrecision = 1; }
674 // should check if pixelPrecision is a power of 2, but that's more complicated
675 }
676}
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126

References KIS_SAFE_ASSERT_RECOVER, KIS_SAFE_ASSERT_RECOVER_RETURN, and m_d.

◆ translate()

void KisLiquifyTransformWorker::translate ( const QPointF & offset)

Definition at line 178 of file kis_liquify_transform_worker.cpp.

179{
180 KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() ==
181 m_d->transformedPoints.size());
182
183 // TODO: make it within Spatial Container, either a hidden offset, or just offsetting all points at once
184 // and benchmark
185 for (int i = 0; i < m_d->transformedPoints.count(); i++) {
186 m_d->originalPointsContainer.movePoint(i, m_d->originalPoints[i], m_d->originalPoints[i] + offset);
187 m_d->transformedPointsContainer.movePoint(i, m_d->transformedPoints[i], m_d->transformedPoints[i] + offset);
188
189 m_d->originalPoints[i] += offset;
190 m_d->transformedPoints[i] += offset;
191 }
192
193 m_d->accumulatedBrushStrokes.translate(offset);
194}

References KIS_ASSERT_RECOVER_RETURN, and m_d.

◆ translateDstSpace()

void KisLiquifyTransformWorker::translateDstSpace ( const QPointF & offset)

Definition at line 196 of file kis_liquify_transform_worker.cpp.

197{
198 // TODO: make it within Spatial Container, either a hidden offset, or just offsetting all points at once
199 // and benchmark
200 for (int i = 0; i < m_d->transformedPoints.count(); i++) {
201 m_d->transformedPointsContainer.movePoint(i, m_d->transformedPoints[i], m_d->transformedPoints[i] + offset);
202 m_d->transformedPoints[i] += offset;
203 }
204}

References m_d.

◆ translatePoints()

void KisLiquifyTransformWorker::translatePoints ( const QPointF & base,
const QPointF & offset,
qreal sigma,
bool useWashMode,
qreal flow )

Definition at line 571 of file kis_liquify_transform_worker.cpp.

576{
577 TranslateOp op(offset);
578 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
579}

References m_d.

◆ undoPoints()

void KisLiquifyTransformWorker::undoPoints ( const QPointF & base,
qreal amount,
qreal sigma )

Definition at line 206 of file kis_liquify_transform_worker.cpp.

209{
210 const qreal maxDistCoeff = 3.0;
211 const qreal maxDist = maxDistCoeff * sigma;
212
213 KIS_ASSERT_RECOVER_RETURN(m_d->originalPoints.size() ==
214 m_d->transformedPoints.size());
215
216 QVector<int> indexes;
217 m_d->transformedPointsContainer.findAllInRange(indexes, base, maxDist);
218 for (int i = 0; i < indexes.count(); i++) {
219
220 QPointF diff = m_d->transformedPoints[indexes[i]] - base;
221 qreal dist = KisAlgebra2D::norm(diff);
222 qreal lambda = exp(-0.5 * pow2(dist / sigma));
223 lambda *= amount;
224
225 QPointF oldPosition = m_d->transformedPoints[indexes[i]];
226 m_d->transformedPoints[indexes[i]] = m_d->originalPoints[indexes[i]] * lambda + m_d->transformedPoints[indexes[i]] * (1.0 - lambda);
227
228 m_d->transformedPointsContainer.movePoint(indexes[i], oldPosition, m_d->transformedPoints[indexes[i]]);
229 }
230}

References KIS_ASSERT_RECOVER_RETURN, m_d, KisAlgebra2D::norm(), and pow2().

Member Data Documentation

◆ accumulatedBrushStrokes

QRectF KisLiquifyTransformWorker::accumulatedBrushStrokes

Definition at line 39 of file kis_liquify_transform_worker.cpp.

◆ gridSize

QSize KisLiquifyTransformWorker::gridSize

Definition at line 43 of file kis_liquify_transform_worker.cpp.

◆ m_d

const QScopedPointer<Private> KisLiquifyTransformWorker::m_d
private

Definition at line 93 of file kis_liquify_transform_worker.h.

◆ originalPoints

const QVector< QPointF > & KisLiquifyTransformWorker::originalPoints

Definition at line 33 of file kis_liquify_transform_worker.cpp.

◆ originalPointsContainer

KisSpatialContainer KisLiquifyTransformWorker::originalPointsContainer

Definition at line 36 of file kis_liquify_transform_worker.cpp.

◆ pixelPrecision

int KisLiquifyTransformWorker::pixelPrecision

Definition at line 42 of file kis_liquify_transform_worker.cpp.

◆ progress

KoUpdater* KisLiquifyTransformWorker::progress

Definition at line 41 of file kis_liquify_transform_worker.cpp.

◆ srcBounds

QRect KisLiquifyTransformWorker::srcBounds

Definition at line 31 of file kis_liquify_transform_worker.cpp.

◆ transformedPoints

QVector< QPointF > & KisLiquifyTransformWorker::transformedPoints

Definition at line 34 of file kis_liquify_transform_worker.cpp.

◆ transformedPointsContainer

KisSpatialContainer KisLiquifyTransformWorker::transformedPointsContainer

Definition at line 37 of file kis_liquify_transform_worker.cpp.


The documentation for this class was generated from the following files: