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)
179#ifdef DEBUG_PAINTING_POLYGONS
181 QRect boundRect = areaToCopy;
192 QColor color = m_debugColor;
193 color.setHsl(
KisAlgebra2D::wrapValue(m_debugColor.hslHue() + 20, 0, 360), m_debugColor.hslSaturation(), m_debugColor.lightness());
205 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
206 operator() (srcPolygon, dstPolygon, dstPolygon);
209 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
210#ifdef DEBUG_PAINTING_POLYGONS
213 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
214 if (boundRect.isEmpty())
return;
221 QRect boundRect = dstPolygon.boundingRect().toAlignedRect();
237#ifdef DEBUG_PAINTING_POLYGONS
246 if (
interp.isValid(0.1)) {
247 int y = boundRect.top();
251 int newY = dstIt.
y();
258 QPointF srcPoint(dstIt.
x(), y);
260 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
262 interp.setX(srcPoint.x());
273 quint8* rawData = dstIt.
rawData();
275#ifdef DEBUG_PAINTING_POLYGONS
276 QColor color = m_debugColor;
277 color.setHsl(
KisAlgebra2D::wrapValue(m_debugColor.hslHue() + m_rectId, 0, 360), m_debugColor.hslSaturation(), qBound(0, m_debugColor.lightness() - 50 - pixelId, 100));
288 QPointF srcPoint(dstIt.
x(), dstIt.
y());
290 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
292#ifdef DEBUG_PAINTING_POLYGONS
293 QColor color = m_debugColor;
294 color.setHsl(
KisAlgebra2D::wrapValue(m_debugColor.hslHue() + m_rectId, 0, 360), m_debugColor.hslSaturation(), qBound(0, m_debugColor.lightness() + 50, 100));
307 QRect areaToCopy = *it;
325#ifdef DEBUG_PAINTING_POLYGONS
326 QColor m_debugColor {Qt::red};
328 inline void setDebugColor(QColor color) {
329 m_debugColor = color;
342 const QPointF &srcImageOffset,
343 const QPointF &dstImageOffset)
375 QRect actualCopyArea = srcAreaUntranslated.intersected(dstAreaUntranslated);
381 int srcX = srcArea.left()*bytesPerPixel;
382 int dstX = dstArea.left()*bytesPerPixel;
384 for (
int srcY = srcArea.top(); srcY <= srcArea.bottom(); ++srcY) {
386 int dstY = dstArea.top() + srcY - srcArea.top();
387 const uchar *srcLine =
m_srcImage.constScanLine(srcY);
389 memcpy(dstLine + dstX, srcLine + srcX, srcArea.width()*bytesPerPixel);
394 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
395 this->
operator() (srcPolygon, dstPolygon, dstPolygon);
398 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
399 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
406 QRect boundRect = dstPolygon.boundingRect().toAlignedRect();
417 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
419 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
421 QPointF srcPoint(x, y);
422 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
424 interp.setX(srcPoint.x());
433 QPoint srcPointI = srcPoint.toPoint();
434 QPoint dstPointI =
dstPoint.toPoint();
444#ifdef DEBUG_PAINTING_POLYGONS
449 gc.setBrush(Qt::green);
452 gc.setBrush(Qt::blue);
464 QRect areaToCopy = *it;
507 const int tl = col + row * gridSize.width();
508 const int tr = tl + 1;
509 const int bl = tl + gridSize.width();
510 const int br = bl + 1;
524 cellPt.y() * gridSize.width();
531 if (pointOffsets.isEmpty()) {
532 pointOffsets << QPoint(0,0);
533 pointOffsets << QPoint(1,0);
534 pointOffsets << QPoint(1,1);
535 pointOffsets << QPoint(0,1);
538 return baseColRow + pointOffsets[index];
549 if (!QRectF(originalBoundsForGrid).intersects(currentBounds)) {
553 QPointF imaginaryGridStartF = QPoint(originalBoundsForGrid.x()/pixelPrecision, originalBoundsForGrid.y()/pixelPrecision)*pixelPrecision;
555 QPointF startPointB = currentBounds.topLeft() - imaginaryGridStartF;
556 QPoint startPointG = QPoint(startPointB.x()/pixelPrecision, startPointB.y()/pixelPrecision);
557 startPointG = QPoint(
kisBoundFast(0, startPointG.x(), gridSize.width()),
kisBoundFast(0, startPointG.y(), gridSize.height()));
559 QPointF endPointB = currentBounds.bottomRight() + QPoint(1, 1) - imaginaryGridStartF;
560 QPoint endPointG = QPoint(std::ceil(endPointB.x()/pixelPrecision), std::ceil(endPointB.y()/pixelPrecision)) + QPoint(1, 1);
561 QPoint endPointPotential = endPointG;
563 QPoint trueEndPoint = QPoint(
kisBoundFast(0, endPointPotential.x(), gridSize.width()),
kisBoundFast(0, endPointPotential.y(), gridSize.height()));
565 QPoint size = trueEndPoint - startPointG;
567 return QRect(startPointG, QSize(size.x(), size.y()));
571 if (subGrid.width() == 0 || subGrid.height() == 0) {
574 QPoint topLeft = subGrid.topLeft();
575 QPoint bottomRight = subGrid.topLeft() + QPoint(subGrid.width() - 1, subGrid.height() - 1);
578 int bottomRightIndex =
pointToIndex(bottomRight, gridSize);
580 topLeftIndex = qMax(0, qMin(topLeftIndex, originalPoints.length() - 1));
581 bottomRightIndex = qMax(0, qMin(bottomRightIndex, originalPoints.length() - 1));
583 QPointF topLeftReal = originalPoints[topLeftIndex];
584 QPointF bottomRightReal = originalPoints[bottomRightIndex];
585 QRectF cutOut = QRectF(topLeftReal, bottomRightReal);
601 QRectF top = QRectF(srcBounds.topLeft(), QPointF(srcBounds.right() + 1, topLeftReal.y()));
602 QRectF bottom = QRectF(QPointF(srcBounds.left(), bottomRightReal.y() + 1), srcBounds.bottomRight() + QPointF(1, 1));
603 QRectF left = QRectF(QPointF(srcBounds.left(), cutOut.top()), QPointF(cutOut.left(), cutOut.bottom() + 1));
604 QRectF right = QRectF(QPointF(cutOut.right() + 1, cutOut.top()), QPointF(srcBounds.right() + 1, cutOut.bottom() + 1));
606 for (
int i = 0; i < rects.length(); i++) {
607 if (!rects[i].isEmpty()) {
608 response << rects[i];
618template <
class IndexesOp>
630 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
631 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {
633 extensionPoints << ext;
636 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
637 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {
639 extensionPoints << ext;
642 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
643 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {
645 extensionPoints << ext;
648 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
649 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {
651 extensionPoints << ext;
654 if (extensionPoints.isEmpty()) {
656 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
657 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {
659 extensionPoints << ext;
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;
681 if (extensionPoints.isEmpty()) {
685 int numResultPoints = 0;
686 *srcPoint = indexesOp.getSrcPointForce(cellPt);
690 QPointF near = transformedPoints[ext.
near];
691 QPointF far = transformedPoints[ext.
far];
693 QPointF nearSrc = originalPoints[ext.
near];
694 QPointF farSrc = originalPoints[ext.
far];
696 QPointF base1 = nearSrc - farSrc;
697 QPointF base2 = near - far;
711template <
class PolygonOp,
class IndexesOp>
715 int numExistingPoints,
716 PolygonOp &polygonOp,
717 IndexesOp &indexesOp,
722 if (numExistingPoints >= 4)
return false;
723 if (numExistingPoints == 0)
return true;
725 QPolygonF srcPolygon;
726 QPolygonF dstPolygon;
728 for (
int i = 0; i < 4; i++) {
729 const int index = polygonPoints[i];
732 srcPolygon << originalPoints[index];
733 dstPolygon << transformedPoints[index];
750 srcPolygon << srcPoint;
756 if (dstPolygon.size() == 4) {
757 QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));
760 for (
int i = 0; i < srcClipPolygon.size(); i++) {
761 const QPointF newPt = forwardTransform.
map(srcClipPolygon[i]);
762 srcClipPolygon[i] = newPt;
765 polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
772template <
class PolygonOp,
class IndexesOp>
776 int numExistingPoints,
777 PolygonOp &polygonOp,
778 IndexesOp &indexesOp,
787 Q_UNUSED(polygonPoints);
788 Q_UNUSED(originalPoints);
789 Q_UNUSED(transformedPoints);
804 int *numExistingPoints)
const {
806 *numExistingPoints = 4;
845 static const qreal
eps = 1e-5;
846 static const QPointF
p1(
eps, 0.0);
848 static const QPointF
p3(0.0,
eps);
855template <
class IndexesOp>
860template <
class IndexesOp>
863 QPoint startPoint = subgrid.topLeft();
864 QPoint endPoint = subgrid.bottomRight();
866 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
867 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
868 int numExistingPoints = 0;
870 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
872 QPolygonF dstPolygon;
874 for (
int i = 0; i < polygonPoints.count(); i++) {
875 const int index = polygonPoints[i];
876 dstPolygon << transformedPoints[index];
894template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
898 IndexesOp &indexesOp,
899 const QSize &gridSize,
903 iterateThroughGrid<IncompletePolygonPolicy, PolygonOp, IndexesOp>(polygonOp, indexesOp, gridSize, originalPoints, transformedPoints, QRect(QPoint(0, 0), gridSize));
906template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
910 IndexesOp &indexesOp,
911 const QSize &gridSize,
917 QPoint startPoint = subGrid.topLeft();
918 QPoint endPoint = subGrid.bottomRight();
922 KIS_SAFE_ASSERT_RECOVER(startPoint.x() >= 0 && startPoint.y() >= 0 && endPoint.x() <= gridSize.width() - 1 && endPoint.y() <= gridSize.height() - 1) {
923 startPoint = QPoint(qMax(startPoint.x(), 0), qMax(startPoint.y(), 0));
924 endPoint = QPoint(qMin(endPoint.x(), gridSize.width() - 1), qMin(startPoint.y(), gridSize.height() - 1));
927 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
928 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
929 int numExistingPoints = 0;
931 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
934 tryProcessPolygon(col, row,
940 transformedPoints)) {
942 QPolygonF srcPolygon;
943 QPolygonF dstPolygon;
945 for (
int i = 0; i < 4; i++) {
946 const int index = polygonPoints[i];
947 srcPolygon << originalPoints[index];
948 dstPolygon << transformedPoints[index];
954 polygonOp(srcPolygon, dstPolygon);
959 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)
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)