Commit 9c8df73c by Dmitry Kazakov

### Implemented Liquify Transoform Worker

`It shares the code with the cage transform almost entirely`
parent 9cd95e9c
 ... ... @@ -203,6 +203,7 @@ set(kritaimage_LIB_SRCS # bsplines/kis_nu_bspline_2d.cpp kis_warptransform_worker.cc kis_cage_transform_worker.cpp kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_algebra_2d.cpp kis_transparency_mask.cc ... ...
 ... ... @@ -167,6 +167,28 @@ inline void accumulateBounds(const Point &pt, Rect *bounds) } } template inline Point clampPoint(Point pt, const Rect &bounds) { if (pt.x() > bounds.right()) { pt.rx() = bounds.right(); } if (pt.x() < bounds.left()) { pt.rx() = bounds.left(); } if (pt.y() > bounds.bottom()) { pt.ry() = bounds.bottom(); } if (pt.y() < bounds.top()) { pt.ry() = bounds.top(); } return pt; } } #endif /* __KIS_ALGEBRA_2D_H */
 ... ... @@ -20,7 +20,6 @@ #include "kis_grid_interpolation_tools.h" #include "kis_green_coordinates_math.h" #include "kis_algebra_2d.h" #include ... ... @@ -28,8 +27,6 @@ #include "kis_selection.h" #include "kis_painter.h" #include "kis_four_point_interpolator_forward.h" struct KisCageTransformWorker::Private { ... ... @@ -69,31 +66,12 @@ struct KisCageTransformWorker::Private QVector calculateTransformedPoints(); /** * A-----B The polygons will be in the following order: * | | * | | polygon << A << B << D << C; * C-----D */ inline QVector calculateCellIndexes(int col, int row); inline QVector calculateMappedIndexes(int col, int row, int *numExistingPoints); inline int pointToIndex(const QPoint &cellPt); int tryGetValidIndex(const QPoint &cellPt); bool getOrthogonalPointApproximation(const QPoint &cellPt, const QVector &transformedPoints, QPointF *srcPoint, QPointF *dstPoint); inline QPoint pointIndexToColRow(QPoint baseColRow, int index); template void iterateThroughGrid(PolygonOp polygonOp, const QVector &transformedPoints); struct MapIndexesOp; }; KisCageTransformWorker::KisCageTransformWorker(KisPaintDeviceSP dev, ... ... @@ -126,12 +104,6 @@ void KisCageTransformWorker::setTransformedCage(const QVector &transfor struct PointsFetcherOp { static const QPointF invalidPoint; static inline bool isPointValid(const QPointF &pt) { return pt.x() > -1e10 && pt.y() > -1e10; } PointsFetcherOp(const QPolygonF &cagePolygon) : m_cagePolygon(cagePolygon), m_numValidPoints(0) ... ... @@ -172,8 +144,6 @@ struct PointsFetcherOp int m_numValidPoints; }; const QPointF PointsFetcherOp::invalidPoint(-1e12, -1e12); void KisCageTransformWorker::prepareTransform() { if (m_d->origCage.size() < 3) return; ... ... @@ -232,29 +202,13 @@ QVector KisCageTransformWorker::Private::calculateTransformedPoints() return transformedPoints; } inline QVector KisCageTransformWorker::Private:: calculateCellIndexes(int col, int row) { const int tl = col + row * gridSize.width(); const int tr = tl + 1; const int bl = tl + gridSize.width(); const int br = bl + 1; QVector cellIndexes; cellIndexes << tl; cellIndexes << tr; cellIndexes << br; cellIndexes << bl; return cellIndexes; } inline QVector KisCageTransformWorker::Private:: calculateMappedIndexes(int col, int row, int *numExistingPoints) { *numExistingPoints = 0; QVector cellIndexes = calculateCellIndexes(col, row); QVector cellIndexes = GridIterationTools::calculateCellIndexes(col, row, gridSize); for (int i = 0; i < 4; i++) { cellIndexes[i] = allToValidPointsMap[cellIndexes[i]]; ... ... @@ -264,12 +218,7 @@ calculateMappedIndexes(int col, int row, return cellIndexes; } inline int KisCageTransformWorker::Private:: pointToIndex(const QPoint &cellPt) { return cellPt.x() + cellPt.y() * gridSize.width(); } int KisCageTransformWorker::Private:: tryGetValidIndex(const QPoint &cellPt) ... ... @@ -281,194 +230,39 @@ tryGetValidIndex(const QPoint &cellPt) cellPt.y() >= 0 && cellPt.x() < gridSize.width() - 1 && cellPt.y() < gridSize.height() - 1 && (index = allToValidPointsMap[pointToIndex(cellPt)]) >= 0, index; (index = allToValidPointsMap[GridIterationTools::pointToIndex(cellPt, gridSize)]) >= 0, index; } struct PointExtension { int near; int far; }; bool KisCageTransformWorker::Private:: getOrthogonalPointApproximation(const QPoint &cellPt, const QVector &transformedPoints, QPointF *srcPoint, QPointF *dstPoint) { QVector extensionPoints; PointExtension ext; // left if ((ext.near = tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) { extensionPoints << ext; } // top if ((ext.near = tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) { extensionPoints << ext; } // right if ((ext.near = tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) { extensionPoints << ext; } // bottom if ((ext.near = tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) { struct KisCageTransformWorker::Private::MapIndexesOp { extensionPoints << ext; MapIndexesOp(KisCageTransformWorker::Private *d) : m_d(d), m_srcCagePolygon(QPolygonF(m_d->origCage)) { } if (extensionPoints.isEmpty()) { // top-left if ((ext.near = tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) { extensionPoints << ext; } // top-right if ((ext.near = tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) { extensionPoints << ext; } // bottom-right if ((ext.near = tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) { extensionPoints << ext; } // bottom-left if ((ext.near = tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 && (ext.far = tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) { inline QVector calculateMappedIndexes(int col, int row, int *numExistingPoints) const { extensionPoints << ext; } return m_d->calculateMappedIndexes(col, row, numExistingPoints); } if (extensionPoints.isEmpty()) { return false; inline int tryGetValidIndex(const QPoint &cellPt) const { return m_d->tryGetValidIndex(cellPt); } int numResultPoints = 0; *srcPoint = allSrcPoints[pointToIndex(cellPt)]; *dstPoint = QPointF(); foreach (const PointExtension &ext, extensionPoints) { QPointF near = transformedPoints[ext.near]; QPointF far = transformedPoints[ext.far]; QPointF nearSrc = validPoints[ext.near]; QPointF farSrc = validPoints[ext.far]; QPointF base1 = nearSrc - farSrc; QPointF base2 = near - far; QPointF pt = near + KisAlgebra2D::transformAsBase(*srcPoint - nearSrc, base1, base2); *dstPoint += pt; numResultPoints++; inline QPointF getSrcPointForce(const QPoint &cellPt) const { return m_d->allSrcPoints[GridIterationTools::pointToIndex(cellPt, m_d->gridSize)]; } *dstPoint /= numResultPoints; return true; } inline QPoint KisCageTransformWorker::Private:: pointIndexToColRow(QPoint baseColRow, int index) { static QVector pointOffsets; if (pointOffsets.isEmpty()) { pointOffsets << QPoint(0,0); pointOffsets << QPoint(1,0); pointOffsets << QPoint(1,1); pointOffsets << QPoint(0,1); inline const QPolygonF srcCropPolygon() const { return m_srcCagePolygon; } return baseColRow + pointOffsets[index]; } template void KisCageTransformWorker::Private:: iterateThroughGrid(PolygonOp polygonOp, const QVector &transformedPoints) { QPolygonF cageDstPolygon(transfCage); QPolygonF cageSrcPolygon(origCage); QVector polygonPoints(4); for (int row = 0; row < gridSize.height() - 1; row++) { for (int col = 0; col < gridSize.width() - 1; col++) { int numExistingPoints = 0; polygonPoints = calculateMappedIndexes(col, row, &numExistingPoints); if (numExistingPoints == 0) continue; if (numExistingPoints < 4) { QPolygonF srcPolygon; QPolygonF dstPolygon; for (int i = 0; i < 4; i++) { const int index = polygonPoints[i]; if (index >= 0) { srcPolygon << validPoints[index]; dstPolygon << transformedPoints[index]; } else { QPoint cellPt = pointIndexToColRow(QPoint(col, row), i); QPointF srcPoint; QPointF dstPoint; bool result = getOrthogonalPointApproximation(cellPt, transformedPoints, &srcPoint, &dstPoint); if (!result) { //qDebug() << "*NOT* found any valid point" << allSrcPoints[pointToIndex(cellPt)] << "->" << ppVar(pt); break; } else { srcPolygon << srcPoint; dstPolygon << dstPoint; } } } if (dstPolygon.size() == 4) { QPolygonF srcClipPolygon(srcPolygon.intersected(cageSrcPolygon)); KisFourPointInterpolatorForward forwardTransform(srcPolygon, dstPolygon); for (int i = 0; i < srcClipPolygon.size(); i++) { const QPointF newPt = forwardTransform.map(srcClipPolygon[i]); srcClipPolygon[i] = newPt; } polygonOp(srcPolygon, dstPolygon, srcClipPolygon); } } else { QPolygonF srcPolygon; QPolygonF dstPolygon; for (int i = 0; i < 4; i++) { const int index = polygonPoints[i]; srcPolygon << validPoints[index]; dstPolygon << transformedPoints[index]; } polygonOp(srcPolygon, dstPolygon); } } } } KisCageTransformWorker::Private *m_d; QPolygonF m_srcCagePolygon; }; void KisCageTransformWorker::run() { ... ... @@ -495,7 +289,12 @@ void KisCageTransformWorker::run() } GridIterationTools::PaintDevicePolygonOp polygonOp(srcDev, tempDevice); m_d->iterateThroughGrid(polygonOp, transformedPoints); Private::MapIndexesOp indexesOp(m_d.data()); GridIterationTools::iterateThroughGrid (polygonOp, indexesOp, m_d->gridSize, m_d->validPoints, transformedPoints); QRect rect = tempDevice->extent(); KisPainter gc(m_d->dev); ... ... @@ -550,7 +349,12 @@ QImage KisCageTransformWorker::runOnQImage(QPointF *newOffset) } GridIterationTools::QImagePolygonOp polygonOp(m_d->srcImage, tempImage, m_d->srcImageOffset, dstQImageOffset); m_d->iterateThroughGrid(polygonOp, transformedPoints); Private::MapIndexesOp indexesOp(m_d.data()); GridIterationTools::iterateThroughGrid (polygonOp, indexesOp, m_d->gridSize, m_d->validPoints, transformedPoints); { QPainter gc(&dstImage); ... ...
 ... ... @@ -24,6 +24,8 @@ #include #include "kis_algebra_2d.h" #include "kis_four_point_interpolator_forward.h" #include "kis_four_point_interpolator_backward.h" #include "kis_iterator_ng.h" #include "kis_random_sub_accessor.h" ... ... @@ -265,10 +267,8 @@ struct QImagePolygonOp QPoint srcPointI = srcPoint.toPoint(); QPoint dstPointI = dstPoint.toPoint(); srcPointI.rx() = qBound(m_dstImageRect.x(), srcPointI.x(), m_dstImageRect.right()); srcPointI.ry() = qBound(m_dstImageRect.y(), srcPointI.y(), m_dstImageRect.bottom()); dstPointI.rx() = qBound(m_srcImageRect.x(), dstPointI.x(), m_srcImageRect.right()); dstPointI.ry() = qBound(m_srcImageRect.y(), dstPointI.y(), m_srcImageRect.bottom()); srcPointI = KisAlgebra2D::clampPoint(srcPointI, m_dstImageRect); dstPointI = KisAlgebra2D::clampPoint(dstPointI, m_srcImageRect); m_dstImage.setPixel(srcPointI, m_srcImage.pixel(dstPointI)); } ... ... @@ -299,6 +299,277 @@ struct QImagePolygonOp QRect m_dstImageRect; }; /*************************************************************/ /* Iteration through precalculated grid */ /*************************************************************/ /** * A-----B The polygons will be in the following order: * | | * | | polygon << A << B << D << C; * C-----D */ inline QVector calculateCellIndexes(int col, int row, const QSize &gridSize) { const int tl = col + row * gridSize.width(); const int tr = tl + 1; const int bl = tl + gridSize.width(); const int br = bl + 1; QVector cellIndexes; cellIndexes << tl; cellIndexes << tr; cellIndexes << br; cellIndexes << bl; return cellIndexes; } inline int pointToIndex(const QPoint &cellPt, const QSize &gridSize) { return cellPt.x() + cellPt.y() * gridSize.width(); } namespace Private { inline QPoint pointPolygonIndexToColRow(QPoint baseColRow, int index) { static QVector pointOffsets; if (pointOffsets.isEmpty()) { pointOffsets << QPoint(0,0); pointOffsets << QPoint(1,0); pointOffsets << QPoint(1,1); pointOffsets << QPoint(0,1); } return baseColRow + pointOffsets[index]; } struct PointExtension { int near; int far; }; } template bool getOrthogonalPointApproximation(const QPoint &cellPt, const QVector &originalPoints, const QVector &transformedPoints, IndexesOp indexesOp, QPointF *srcPoint, QPointF *dstPoint) { QVector extensionPoints; Private::PointExtension ext; // left if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) { extensionPoints << ext; } // top if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) { extensionPoints << ext; } // right if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) { extensionPoints << ext; } // bottom if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) { extensionPoints << ext; } if (extensionPoints.isEmpty()) { // top-left if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) { extensionPoints << ext; } // top-right if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) { extensionPoints << ext; } // bottom-right if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) { extensionPoints << ext; } // bottom-left if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 && (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) { extensionPoints << ext; } } if (extensionPoints.isEmpty()) { return false; } int numResultPoints = 0; *srcPoint = indexesOp.getSrcPointForce(cellPt); *dstPoint = QPointF(); foreach (const Private::PointExtension &ext, extensionPoints) { QPointF near = transformedPoints[ext.near]; QPointF far = transformedPoints[ext.far]; QPointF nearSrc = originalPoints[ext.near]; QPointF farSrc = originalPoints[ext.far]; QPointF base1 = nearSrc - farSrc; QPointF base2 = near - far; QPointF pt = near + KisAlgebra2D::transformAsBase(*srcPoint - nearSrc, base1, base2); *dstPoint += pt; numResultPoints++; } *dstPoint /= numResultPoints; return true; } template struct IncompletePolygonPolicy { static inline bool tryProcessPolygon(int col, int row, int numExistingPoints, PolygonOp &polygonOp, IndexesOp &indexesOp, const QVector &polygonPoints, const QVector &originalPoints, const QVector &transformedPoints) { if (numExistingPoints >= 4) return false; if (numExistingPoints == 0) return true; QPolygonF srcPolygon; QPolygonF dstPolygon; for (int i = 0; i < 4; i++) { const int index = polygonPoints[i]; if (index >= 0) { srcPolygon << originalPoints[index]; dstPolygon << transformedPoints[index]; } else { QPoint cellPt = Private::pointPolygonIndexToColRow(QPoint(col, row), i); QPointF srcPoint; QPointF dstPoint; bool result = getOrthogonalPointApproximation(cellPt, originalPoints, transformedPoints, indexesOp, &srcPoint, &dstPoint); if (!result) { //qDebug() << "*NOT* found any valid point" << allSrcPoints[pointToIndex(cellPt)] << "->" << ppVar(pt); break; } else { srcPolygon << srcPoint; dstPolygon << dstPoint; } } } if (dstPolygon.size() == 4) { QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon())); KisFourPointInterpolatorForward forwardTransform(srcPolygon, dstPolygon); for (int i = 0; i < srcClipPolygon.size(); i++) { const QPointF newPt = forwardTransform.map(srcClipPolygon[i]); srcClipPolygon[i] = newPt; } polygonOp(srcPolygon, dstPolygon, srcClipPolygon); } return true;