kis_cage_transform_worker.cpp 11.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 *  Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kis_cage_transform_worker.h"

#include "kis_grid_interpolation_tools.h"
#include "kis_green_coordinates_math.h"

24 25
#include <QPainter>

26 27 28
#include "KoColor.h"
#include "kis_selection.h"
#include "kis_painter.h"
Michael Abrahams's avatar
Michael Abrahams committed
29
#include "kis_image.h"
30

Boudewijn Rempt's avatar
Boudewijn Rempt committed
31 32
#ifdef Q_OS_WIN
#include <float.h>
33
#ifndef __MINGW32__
Boudewijn Rempt's avatar
Boudewijn Rempt committed
34 35
#define isnan _isnan
#endif
36
#endif
37

38
struct Q_DECL_HIDDEN KisCageTransformWorker::Private
39 40 41 42 43 44 45 46 47 48 49 50 51
{
    Private(KisPaintDeviceSP _dev,
            const QVector<QPointF> &_origCage,
            KoUpdater *_progress,
            int _pixelPrecision)
        : dev(_dev),
          origCage(_origCage),
          progress(_progress),
          pixelPrecision(_pixelPrecision)
    {
    }

    KisPaintDeviceSP dev;
52 53 54 55

    QImage srcImage;
    QPointF srcImageOffset;

56 57 58 59 60 61 62 63
    QVector<QPointF> origCage;
    QVector<QPointF> transfCage;
    KoUpdater *progress;
    int pixelPrecision;

    QVector<int> allToValidPointsMap;
    QVector<QPointF> validPoints;

64 65 66 67 68 69
    /**
     * Contains all points fo the grid including non-defined
     * points (the ones which are placed outside the cage).
     */
    QVector<QPointF> allSrcPoints;

70 71 72
    KisGreenCoordinatesMath cage;

    QSize gridSize;
73

74 75 76 77 78
    bool isGridEmpty() const {
        return allSrcPoints.isEmpty();
    }


79 80
    QVector<QPointF> calculateTransformedPoints();

81 82 83 84 85
    inline QVector<int> calculateMappedIndexes(int col, int row,
                                               int *numExistingPoints);

    int tryGetValidIndex(const QPoint &cellPt);

86
    struct MapIndexesOp;
87 88 89 90 91 92 93 94 95 96
};

KisCageTransformWorker::KisCageTransformWorker(KisPaintDeviceSP dev,
                                               const QVector<QPointF> &origCage,
                                               KoUpdater *progress,
                                               int pixelPrecision)
    : m_d(new Private(dev, origCage, progress, pixelPrecision))
{
}

97 98 99 100 101 102 103 104 105 106 107
KisCageTransformWorker::KisCageTransformWorker(const QImage &srcImage,
                                               const QPointF &srcImageOffset,
                                               const QVector<QPointF> &origCage,
                                               KoUpdater *progress,
                                               int pixelPrecision)
    : m_d(new Private(0, origCage, progress, pixelPrecision))
{
    m_d->srcImage = srcImage;
    m_d->srcImageOffset = srcImageOffset;
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
KisCageTransformWorker::~KisCageTransformWorker()
{
}

void KisCageTransformWorker::setTransformedCage(const QVector<QPointF> &transformedCage)
{
    m_d->transfCage = transformedCage;
}

struct PointsFetcherOp
{
    PointsFetcherOp(const QPolygonF &cagePolygon)
        : m_cagePolygon(cagePolygon),
          m_numValidPoints(0)
    {
        m_polygonDirection = KisAlgebra2D::polygonDirection(cagePolygon);
    }

    inline void processPoint(int col, int row,
                             int prevCol, int prevRow,
                             int colIndex, int rowIndex) {

        Q_UNUSED(prevCol);
        Q_UNUSED(prevRow);
        Q_UNUSED(colIndex);
        Q_UNUSED(rowIndex);

        QPointF pt(col, row);

        if (m_cagePolygon.containsPoint(pt, Qt::OddEvenFill)) {
            KisAlgebra2D::adjustIfOnPolygonBoundary(m_cagePolygon, m_polygonDirection, &pt);

            m_points << pt;
141
            m_pointValid << true;
142 143
            m_numValidPoints++;
        } else {
144 145
            m_points << pt;
            m_pointValid << false;
146 147 148 149 150 151
        }
    }

    inline void nextLine() {
    }

152
    QVector<bool> m_pointValid;
153 154 155 156 157 158 159 160 161 162 163
    QVector<QPointF> m_points;
    QPolygonF m_cagePolygon;
    int m_polygonDirection;
    int m_numValidPoints;
};

void KisCageTransformWorker::prepareTransform()
{
    if (m_d->origCage.size() < 3) return;

    const QPolygonF srcPolygon(m_d->origCage);
164 165 166 167

    QRect srcBounds = m_d->dev ? m_d->dev->region().boundingRect() :
        QRectF(m_d->srcImageOffset, m_d->srcImage.size()).toAlignedRect();
    srcBounds &= srcPolygon.boundingRect().toAlignedRect();
168

169 170 171
    // no need to process empty devices
    if (srcBounds.isEmpty()) return;

172 173 174 175 176 177 178 179 180 181
    m_d->gridSize =
        GridIterationTools::calcGridSize(srcBounds, m_d->pixelPrecision);

    PointsFetcherOp pointsOp(srcPolygon);
    GridIterationTools::processGrid(pointsOp, srcBounds, m_d->pixelPrecision);

    const int numPoints = pointsOp.m_points.size();

    KIS_ASSERT_RECOVER_RETURN(numPoints == m_d->gridSize.width() * m_d->gridSize.height());

182
    m_d->allSrcPoints = pointsOp.m_points;
183 184 185 186 187 188 189
    m_d->allToValidPointsMap.resize(pointsOp.m_points.size());
    m_d->validPoints.resize(pointsOp.m_numValidPoints);

    {
        int validIdx = 0;
        for (int i = 0; i < numPoints; i++) {
            const QPointF &pt = pointsOp.m_points[i];
190
            const bool pointValid = pointsOp.m_pointValid[i];
191

192
            if (pointValid) {
193 194 195 196 197 198 199 200 201 202 203 204 205
                m_d->validPoints[validIdx] = pt;
                m_d->allToValidPointsMap[i] = validIdx;
                validIdx++;
            } else {
                m_d->allToValidPointsMap[i] = -1;
            }
        }
        KIS_ASSERT_RECOVER_NOOP(validIdx == m_d->validPoints.size());
    }

    m_d->cage.precalculateGreenCoordinates(m_d->origCage, m_d->validPoints);
}

206
QVector<QPointF> KisCageTransformWorker::Private::calculateTransformedPoints()
207
{
208
    cage.generateTransformedCageNormals(transfCage);
209

210
    const int numValidPoints = validPoints.size();
211 212 213
    QVector<QPointF> transformedPoints(numValidPoints);

    for (int i = 0; i < numValidPoints; i++) {
214
        transformedPoints[i] = cage.transformedPoint(i, transfCage);
215

Boudewijn Rempt's avatar
Boudewijn Rempt committed
216 217 218 219
#ifdef Q_CC_MSVC
        if (isnan(transformedPoints[i].x()) ||
            isnan(transformedPoints[i].y())) {
#else
220 221
        if (std::isnan(transformedPoints[i].x()) ||
            std::isnan(transformedPoints[i].y())) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
222
#endif
223

224
            warnKrita << "WARNING:     One grid point has been removed from a consideration" << validPoints[i];
225 226 227
            transformedPoints[i] = validPoints[i];
        }

228 229
    }

230 231 232
    return transformedPoints;
}

233 234 235 236 237
inline QVector<int> KisCageTransformWorker::Private::
calculateMappedIndexes(int col, int row,
                       int *numExistingPoints)
{
    *numExistingPoints = 0;
238 239
    QVector<int> cellIndexes =
        GridIterationTools::calculateCellIndexes(col, row, gridSize);
240 241 242 243 244 245 246 247 248

    for (int i = 0; i < 4; i++) {
        cellIndexes[i] = allToValidPointsMap[cellIndexes[i]];
        *numExistingPoints += cellIndexes[i] >= 0;
    }

    return cellIndexes;
}

249

250 251 252 253 254 255 256 257 258 259 260

int KisCageTransformWorker::Private::
tryGetValidIndex(const QPoint &cellPt)
{
    int index = -1;

    return
        cellPt.x() >= 0 &&
        cellPt.y() >= 0 &&
        cellPt.x() < gridSize.width() - 1 &&
        cellPt.y() < gridSize.height() - 1 &&
261
        (index = allToValidPointsMap[GridIterationTools::pointToIndex(cellPt, gridSize)]) >= 0, index;
262 263 264
}


265
struct KisCageTransformWorker::Private::MapIndexesOp {
266

267 268 269 270
    MapIndexesOp(KisCageTransformWorker::Private *d)
        : m_d(d),
          m_srcCagePolygon(QPolygonF(m_d->origCage))
    {
271 272
    }

273 274
    inline QVector<int> calculateMappedIndexes(int col, int row,
                                               int *numExistingPoints) const {
275

276
        return m_d->calculateMappedIndexes(col, row, numExistingPoints);
277 278
    }

279 280
    inline int tryGetValidIndex(const QPoint &cellPt) const {
        return m_d->tryGetValidIndex(cellPt);
281 282
    }

283 284
    inline QPointF getSrcPointForce(const QPoint &cellPt) const {
        return m_d->allSrcPoints[GridIterationTools::pointToIndex(cellPt, m_d->gridSize)];
285 286
    }

287 288
    inline const QPolygonF srcCropPolygon() const {
        return m_srcCagePolygon;
289 290
    }

291 292 293
    KisCageTransformWorker::Private *m_d;
    QPolygonF m_srcCagePolygon;
};
294 295 296

void KisCageTransformWorker::run()
{
297 298
    if (m_d->isGridEmpty()) return;

299
    KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() >= 3);
300 301 302
    KIS_ASSERT_RECOVER_RETURN(m_d->origCage.size() == m_d->transfCage.size());

    QVector<QPointF> transformedPoints = m_d->calculateTransformedPoints();
303 304

    KisPaintDeviceSP srcDev = new KisPaintDevice(*m_d->dev.data());
305
    KisPaintDeviceSP tempDevice = new KisPaintDevice(m_d->dev->colorSpace());
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

    {
        KisSelectionSP selection = new KisSelection();

        KisPainter painter(selection->pixelSelection());
        painter.setPaintColor(KoColor(Qt::black, selection->pixelSelection()->colorSpace()));
        painter.setAntiAliasPolygonFill(true);
        painter.setFillStyle(KisPainter::FillStyleForegroundColor);
        painter.setStrokeStyle(KisPainter::StrokeStyleNone);

        painter.paintPolygon(m_d->origCage);

        m_d->dev->clearSelection(selection);
    }

321
    GridIterationTools::PaintDevicePolygonOp polygonOp(srcDev, tempDevice);
322 323 324 325 326 327
    Private::MapIndexesOp indexesOp(m_d.data());
    GridIterationTools::iterateThroughGrid
        <GridIterationTools::IncompletePolygonPolicy>(polygonOp, indexesOp,
                                                      m_d->gridSize,
                                                      m_d->validPoints,
                                                      transformedPoints);
328 329 330 331

    QRect rect = tempDevice->extent();
    KisPainter gc(m_d->dev);
    gc.bitBlt(rect.topLeft(), tempDevice, rect);
332
}
333

334 335
QImage KisCageTransformWorker::runOnQImage(QPointF *newOffset)
{
336
    if (m_d->isGridEmpty()) return QImage();
337

338 339
    KIS_ASSERT_RECOVER(m_d->origCage.size() >= 3 &&
                       m_d->origCage.size() == m_d->transfCage.size()) {
340 341
        return QImage();
    }
342

343 344 345
    KIS_ASSERT_RECOVER(!m_d->srcImage.isNull()) {
        return QImage();
    }
346

347 348 349
    KIS_ASSERT_RECOVER(m_d->srcImage.format() == QImage::Format_ARGB32) {
        return QImage();
    }
350

351
    QVector<QPointF> transformedPoints = m_d->calculateTransformedPoints();
352

353
    QRectF dstBounds;
354
    Q_FOREACH (const QPointF &pt, transformedPoints) {
355
        KisAlgebra2D::accumulateBounds(pt, &dstBounds);
356
    }
357 358 359 360 361 362 363 364 365 366 367 368 369

    const QRectF srcBounds(m_d->srcImageOffset, m_d->srcImage.size());
    dstBounds |= srcBounds;

    QPointF dstQImageOffset = dstBounds.topLeft();
    *newOffset = dstQImageOffset;

    QRect dstBoundsI = dstBounds.toAlignedRect();


    QImage dstImage(dstBoundsI.size(), m_d->srcImage.format());
    dstImage.fill(0);

370 371
    QImage tempImage(dstImage);

372 373 374 375 376 377 378 379 380 381
    {
        // we shouldn't create too many painters
        QPainter gc(&dstImage);
        gc.drawImage(-dstQImageOffset + m_d->srcImageOffset, m_d->srcImage);
        gc.setBrush(Qt::black);
        gc.setPen(Qt::black);
        gc.setCompositionMode(QPainter::CompositionMode_Clear);
        gc.drawPolygon(QPolygonF(m_d->origCage).translated(-dstQImageOffset));
        gc.end();
    }
382

383
    GridIterationTools::QImagePolygonOp polygonOp(m_d->srcImage, tempImage, m_d->srcImageOffset, dstQImageOffset);
384 385 386 387 388 389
    Private::MapIndexesOp indexesOp(m_d.data());
    GridIterationTools::iterateThroughGrid
        <GridIterationTools::IncompletePolygonPolicy>(polygonOp, indexesOp,
                                                      m_d->gridSize,
                                                      m_d->validPoints,
                                                      transformedPoints);
390

391 392 393 394 395
    {
        QPainter gc(&dstImage);
        gc.drawImage(QPoint(), tempImage);
    }

396
    return dstImage;
397
}
398