7#ifndef __KIS_GRID_INTERPOLATION_TOOLS_H
8#define __KIS_GRID_INTERPOLATION_TOOLS_H
23#ifdef DEBUG_PAINTING_POLYGONS
31 const int alignmentMask = ~(pixelPrecision - 1);
33 int alignedStart = (start + pixelPrecision - 1) & alignmentMask;
34 int alignedEnd = end & alignmentMask;
38 if (alignedEnd > alignedStart) {
39 size = (alignedEnd - alignedStart) / pixelPrecision + 1;
40 size += alignedStart != start;
41 size += alignedEnd != end;
43 size = 2 + (end - start >= pixelPrecision);
49inline QSize
calcGridSize(
const QRect &srcBounds,
const int pixelPrecision) {
54template <
class ProcessPolygon,
class ForwardTransform>
57 CellOp(ProcessPolygon &_polygonOp, ForwardTransform &_transformOp)
64 int prevCol,
int prevRow,
65 int colIndex,
int rowIndex) {
70 if (rowIndex >= 1 && colIndex >= 1) {
73 srcPolygon << QPointF(prevCol, prevRow);
74 srcPolygon << QPointF(col, prevRow);
75 srcPolygon << QPointF(col, row);
76 srcPolygon << QPointF(prevCol, row);
105template <
class ProcessCell>
107 const QRect &srcBounds,
108 const int pixelPrecision)
110 if (srcBounds.isEmpty())
return;
112 const int alignmentMask = ~(pixelPrecision - 1);
114 int prevRow = std::numeric_limits<int>::max();
115 int prevCol = std::numeric_limits<int>::max();
120 for (
int row = srcBounds.top(); row <= srcBounds.bottom();) {
121 for (
int col = srcBounds.left(); col <= srcBounds.right();) {
123 cellOp.processPoint(col, row,
128 col += pixelPrecision;
131 if (col > srcBounds.right() &&
132 col <= srcBounds.right() + pixelPrecision - 1) {
134 col = srcBounds.right();
136 col &= alignmentMask;
144 row += pixelPrecision;
147 if (row > srcBounds.bottom() &&
148 row <= srcBounds.bottom() + pixelPrecision - 1) {
150 row = srcBounds.bottom();
152 row &= alignmentMask;
157template <
class ProcessPolygon,
class ForwardTransform>
158void processGrid(ProcessPolygon &polygonOp, ForwardTransform &transformOp,
159 const QRect &srcBounds,
const int pixelPrecision)
170 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
171 this->
operator() (srcPolygon, dstPolygon, dstPolygon);
174 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
175 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
176 if (boundRect.isEmpty())
return;
188 if (
interp.isValid(0.1)) {
189 int y = boundRect.top();
193 int newY = dstIt.
y();
200 QPointF srcPoint(dstIt.
x(), y);
202 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
204 interp.setX(srcPoint.x());
223 QPointF srcPoint(dstIt.
x(), dstIt.
y());
225 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
240 const QPointF &srcImageOffset,
241 const QPointF &dstImageOffset)
250 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon) {
251 this->
operator() (srcPolygon, dstPolygon, dstPolygon);
254 void operator() (
const QPolygonF &srcPolygon,
const QPolygonF &dstPolygon,
const QPolygonF &clipDstPolygon) {
255 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
258 for (
int y = boundRect.top(); y <= boundRect.bottom(); y++) {
260 for (
int x = boundRect.left(); x <= boundRect.right(); x++) {
262 QPointF srcPoint(x, y);
263 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
265 interp.setX(srcPoint.x());
274 QPoint srcPointI = srcPoint.toPoint();
275 QPoint dstPointI =
dstPoint.toPoint();
285#ifdef DEBUG_PAINTING_POLYGONS
290 gc.setBrush(Qt::green);
293 gc.setBrush(Qt::blue);
321 const int tl = col + row * gridSize.width();
322 const int tr = tl + 1;
323 const int bl = tl + gridSize.width();
324 const int br = bl + 1;
338 cellPt.y() * gridSize.width();
345 if (pointOffsets.isEmpty()) {
346 pointOffsets << QPoint(0,0);
347 pointOffsets << QPoint(1,0);
348 pointOffsets << QPoint(1,1);
349 pointOffsets << QPoint(0,1);
352 return baseColRow + pointOffsets[index];
361template <
class IndexesOp>
373 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
374 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {
376 extensionPoints << ext;
379 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
380 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {
382 extensionPoints << ext;
385 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
386 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {
388 extensionPoints << ext;
391 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
392 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {
394 extensionPoints << ext;
397 if (extensionPoints.isEmpty()) {
399 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
400 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {
402 extensionPoints << ext;
405 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 &&
406 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) {
408 extensionPoints << ext;
411 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 &&
412 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) {
414 extensionPoints << ext;
417 if ((ext.
near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 &&
418 (ext.
far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) {
420 extensionPoints << ext;
424 if (extensionPoints.isEmpty()) {
428 int numResultPoints = 0;
429 *srcPoint = indexesOp.getSrcPointForce(cellPt);
433 QPointF near = transformedPoints[ext.
near];
434 QPointF far = transformedPoints[ext.
far];
436 QPointF nearSrc = originalPoints[ext.
near];
437 QPointF farSrc = originalPoints[ext.
far];
439 QPointF base1 = nearSrc - farSrc;
440 QPointF base2 = near - far;
454template <
class PolygonOp,
class IndexesOp>
458 int numExistingPoints,
459 PolygonOp &polygonOp,
460 IndexesOp &indexesOp,
465 if (numExistingPoints >= 4)
return false;
466 if (numExistingPoints == 0)
return true;
468 QPolygonF srcPolygon;
469 QPolygonF dstPolygon;
471 for (
int i = 0; i < 4; i++) {
472 const int index = polygonPoints[i];
475 srcPolygon << originalPoints[index];
476 dstPolygon << transformedPoints[index];
493 srcPolygon << srcPoint;
499 if (dstPolygon.size() == 4) {
500 QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));
503 for (
int i = 0; i < srcClipPolygon.size(); i++) {
504 const QPointF newPt = forwardTransform.
map(srcClipPolygon[i]);
505 srcClipPolygon[i] = newPt;
508 polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
515template <
class PolygonOp,
class IndexesOp>
519 int numExistingPoints,
520 PolygonOp &polygonOp,
521 IndexesOp &indexesOp,
530 Q_UNUSED(polygonPoints);
531 Q_UNUSED(originalPoints);
532 Q_UNUSED(transformedPoints);
547 int *numExistingPoints)
const {
549 *numExistingPoints = 4;
588 static const qreal
eps = 1e-5;
589 static const QPointF
p1(
eps, 0.0);
591 static const QPointF
p3(0.0,
eps);
598template <
template <
class PolygonOp,
class IndexesOp>
class IncompletePolygonPolicy,
602 IndexesOp &indexesOp,
603 const QSize &gridSize,
609 for (
int row = 0; row < gridSize.height() - 1; row++) {
610 for (
int col = 0; col < gridSize.width() - 1; col++) {
611 int numExistingPoints = 0;
613 polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);
616 tryProcessPolygon(col, row,
622 transformedPoints)) {
624 QPolygonF srcPolygon;
625 QPolygonF dstPolygon;
627 for (
int i = 0; i < 4; i++) {
628 const int index = polygonPoints[i];
629 srcPolygon << originalPoints[index];
630 dstPolygon << transformedPoints[index];
636 polygonOp(srcPolygon, dstPolygon);
QPointF map(const QPointF &pt)
KisRandomSubAccessorSP createRandomSubAccessor() const
void sampledOldRawData(quint8 *dst)
void moveTo(qreal x, qreal y)
ALWAYS_INLINE quint8 * rawData()
ALWAYS_INLINE int x() const
ALWAYS_INLINE int y() const
#define KIS_ASSERT_RECOVER_NOOP(cond)
qreal interp(qreal r, qreal a, qreal b)
private functions
QPointF transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2)