Commit 13ca806d authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix 1 px borderline problem in Liquify Transform worker

The border line appeared because of weird rounding/conversions between
QPolygonF->QRectF->QRect. Now we add some noise manually to avoid that.
parent 1c000ac6
......@@ -531,6 +531,26 @@ struct AlwaysCompletePolygonPolicy {
}
};
/**
* There is a weird problem in fetching correct bounds of the polygon.
* If the rightmost (bottommost) point of the polygon is integral, then
* QRectF() will end exactly on it, but when converting into QRect the last
* point will not be taken into account. It happens due to the difference
* between center-point/topleft-point point representation. In many cases
* the latter is expected, but we don't work with it in Qt/Krita.
*/
inline void adjustAlignedPolygon(QPolygonF &polygon)
{
static const qreal eps = 1e-5;
static const QPointF p1(eps, 0.0);
static const QPointF p2(eps, eps);
static const QPointF p3(0.0, eps);
polygon[1] += p1;
polygon[2] += p2;
polygon[3] += p3;
}
template <template <class PolygonOp, class IndexesOp> class IncompletePolygonPolicy,
class PolygonOp,
class IndexesOp>
......@@ -566,6 +586,9 @@ void iterateThroughGrid(PolygonOp &polygonOp,
dstPolygon << transformedPoints[index];
}
adjustAlignedPolygon(srcPolygon);
adjustAlignedPolygon(dstPolygon);
polygonOp(srcPolygon, dstPolygon);
}
}
......
......@@ -723,6 +723,16 @@ QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColor
return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags);
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile,
const QRect &rc,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
return convertToQImage(dstProfile,
rc.x(), rc.y(), rc.width(), rc.height(),
renderingIntent, conversionFlags);
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile * dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
......
......@@ -445,6 +445,14 @@ public:
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags) const;
/**
* Overridden method for convenience
*/
QImage convertToQImage(const KoColorProfile *dstProfile,
const QRect &rc,
KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::InternalRenderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::InternalConversionFlags) const;
/**
* Create an RGBA QImage from a rectangle in the paint device. The
* rectangle is defined by the parent image's bounds.
......
......@@ -20,112 +20,14 @@
#include <qtest_kde.h>
#include <KoColor.h>
#include <KoUpdater.h>
#include "testutil.h"
#include <kis_liquify_transform_worker.h>
#include <kis_cage_transform_worker.h>
#include <algorithm>
#include <kis_algebra_2d.h>
void testCage(bool clockwise, bool unityTransform, bool benchmarkPrepareOnly = false, int pixelPrecision = 8, bool testQImage = false)
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QImage image(TestUtil::fetchDataFileLazy("test_cage_transform.png"));
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->convertFromQImage(image, 0);
QVector<QPointF> origPoints;
QVector<QPointF> transfPoints;
QRectF bounds(dev->exactBounds());
origPoints << bounds.topLeft();
origPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
origPoints << 0.5 * (bounds.topLeft() + bounds.bottomRight());
origPoints << 0.5 * (bounds.topRight() + bounds.bottomRight());
origPoints << bounds.bottomRight();
origPoints << bounds.bottomLeft();
if (!clockwise) {
std::reverse(origPoints.begin(), origPoints.end());
}
if (unityTransform) {
transfPoints = origPoints;
} else {
transfPoints << bounds.topLeft();
transfPoints << 0.5 * (bounds.topLeft() + bounds.topRight());
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight());
transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) +
(bounds.bottomLeft() - bounds.topLeft());
transfPoints << bounds.bottomLeft() +
(bounds.bottomLeft() - bounds.topLeft());
transfPoints << bounds.bottomLeft();
if (!clockwise) {
std::reverse(transfPoints.begin(), transfPoints.end());
}
}
KisCageTransformWorker worker(dev,
origPoints,
updater,
pixelPrecision);
QImage result;
QPointF srcQImageOffset(0, 0);
QPointF dstQImageOffset;
QBENCHMARK_ONCE {
if (!testQImage) {
worker.prepareTransform();
if (!benchmarkPrepareOnly) {
worker.setTransformedCage(transfPoints);
worker.run();
}
} else {
QImage srcImage(image);
image = QImage(image.size(), QImage::Format_ARGB32);
QPainter gc(&image);
gc.drawImage(QPoint(), srcImage);
image.convertToFormat(QImage::Format_ARGB32);
KisCageTransformWorker qimageWorker(image,
srcQImageOffset,
origPoints,
updater,
pixelPrecision);
qimageWorker.prepareTransform();
qimageWorker.setTransformedCage(transfPoints);
result = qimageWorker.runOnQImage(&dstQImageOffset);
}
}
QString testName = QString("%1_%2")
.arg(clockwise ? "clk" : "cclk")
.arg(unityTransform ? "unity" : "normal");
if (testQImage) {
QVERIFY(TestUtil::checkQImage(result, "cage_transform_test", "cage_qimage", testName));
} else if (!benchmarkPrepareOnly && pixelPrecision == 8) {
result = dev->convertToQImage(0);
QVERIFY(TestUtil::checkQImage(result, "cage_transform_test", "cage", testName));
}
}
void KisLiquifyTransformWorkerTest::testPoints()
{
TestUtil::TestProgressBar bar;
......@@ -226,4 +128,29 @@ void KisLiquifyTransformWorkerTest::testPointsQImage()
TestUtil::checkQImage(result, "liquify_transform_test", "liquify_qimage", "resultImage");
}
void KisLiquifyTransformWorkerTest::testIdentityTransform()
{
TestUtil::TestProgressBar bar;
KoProgressUpdater pu(&bar);
KoUpdaterPtr updater = pu.startSubtask();
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
QRect rc(0,0,13,23);
KisPaintDeviceSP dev = new KisPaintDevice(cs);
dev->fill(rc, KoColor(Qt::blue, cs));
const int pixelPrecision = 8;
KisLiquifyTransformWorker worker(dev->exactBounds(),
updater,
pixelPrecision);
worker.run(dev);
QImage result = dev->convertToQImage(0, rc);
TestUtil::checkQImage(result, "liquify_transform_test", "liquify_dev", "identity");
}
QTEST_KDEMAIN(KisLiquifyTransformWorkerTest, GUI)
......@@ -27,6 +27,7 @@ class KisLiquifyTransformWorkerTest : public QObject
private slots:
void testPoints();
void testPointsQImage();
void testIdentityTransform();
};
#endif /* __KIS_LIQUIFY_TRANSFORM_WORKER_TEST_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment