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)
189#ifdef DEBUG_PAINTING_POLYGONS
191 QRect boundRect = areaToCopy;
202 QColor color = m_debugColor;
203 color.setHsl(
KisAlgebra2D::wrapValue(m_debugColor.hslHue() + 20, 0, 360), m_debugColor.hslSaturation(), m_debugColor.lightness());
215 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
216 operator() (srcPolygon, dstPolygon, dstPolygon);
219 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
220#ifdef DEBUG_PAINTING_POLYGONS
223 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
224 if (boundRect.isEmpty())
return;
231 QRect boundRect = dstPolygon.boundingRect().toAlignedRect();
247#ifdef DEBUG_PAINTING_POLYGONS
256 if (
interp.isValid(0.1)) {
257 int y = boundRect.top();
261 int newY = dstIt.
y();
268 QPointF srcPoint(dstIt.
x(), y);
270 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
272 interp.setX(srcPoint.x());
283 quint8* rawData = dstIt.
rawData();
285#ifdef DEBUG_PAINTING_POLYGONS
286 QColor color = m_debugColor;
287 color.setHsl(
KisAlgebra2D::wrapValue(m_debugColor.hslHue() + m_rectId, 0, 360), m_debugColor.hslSaturation(), qBound(0, m_debugColor.lightness() - 50 - pixelId, 100));
298 QPointF srcPoint(dstIt.
x(), dstIt.
y());
300 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
302#ifdef DEBUG_PAINTING_POLYGONS
303 QColor color = m_debugColor;
304 color.setHsl(
KisAlgebra2D::wrapValue(m_debugColor.hslHue() + m_rectId, 0, 360), m_debugColor.hslSaturation(), qBound(0, m_debugColor.lightness() + 50, 100));
317 QRect areaToCopy = *it;
341#ifdef DEBUG_PAINTING_POLYGONS
342 QColor m_debugColor {Qt::red};
344 inline void setDebugColor(QColor color) {
345 m_debugColor = color;
358 const QPointF &srcImageOffset,
359 const QPointF &dstImageOffset)
401 QRect actualCopyArea = srcAreaUntranslated.intersected(dstAreaUntranslated);
407 int srcX = srcArea.left()*bytesPerPixel;
408 int dstX = dstArea.left()*bytesPerPixel;
410 for (
int srcY = srcArea.top(); srcY <= srcArea.bottom(); ++srcY) {
412 int dstY = dstArea.top() + srcY - srcArea.top();
413 const uchar *srcLine =
m_srcImage.constScanLine(srcY);
415 memcpy(dstLine + dstX, srcLine + srcX, srcArea.width()*bytesPerPixel);
420 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
421 this->
operator() (srcPolygon, dstPolygon, dstPolygon);
424 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
425 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
432 QRect boundRect = dstPolygon.boundingRect().toAlignedRect();
443 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
445 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
447 QPointF srcPoint(x, y);
448 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
450 interp.setX(srcPoint.x());
459 QPoint srcPointI = srcPoint.toPoint();
460 QPoint dstPointI =
dstPoint.toPoint();
470#ifdef DEBUG_PAINTING_POLYGONS
475 gc.setBrush(Qt::green);
478 gc.setBrush(Qt::blue);
490 QRect areaToCopy = *it;
538 const int tl = col + row * gridSize.width();
539 const int tr = tl + 1;
540 const int bl = tl + gridSize.width();
541 const int br = bl + 1;
555 cellPt.y() * gridSize.width();
562 if (pointOffsets.isEmpty()) {
563 pointOffsets << QPoint(0,0);
564 pointOffsets << QPoint(1,0);
565 pointOffsets << QPoint(1,1);
566 pointOffsets << QPoint(0,1);
569 return baseColRow + pointOffsets[index];
580 if (!QRectF(originalBoundsForGrid).intersects(currentBounds)) {
584 QPointF imaginaryGridStartF = QPoint(originalBoundsForGrid.x()/pixelPrecision, originalBoundsForGrid.y()/pixelPrecision)*pixelPrecision;
586 QPointF startPointB = currentBounds.topLeft() - imaginaryGridStartF;
587 QPoint startPointG = QPoint(startPointB.x()/pixelPrecision, startPointB.y()/pixelPrecision);
588 startPointG = QPoint(
kisBoundFast(0, startPointG.x(), gridSize.width()),
kisBoundFast(0, startPointG.y(), gridSize.height()));
590 QPointF endPointB = currentBounds.bottomRight() + QPoint(1, 1) - imaginaryGridStartF;
591 QPoint endPointG = QPoint(std::ceil(endPointB.x()/pixelPrecision), std::ceil(endPointB.y()/pixelPrecision)) + QPoint(1, 1);
592 QPoint endPointPotential = endPointG;
594 QPoint trueEndPoint = QPoint(
kisBoundFast(0, endPointPotential.x(), gridSize.width()),
kisBoundFast(0, endPointPotential.y(), gridSize.height()));
596 QPoint size = trueEndPoint - startPointG;
598 return QRect(startPointG, QSize(size.x(), size.y()));
602 if (subGrid.width() == 0 || subGrid.height() == 0) {
605 QPoint topLeft = subGrid.topLeft();
606 QPoint bottomRight = subGrid.topLeft() + QPoint(subGrid.width() - 1, subGrid.height() - 1);
609 int bottomRightIndex =
pointToIndex(bottomRight, gridSize);
611 topLeftIndex = qMax(0, qMin(topLeftIndex, originalPoints.length() - 1));
612 bottomRightIndex = qMax(0, qMin(bottomRightIndex, originalPoints.length() - 1));
614 QPointF topLeftReal = originalPoints[topLeftIndex];
615 QPointF bottomRightReal = originalPoints[bottomRightIndex];
616 QRectF cutOut = QRectF(topLeftReal, bottomRightReal);
632 QRectF top = QRectF(srcBounds.topLeft(), QPointF(srcBounds.right() + 1, topLeftReal.y()));
633 QRectF bottom = QRectF(QPointF(srcBounds.left(), bottomRightReal.y() + 1), srcBounds.bottomRight() + QPointF(1, 1));
634 QRectF left = QRectF(QPointF(srcBounds.left(), cutOut.top()), QPointF(cutOut.left(), cutOut.bottom() + 1));
635 QRectF right = QRectF(QPointF(cutOut.right() + 1, cutOut.top()), QPointF(srcBounds.right() + 1, cutOut.bottom() + 1));
637 for (
int i = 0; i < rects.length(); i++) {
638 if (!rects[i].isEmpty()) {
639 response << rects[i];
649template <
class IndexesOp>
661 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
662 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {
664 extensionPoints << ext;
667 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
668 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {
670 extensionPoints << ext;
673 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
674 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {
676 extensionPoints << ext;
679 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
680 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {
682 extensionPoints << ext;
685 if (extensionPoints.isEmpty()) {
687 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
688 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {
690 extensionPoints << ext;
693 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 &&
694 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) {
696 extensionPoints << ext;
699 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 &&
700 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) {
702 extensionPoints << ext;
705 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 &&
706 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) {
708 extensionPoints << ext;
712 if (extensionPoints.isEmpty()) {
716 int numResultPoints = 0;
717 *srcPoint = indexesOp.getSrcPointForce(cellPt);
721 QPointF near = transformedPoints[ext.
near];
722 QPointF far = transformedPoints[ext.
far];
724 QPointF nearSrc = originalPoints[ext.
near];
725 QPointF farSrc = originalPoints[ext.
far];
727 QPointF base1 = nearSrc - farSrc;
728 QPointF base2 = near - far;
742template <
class PolygonOp,
class IndexesOp>
746 int numExistingPoints,
747 PolygonOp &polygonOp,
748 IndexesOp &indexesOp,
753 if (numExistingPoints >= 4)
return false;
754 if (numExistingPoints == 0)
return true;
756 QPolygonF srcPolygon;
757 QPolygonF dstPolygon;
759 for (
int i = 0; i < 4; i++) {
760 const int index = polygonPoints[i];
763 srcPolygon << originalPoints[index];
764 dstPolygon << transformedPoints[index];
781 srcPolygon << srcPoint;
787 if (dstPolygon.size() == 4) {
788 QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));
791 for (
int i = 0; i < srcClipPolygon.size(); i++) {
792 const QPointF newPt = forwardTransform.
map(srcClipPolygon[i]);
793 srcClipPolygon[i] = newPt;
796 polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
803template <
class PolygonOp,
class IndexesOp>
807 int numExistingPoints,
808 PolygonOp &polygonOp,
809 IndexesOp &indexesOp,
818 Q_UNUSED(polygonPoints);
819 Q_UNUSED(originalPoints);
820 Q_UNUSED(transformedPoints);
835 int *numExistingPoints)
const {
837 *numExistingPoints = 4;
876 static const qreal
eps = 1e-5;
877 static const QPointF
p1(
eps, 0.0);
879 static const QPointF
p3(0.0,
eps);
886template <
class IndexesOp>
891template <
class IndexesOp>
894 QPoint startPoint = subgrid.topLeft();
895 QPoint endPoint = subgrid.bottomRight();
897 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
898 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
899 int numExistingPoints = 0;
901 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
903 QPolygonF dstPolygon;
905 for (
int i = 0; i < polygonPoints.count(); i++) {
906 const int index = polygonPoints[i];
907 dstPolygon << transformedPoints[index];
925template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
929 IndexesOp &indexesOp,
930 const QSize &gridSize,
934 iterateThroughGrid<IncompletePolygonPolicy, PolygonOp, IndexesOp>(polygonOp, indexesOp, gridSize, originalPoints, transformedPoints, QRect(QPoint(0, 0), gridSize));
937template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
941 IndexesOp &indexesOp,
942 const QSize &gridSize,
948 QPoint startPoint = subGrid.topLeft();
949 QPoint endPoint = subGrid.bottomRight();
953 KIS_SAFE_ASSERT_RECOVER(startPoint.x() >= 0 && startPoint.y() >= 0 && endPoint.x() <= gridSize.width() - 1 && endPoint.y() <= gridSize.height() - 1) {
954 startPoint = QPoint(qMax(startPoint.x(), 0), qMax(startPoint.y(), 0));
955 endPoint = QPoint(qMin(endPoint.x(), gridSize.width() - 1), qMin(startPoint.y(), gridSize.height() - 1));
958 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
959 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
960 int numExistingPoints = 0;
962 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
965 tryProcessPolygon(col, row,
971 transformedPoints)) {
973 QPolygonF srcPolygon;
974 QPolygonF dstPolygon;
976 for (
int i = 0; i < 4; i++) {
977 const int index = polygonPoints[i];
978 srcPolygon << originalPoints[index];
979 dstPolygon << transformedPoints[index];
985 polygonOp(srcPolygon, dstPolygon);
990 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
virtual void fromQColor(const QColor &color, quint8 *dst) const =0
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_ASSERT_RECOVER_NOOP(cond)
#define KIS_SAFE_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 isPolygonPixelAlignedRect(const Polygon &poly, Difference tolerance)
T wrapValue(T value, T wrapBounds)
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)