9#ifndef __KIS_GRID_INTERPOLATION_TOOLS_H
10#define __KIS_GRID_INTERPOLATION_TOOLS_H
27#ifdef DEBUG_PAINTING_POLYGONS
35 const int alignmentMask = ~(pixelPrecision - 1);
37 int alignedStart = (start + pixelPrecision - 1) & alignmentMask;
38 int alignedEnd = end & alignmentMask;
42 if (alignedEnd > alignedStart) {
43 size = (alignedEnd - alignedStart) / pixelPrecision + 1;
44 size += alignedStart != start;
45 size += alignedEnd != end;
47 size = 2 + (end - start >= pixelPrecision);
53inline QSize
calcGridSize(
const QRect &srcBounds,
const int pixelPrecision) {
58template <
class ProcessPolygon,
class ForwardTransform>
61 CellOp(ProcessPolygon &_polygonOp, ForwardTransform &_transformOp)
68 int prevCol,
int prevRow,
69 int colIndex,
int rowIndex) {
74 if (rowIndex >= 1 && colIndex >= 1) {
77 srcPolygon << QPointF(prevCol, prevRow);
78 srcPolygon << QPointF(col, prevRow);
79 srcPolygon << QPointF(col, row);
80 srcPolygon << QPointF(prevCol, row);
109template <
class ProcessCell>
111 const QRect &srcBounds,
112 const int pixelPrecision)
114 if (srcBounds.isEmpty())
return;
116 const int alignmentMask = ~(pixelPrecision - 1);
118 int prevRow = std::numeric_limits<int>::max();
119 int prevCol = std::numeric_limits<int>::max();
124 for (
int row = srcBounds.top(); row <= srcBounds.bottom();) {
125 for (
int col = srcBounds.left(); col <= srcBounds.right();) {
127 cellOp.processPoint(col, row,
132 col += pixelPrecision;
135 if (col > srcBounds.right() &&
136 col <= srcBounds.right() + pixelPrecision - 1) {
138 col = srcBounds.right();
140 col &= alignmentMask;
148 row += pixelPrecision;
151 if (row > srcBounds.bottom() &&
152 row <= srcBounds.bottom() + pixelPrecision - 1) {
154 row = srcBounds.bottom();
156 row &= alignmentMask;
161template <
class ProcessPolygon,
class ForwardTransform>
162void processGrid(ProcessPolygon &polygonOp, ForwardTransform &transformOp,
163 const QRect &srcBounds,
const int pixelPrecision)
176 QRect boundRect = areaToCopy.boundingRect().toAlignedRect();
177 if (boundRect.isEmpty())
return;
194 if (areaToCopy.containsPoint(QPoint(dstIt.
x(), dstIt.
y()), Qt::OddEvenFill)) {
212 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
213 operator() (srcPolygon, dstPolygon, dstPolygon);
216 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
217 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
218 if (boundRect.isEmpty())
return;
240 if (
interp.isValid(0.1)) {
241 int y = boundRect.top();
245 int newY = dstIt.
y();
252 QPointF srcPoint(dstIt.
x(), y);
254 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
256 interp.setX(srcPoint.x());
267 quint8* rawData = dstIt.
rawData();
276 QPointF srcPoint(dstIt.
x(), dstIt.
y());
278 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
291 QRect areaToCopy = *it;
314 const QPointF &srcImageOffset,
315 const QPointF &dstImageOffset)
325 QRect boundRect = areaToCopy.boundingRect().toAlignedRect();
327 if (boundRect.isEmpty())
return;
340 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
341 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
345 if (areaToCopy.containsPoint(srcPoint, Qt::OddEvenFill)) {
353 QPoint srcPointI = srcPoint.toPoint();
354 QPoint dstPointI =
dstPoint.toPoint();
390 QRect actualCopyArea = srcAreaUntranslated.intersected(dstAreaUntranslated);
396 int srcX = srcArea.left()*bytesPerPixel;
397 int dstX = dstArea.left()*bytesPerPixel;
399 for (
int srcY = srcArea.top(); srcY <= srcArea.bottom(); ++srcY) {
401 int dstY = dstArea.top() + srcY - srcArea.top();
402 const uchar *srcLine =
m_srcImage.constScanLine(srcY);
404 memcpy(dstLine + dstX, srcLine + srcX, srcArea.width()*bytesPerPixel);
409 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
410 this->
operator() (srcPolygon, dstPolygon, dstPolygon);
413 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
414 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
427 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
429 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
431 QPointF srcPoint(x, y);
432 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
434 interp.setX(srcPoint.x());
443 QPoint srcPointI = srcPoint.toPoint();
444 QPoint dstPointI =
dstPoint.toPoint();
454#ifdef DEBUG_PAINTING_POLYGONS
459 gc.setBrush(Qt::green);
462 gc.setBrush(Qt::blue);
474 QRect areaToCopy = *it;
513 const int tl = col + row * gridSize.width();
514 const int tr = tl + 1;
515 const int bl = tl + gridSize.width();
516 const int br = bl + 1;
530 cellPt.y() * gridSize.width();
537 if (pointOffsets.isEmpty()) {
538 pointOffsets << QPoint(0,0);
539 pointOffsets << QPoint(1,0);
540 pointOffsets << QPoint(1,1);
541 pointOffsets << QPoint(0,1);
544 return baseColRow + pointOffsets[index];
555 if (!QRectF(originalBoundsForGrid).intersects(currentBounds)) {
559 QPoint offsetB = originalBoundsForGrid.topLeft();
561 QPointF startPointB = currentBounds.topLeft() - offsetB;
562 QPoint startPointG = QPoint(startPointB.x()/pixelPrecision, startPointB.y()/pixelPrecision);
563 startPointG = QPoint(
kisBoundFast(0, startPointG.x(), gridSize.width()),
kisBoundFast(0, startPointG.y(), gridSize.height()));
565 QPointF endPointB = currentBounds.bottomRight() + QPoint(1, 1) - offsetB;
566 QPoint endPointG = QPoint(std::ceil(endPointB.x()/pixelPrecision), std::ceil(endPointB.y()/pixelPrecision));
567 QPoint endPointPotential = endPointG;
569 QPoint trueEndPoint = QPoint(
kisBoundFast(0, endPointPotential.x(), gridSize.width()),
kisBoundFast(0, endPointPotential.y(), gridSize.height()));
571 QPoint size = trueEndPoint - startPointG;
573 return QRect(startPointG, QSize(size.x(), size.y()));
577 if (subGrid.width() == 0 || subGrid.height() == 0) {
580 QPoint topLeft = subGrid.topLeft();
581 QPoint bottomRight = subGrid.topLeft() + QPoint(subGrid.width() - 1, subGrid.height() - 1);
584 int bottomRightIndex =
pointToIndex(bottomRight, gridSize);
586 topLeftIndex = qMax(0, qMin(topLeftIndex, originalPoints.length() - 1));
587 bottomRightIndex = qMax(0, qMin(bottomRightIndex, originalPoints.length() - 1));
589 QPointF topLeftReal = originalPoints[topLeftIndex];
590 QPointF bottomRightReal = originalPoints[bottomRightIndex];
591 QRectF cutOut = QRectF(topLeftReal, bottomRightReal);
607 QRectF top = QRectF(srcBounds.topLeft(), QPointF(srcBounds.right() + 1, topLeftReal.y()));
608 QRectF bottom = QRectF(QPointF(srcBounds.left(), bottomRightReal.y()), srcBounds.bottomRight() + QPointF(1, 1));
609 QRectF left = QRectF(QPointF(srcBounds.left(), cutOut.top()), QPointF(cutOut.left(), cutOut.bottom()));
610 QRectF right = QRectF(QPointF(cutOut.right(), cutOut.top()), QPointF(srcBounds.right() + 1, cutOut.bottom()));
612 for (
int i = 0; i < rects.length(); i++) {
613 if (!rects[i].isEmpty()) {
614 response << rects[i];
624template <
class IndexesOp>
636 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
637 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {
639 extensionPoints << ext;
642 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
643 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {
645 extensionPoints << ext;
648 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
649 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {
651 extensionPoints << ext;
654 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
655 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {
657 extensionPoints << ext;
660 if (extensionPoints.isEmpty()) {
662 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
663 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {
665 extensionPoints << ext;
668 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 &&
669 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) {
671 extensionPoints << ext;
674 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 &&
675 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) {
677 extensionPoints << ext;
680 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 &&
681 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) {
683 extensionPoints << ext;
687 if (extensionPoints.isEmpty()) {
691 int numResultPoints = 0;
692 *srcPoint = indexesOp.getSrcPointForce(cellPt);
696 QPointF near = transformedPoints[ext.
near];
697 QPointF far = transformedPoints[ext.
far];
699 QPointF nearSrc = originalPoints[ext.
near];
700 QPointF farSrc = originalPoints[ext.
far];
702 QPointF base1 = nearSrc - farSrc;
703 QPointF base2 = near - far;
717template <
class PolygonOp,
class IndexesOp>
721 int numExistingPoints,
722 PolygonOp &polygonOp,
723 IndexesOp &indexesOp,
728 if (numExistingPoints >= 4)
return false;
729 if (numExistingPoints == 0)
return true;
731 QPolygonF srcPolygon;
732 QPolygonF dstPolygon;
734 for (
int i = 0; i < 4; i++) {
735 const int index = polygonPoints[i];
738 srcPolygon << originalPoints[index];
739 dstPolygon << transformedPoints[index];
756 srcPolygon << srcPoint;
762 if (dstPolygon.size() == 4) {
763 QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));
766 for (
int i = 0; i < srcClipPolygon.size(); i++) {
767 const QPointF newPt = forwardTransform.
map(srcClipPolygon[i]);
768 srcClipPolygon[i] = newPt;
771 polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
778template <
class PolygonOp,
class IndexesOp>
782 int numExistingPoints,
783 PolygonOp &polygonOp,
784 IndexesOp &indexesOp,
793 Q_UNUSED(polygonPoints);
794 Q_UNUSED(originalPoints);
795 Q_UNUSED(transformedPoints);
810 int *numExistingPoints)
const {
812 *numExistingPoints = 4;
851 static const qreal
eps = 1e-5;
852 static const QPointF
p1(
eps, 0.0);
854 static const QPointF
p3(0.0,
eps);
861template <
class IndexesOp>
866template <
class IndexesOp>
869 QPoint startPoint = subgrid.topLeft();
870 QPoint endPoint = subgrid.bottomRight();
873 int polygonsChecked = 0;
875 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
876 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
877 int numExistingPoints = 0;
879 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
881 QPolygonF dstPolygon;
883 for (
int i = 0; i < polygonPoints.count(); i++) {
884 const int index = polygonPoints[i];
885 dstPolygon << transformedPoints[index];
903template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
907 IndexesOp &indexesOp,
908 const QSize &gridSize,
912 iterateThroughGrid<IncompletePolygonPolicy, PolygonOp, IndexesOp>(polygonOp, indexesOp, gridSize, originalPoints, transformedPoints, QRect(QPoint(0, 0), gridSize));
915template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
919 IndexesOp &indexesOp,
920 const QSize &gridSize,
926 QPoint startPoint = subGrid.topLeft();
927 QPoint endPoint = subGrid.bottomRight();
931 KIS_SAFE_ASSERT_RECOVER(startPoint.x() >= 0 && startPoint.y() >= 0 && endPoint.x() <= gridSize.width() - 1 && endPoint.y() <= gridSize.height() - 1) {
932 startPoint = QPoint(qMax(startPoint.x(), 0), qMax(startPoint.y(), 0));
933 endPoint = QPoint(qMin(endPoint.x(), gridSize.width() - 1), qMin(startPoint.y(), gridSize.height() - 1));
936 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
937 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
938 int numExistingPoints = 0;
940 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
943 tryProcessPolygon(col, row,
949 transformedPoints)) {
951 QPolygonF srcPolygon;
952 QPolygonF dstPolygon;
954 for (
int i = 0; i < 4; i++) {
955 const int index = polygonPoints[i];
956 srcPolygon << originalPoints[index];
957 dstPolygon << transformedPoints[index];
963 polygonOp(srcPolygon, dstPolygon);
968 polygonOp.finalize();
QPointF map(const QPointF &pt)
quint32 pixelSize() const
const KoColorSpace * colorSpace() const
KisRandomSubAccessorSP createRandomSubAccessor() const
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
void sampledOldRawData(quint8 *dst)
void moveTo(qreal x, qreal y)
static QVector< QRect >::iterator mergeSparseRects(QVector< QRect >::iterator beginIt, QVector< QRect >::iterator endIt)
merge a set of rectangles into a smaller set of bigger rectangles
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE int x() const
ALWAYS_INLINE const quint8 * oldRawData() const
ALWAYS_INLINE int y() const
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_ASSERT_RECOVER_NOOP(cond)
qreal interp(qreal r, qreal a, qreal b)
private functions
constexpr const T & kisBoundFast(const T &min, const T &val, const T &max)
bool isPolygonRect(const Polygon &poly, Difference tolerance)
bool isPolygonTrulyConvex(const QVector< T > &polygon)
QPointF transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)