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)
170 return QPointF(x + 0.5, y + 0.5);
174 return QPointF(
p.x() + 0.5,
p.y() + 0.5);
178 return QPointF(
p.x() + 0.5,
p.y() + 0.5);
188 QRect boundRect = areaToCopy.boundingRect().toAlignedRect();
189 if (boundRect.isEmpty())
return;
193#ifdef DEBUG_PAINTING_POLYGONS
210 if (areaToCopy.containsPoint(
middlePoint(dstIt.
x(), dstIt.
y()), Qt::OddEvenFill)) {
212#ifdef DEBUG_PAINTING_POLYGONS
224#ifdef DEBUG_PAINTING_POLYGONS
235 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
236 operator() (srcPolygon, dstPolygon, dstPolygon);
239 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
240 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
241 if (boundRect.isEmpty())
return;
257#ifdef DEBUG_PAINTING_POLYGONS
266 if (
interp.isValid(0.1)) {
267 int y = boundRect.top();
271 int newY = dstIt.
y();
278 QPointF srcPoint(dstIt.
x(), y);
280 if (clipDstPolygon.containsPoint(
middlePoint(srcPoint), Qt::OddEvenFill)) {
282 interp.setX(srcPoint.x());
293 quint8* rawData = dstIt.
rawData();
295#ifdef DEBUG_PAINTING_POLYGONS
296 QColor color = m_debugColor;
297 color.setHsl(m_debugColor.hslHue(), m_debugColor.hslSaturation(), qMin(m_debugColor.lightness() - 50 - pixelId, 100));
308 QPointF srcPoint(dstIt.
x(), dstIt.
y());
310 if (clipDstPolygon.containsPoint(
middlePoint(srcPoint), Qt::OddEvenFill)) {
312#ifdef DEBUG_PAINTING_POLYGONS
313 QColor color = m_debugColor;
314 color.setHsl(m_debugColor.hslHue(), m_debugColor.hslSaturation(), m_debugColor.lightness() + 50);
328 QRect areaToCopy = *it;
342#ifdef DEBUG_PAINTING_POLYGONS
344 inline void setDebugColor(QColor color) {
345 m_debugColor = color;
358 const QPointF &srcImageOffset,
359 const QPointF &dstImageOffset)
369 QRect boundRect = areaToCopy.boundingRect().toAlignedRect();
371 if (boundRect.isEmpty())
return;
384 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
385 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
389 if (areaToCopy.containsPoint(
middlePoint(srcPoint), Qt::OddEvenFill)) {
397 QPoint srcPointI = srcPoint.toPoint();
398 QPoint dstPointI =
dstPoint.toPoint();
434 QRect actualCopyArea = srcAreaUntranslated.intersected(dstAreaUntranslated);
440 int srcX = srcArea.left()*bytesPerPixel;
441 int dstX = dstArea.left()*bytesPerPixel;
443 for (
int srcY = srcArea.top(); srcY <= srcArea.bottom(); ++srcY) {
445 int dstY = dstArea.top() + srcY - srcArea.top();
446 const uchar *srcLine =
m_srcImage.constScanLine(srcY);
448 memcpy(dstLine + dstX, srcLine + srcX, srcArea.width()*bytesPerPixel);
453 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
454 this->
operator() (srcPolygon, dstPolygon, dstPolygon);
457 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
458 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
471 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
473 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
475 QPointF srcPoint(x, y);
476 if (clipDstPolygon.containsPoint(
middlePoint(srcPoint), Qt::OddEvenFill)) {
478 interp.setX(srcPoint.x());
487 QPoint srcPointI = srcPoint.toPoint();
488 QPoint dstPointI =
dstPoint.toPoint();
498#ifdef DEBUG_PAINTING_POLYGONS
503 gc.setBrush(Qt::green);
506 gc.setBrush(Qt::blue);
518 QRect areaToCopy = *it;
557 const int tl = col + row * gridSize.width();
558 const int tr = tl + 1;
559 const int bl = tl + gridSize.width();
560 const int br = bl + 1;
574 cellPt.y() * gridSize.width();
581 if (pointOffsets.isEmpty()) {
582 pointOffsets << QPoint(0,0);
583 pointOffsets << QPoint(1,0);
584 pointOffsets << QPoint(1,1);
585 pointOffsets << QPoint(0,1);
588 return baseColRow + pointOffsets[index];
599 if (!QRectF(originalBoundsForGrid).intersects(currentBounds)) {
603 QPoint offsetB = originalBoundsForGrid.topLeft();
605 QPointF startPointB = currentBounds.topLeft() - offsetB;
606 QPoint startPointG = QPoint(startPointB.x()/pixelPrecision, startPointB.y()/pixelPrecision);
607 startPointG = QPoint(
kisBoundFast(0, startPointG.x(), gridSize.width()),
kisBoundFast(0, startPointG.y(), gridSize.height()));
609 QPointF endPointB = currentBounds.bottomRight() + QPoint(1, 1) - offsetB;
610 QPoint endPointG = QPoint(std::ceil(endPointB.x()/pixelPrecision), std::ceil(endPointB.y()/pixelPrecision));
611 QPoint endPointPotential = endPointG;
613 QPoint trueEndPoint = QPoint(
kisBoundFast(0, endPointPotential.x(), gridSize.width()),
kisBoundFast(0, endPointPotential.y(), gridSize.height()));
615 QPoint size = trueEndPoint - startPointG;
617 return QRect(startPointG, QSize(size.x(), size.y()));
621 if (subGrid.width() == 0 || subGrid.height() == 0) {
624 QPoint topLeft = subGrid.topLeft();
625 QPoint bottomRight = subGrid.topLeft() + QPoint(subGrid.width() - 1, subGrid.height() - 1);
628 int bottomRightIndex =
pointToIndex(bottomRight, gridSize);
630 topLeftIndex = qMax(0, qMin(topLeftIndex, originalPoints.length() - 1));
631 bottomRightIndex = qMax(0, qMin(bottomRightIndex, originalPoints.length() - 1));
633 QPointF topLeftReal = originalPoints[topLeftIndex];
634 QPointF bottomRightReal = originalPoints[bottomRightIndex];
635 QRectF cutOut = QRectF(topLeftReal, bottomRightReal);
651 QRectF top = QRectF(srcBounds.topLeft(), QPointF(srcBounds.right() + 1, topLeftReal.y()));
652 QRectF bottom = QRectF(QPointF(srcBounds.left(), bottomRightReal.y()), srcBounds.bottomRight() + QPointF(1, 1));
653 QRectF left = QRectF(QPointF(srcBounds.left(), cutOut.top()), QPointF(cutOut.left(), cutOut.bottom()));
654 QRectF right = QRectF(QPointF(cutOut.right(), cutOut.top()), QPointF(srcBounds.right() + 1, cutOut.bottom()));
656 for (
int i = 0; i < rects.length(); i++) {
657 if (!rects[i].isEmpty()) {
658 response << rects[i];
668template <
class IndexesOp>
680 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
681 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {
683 extensionPoints << ext;
686 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
687 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {
689 extensionPoints << ext;
692 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
693 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {
695 extensionPoints << ext;
698 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
699 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {
701 extensionPoints << ext;
704 if (extensionPoints.isEmpty()) {
706 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
707 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {
709 extensionPoints << ext;
712 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 &&
713 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) {
715 extensionPoints << ext;
718 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 &&
719 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) {
721 extensionPoints << ext;
724 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 &&
725 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) {
727 extensionPoints << ext;
731 if (extensionPoints.isEmpty()) {
735 int numResultPoints = 0;
736 *srcPoint = indexesOp.getSrcPointForce(cellPt);
740 QPointF near = transformedPoints[ext.
near];
741 QPointF far = transformedPoints[ext.
far];
743 QPointF nearSrc = originalPoints[ext.
near];
744 QPointF farSrc = originalPoints[ext.
far];
746 QPointF base1 = nearSrc - farSrc;
747 QPointF base2 = near - far;
761template <
class PolygonOp,
class IndexesOp>
765 int numExistingPoints,
766 PolygonOp &polygonOp,
767 IndexesOp &indexesOp,
772 if (numExistingPoints >= 4)
return false;
773 if (numExistingPoints == 0)
return true;
775 QPolygonF srcPolygon;
776 QPolygonF dstPolygon;
778 for (
int i = 0; i < 4; i++) {
779 const int index = polygonPoints[i];
782 srcPolygon << originalPoints[index];
783 dstPolygon << transformedPoints[index];
800 srcPolygon << srcPoint;
806 if (dstPolygon.size() == 4) {
807 QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));
810 for (
int i = 0; i < srcClipPolygon.size(); i++) {
811 const QPointF newPt = forwardTransform.
map(srcClipPolygon[i]);
812 srcClipPolygon[i] = newPt;
815 polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
822template <
class PolygonOp,
class IndexesOp>
826 int numExistingPoints,
827 PolygonOp &polygonOp,
828 IndexesOp &indexesOp,
837 Q_UNUSED(polygonPoints);
838 Q_UNUSED(originalPoints);
839 Q_UNUSED(transformedPoints);
854 int *numExistingPoints)
const {
856 *numExistingPoints = 4;
895 static const qreal
eps = 1e-5;
896 static const QPointF
p1(
eps, 0.0);
898 static const QPointF
p3(0.0,
eps);
905template <
class IndexesOp>
910template <
class IndexesOp>
913 QPoint startPoint = subgrid.topLeft();
914 QPoint endPoint = subgrid.bottomRight();
917 int polygonsChecked = 0;
919 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
920 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
921 int numExistingPoints = 0;
923 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
925 QPolygonF dstPolygon;
927 for (
int i = 0; i < polygonPoints.count(); i++) {
928 const int index = polygonPoints[i];
929 dstPolygon << transformedPoints[index];
947template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
951 IndexesOp &indexesOp,
952 const QSize &gridSize,
956 iterateThroughGrid<IncompletePolygonPolicy, PolygonOp, IndexesOp>(polygonOp, indexesOp, gridSize, originalPoints, transformedPoints, QRect(QPoint(0, 0), gridSize));
959template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
963 IndexesOp &indexesOp,
964 const QSize &gridSize,
970 QPoint startPoint = subGrid.topLeft();
971 QPoint endPoint = subGrid.bottomRight();
975 KIS_SAFE_ASSERT_RECOVER(startPoint.x() >= 0 && startPoint.y() >= 0 && endPoint.x() <= gridSize.width() - 1 && endPoint.y() <= gridSize.height() - 1) {
976 startPoint = QPoint(qMax(startPoint.x(), 0), qMax(startPoint.y(), 0));
977 endPoint = QPoint(qMin(endPoint.x(), gridSize.width() - 1), qMin(startPoint.y(), gridSize.height() - 1));
980 for (
int row = startPoint.y(); row < endPoint.y(); row++) {
981 for (
int col = startPoint.x(); col < endPoint.x(); col++) {
982 int numExistingPoints = 0;
984 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
987 tryProcessPolygon(col, row,
993 transformedPoints)) {
995 QPolygonF srcPolygon;
996 QPolygonF dstPolygon;
998 for (
int i = 0; i < 4; i++) {
999 const int index = polygonPoints[i];
1000 srcPolygon << originalPoints[index];
1001 dstPolygon << transformedPoints[index];
1007 polygonOp(srcPolygon, dstPolygon);
1012 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 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)