diff --git a/libs/brush/tests/kis_auto_brush_test.cpp b/libs/brush/tests/kis_auto_brush_test.cpp index e8ec39a070..3d8144c675 100644 --- a/libs/brush/tests/kis_auto_brush_test.cpp +++ b/libs/brush/tests/kis_auto_brush_test.cpp @@ -1,196 +1,196 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * Copyright (c) 2009 Cyrille Berger * * 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 //MSVC requires that Vc come first #include "kis_auto_brush_test.h" #include #include #include "../kis_auto_brush.h" #include "kis_mask_generator.h" #include "kis_paint_device.h" #include "kis_fill_painter.h" #include #include #include #include #include #include void KisAutoBrushTest::testCreation() { KisCircleMaskGenerator circle(10, 1.0, 1.0, 1.0, 2, true); KisRectangleMaskGenerator rect(10, 1.0, 1.0, 1.0, 2, true); } void KisAutoBrushTest::testMaskGeneration() { KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 1.0, 1.0, 1.0, 2, false); KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintInformation info(QPointF(100.0, 100.0), 0.5); // check masking an existing paint device KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->setRect(QRect(0, 0, 100, 100)); fdev->initialize(); cs->setOpacity(fdev->data(), OPACITY_OPAQUE_U8, 100 * 100); QPoint errpoint; QImage result(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_1.png"); QImage image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_1.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Check creating a mask dab with a single color fdev = new KisFixedPaintDevice(cs); a->mask(fdev, KoColor(Qt::black, cs), KisDabShape(), info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_3.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_3.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } // Check creating a mask dab with a color taken from a paint device KoColor red(Qt::red, cs); cs->setOpacity(red.data(), quint8(128), 1); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 100, 100, red.data()); fdev = new KisFixedPaintDevice(cs); a->mask(fdev, dev, KisDabShape(), info); result = QImage(QString(FILES_DATA_DIR) + QDir::separator() + "result_autobrush_4.png"); image = fdev->convertToQImage(0); if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_autobrush_test_4.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } static void dabSizeHelper(KisBrushSP const& brush, QString const& name, KisDabShape const& shape, int expectedWidth, int expectedHeight) { qDebug() << name; QCOMPARE(brush->maskWidth(shape, 0.0, 0.0, KisPaintInformation()), expectedWidth); QCOMPARE(brush->maskHeight(shape, 0.0, 0.0, KisPaintInformation()), expectedHeight); } void KisAutoBrushTest::testDabSize() { KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.5, 1.0, 1.0, 2, false); KisBrushSP a = new KisAutoBrush(circle, 0.0, 0.0); QCOMPARE(a->width(), 10); QCOMPARE(a->height(), 5); dabSizeHelper(a, "Identity", KisDabShape(), 10, 5); dabSizeHelper(a, "Double", KisDabShape(2.0, 1.0, 0.0), 20, 10); dabSizeHelper(a, "Halve", KisDabShape(0.5, 1.0, 0.0), 5, 3); dabSizeHelper(a, "180 deg", KisDabShape(1.0, 1.0, M_PI), 10, 5); dabSizeHelper(a, "90 deg", KisDabShape(1.0, 1.0, M_PI_2), 6, 10); // ceil rule dabSizeHelper(a, "-90 deg", KisDabShape(1.0, 1.0, -M_PI_2), 6, 11); // ceil rule dabSizeHelper(a, "45 deg", KisDabShape(1.0, 1.0, 0.25 * M_PI), 11, 11); dabSizeHelper(a, "2x, 45d", KisDabShape(2.0, 1.0, 0.25 * M_PI), 22, 22); dabSizeHelper(a, "0.5x, 45d", KisDabShape(0.5, 1.0, 0.25 * M_PI), 6, 6); dabSizeHelper(a, "0.5x, 45d", KisDabShape(0.5, 1.0, 0.25 * M_PI), 6, 6); - dabSizeHelper(a, "0.5y", KisDabShape(1.0, 0.5, 0.0), 10, 3); + dabSizeHelper(a, "0.5y", KisDabShape(1.0, 0.5, 0.0), 10, 5); } //#define SAVE_OUTPUT_IMAGES void KisAutoBrushTest::testCopyMasking() { int w = 64; int h = 64; int x = 0; int y = 0; QRect rc(x, y, w, h); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor black(Qt::black, cs); KoColor red(Qt::red, cs); KisPaintDeviceSP tempDev = new KisPaintDevice(cs); tempDev->fill(0, 0, w, h, red.data()); #ifdef SAVE_OUTPUT_IMAGES tempDev->convertToQImage(0).save("tempDev.png"); #endif KisCircleMaskGenerator * mask = new KisCircleMaskGenerator(w, 1.0, 0.5, 0.5, 2, true); KisAutoBrush brush(mask, 0, 0); KisFixedPaintDeviceSP maskDab = new KisFixedPaintDevice(cs); brush.mask(maskDab, black, KisDabShape(), KisPaintInformation()); maskDab->convertTo(KoColorSpaceRegistry::instance()->alpha8()); #ifdef SAVE_OUTPUT_IMAGES maskDab->convertToQImage(0, 0, 0, 64, 64).save("maskDab.png"); #endif QCOMPARE(tempDev->exactBounds(), rc); QCOMPARE(maskDab->bounds(), rc); KisFixedPaintDeviceSP dev2fixed = new KisFixedPaintDevice(cs); dev2fixed->setRect(rc); dev2fixed->initialize(); tempDev->readBytes(dev2fixed->data(), rc); dev2fixed->convertToQImage(0).save("converted-tempDev-to-fixed.png"); KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter painter(dev); painter.setCompositeOp(COMPOSITE_COPY); painter.bltFixedWithFixedSelection(x, y, dev2fixed, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); //painter.bitBltWithFixedSelection(x, y, tempDev, maskDab, 0, 0, 0, 0, rc.width(), rc.height()); #ifdef SAVE_OUTPUT_IMAGES dev->convertToQImage(0).save("final.png"); #endif } void KisAutoBrushTest::testClone() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisCircleMaskGenerator* circle = new KisCircleMaskGenerator(10, 0.7, 0.85, 0.5, 2, true); KisBrushSP brush = new KisAutoBrush(circle, 0.5, 0.0); KisPaintInformation info(QPointF(100.0, 100.0), 0.5); KisFixedPaintDeviceSP fdev1 = new KisFixedPaintDevice(cs); brush->mask(fdev1, KoColor(Qt::black, cs), KisDabShape(0.8, 1.0, 8.0), info); QImage res1 = fdev1->convertToQImage(0); KisBrushSP clone = brush->clone(); KisFixedPaintDeviceSP fdev2 = new KisFixedPaintDevice(cs); clone->mask(fdev2, KoColor(Qt::black, cs), KisDabShape(0.8, 1.0, 8.0), info); QImage res2 = fdev2->convertToQImage(0); QCOMPARE(res1, res2); } QTEST_MAIN(KisAutoBrushTest) diff --git a/libs/image/kis_selection_filters.cpp b/libs/image/kis_selection_filters.cpp index 411521a01a..b790436013 100644 --- a/libs/image/kis_selection_filters.cpp +++ b/libs/image/kis_selection_filters.cpp @@ -1,864 +1,864 @@ /* * Copyright (c) 2005 Michael Thaler * Copyright (c) 2011 Dmitry Kazakov * * 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_selection_filters.h" #include #include #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_pixel_selection.h" #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define RINT(x) floor ((x) + 0.5) KisSelectionFilter::~KisSelectionFilter() { } KUndo2MagicString KisSelectionFilter::name() { return KUndo2MagicString(); } QRect KisSelectionFilter::changeRect(const QRect& rect) { return rect; } void KisSelectionFilter::computeBorder(qint32* circ, qint32 xradius, qint32 yradius) { qint32 i; qint32 diameter = xradius * 2 + 1; double tmp; for (i = 0; i < diameter; i++) { if (i > xradius) tmp = (i - xradius) - 0.5; else if (i < xradius) tmp = (xradius - i) - 0.5; else tmp = 0.0; circ[i] = (qint32) RINT(yradius / (double) xradius * sqrt(xradius * xradius - tmp * tmp)); } } void KisSelectionFilter::rotatePointers(quint8** p, quint32 n) { quint32 i; quint8 *p0 = p[0]; for (i = 0; i < n - 1; i++) { p[i] = p[i + 1]; } p[i] = p0; } void KisSelectionFilter::computeTransition(quint8* transition, quint8** buf, qint32 width) { qint32 x = 0; if (width == 1) { if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128)) transition[x] = 255; else transition[x] = 0; return; } if (buf[1][x] > 127) { if (buf[0][x] < 128 || buf[0][x + 1] < 128 || buf[1][x + 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; for (qint32 x = 1; x < width - 1; x++) { if (buf[1][x] >= 128) { if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 || buf[1][x - 1] < 128 || buf[1][x + 1] < 128 || buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; } if (buf[1][x] >= 128) { if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[1][x - 1] < 128 || buf[2][x - 1] < 128 || buf[2][x] < 128) transition[x] = 255; else transition[x] = 0; } else transition[x] = 0; } KUndo2MagicString KisErodeSelectionFilter::name() { return kundo2_i18n("Erode Selection"); } QRect KisErodeSelectionFilter::changeRect(const QRect& rect) { const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisErodeSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // Erode (radius 1 pixel) a mask (1bpp) quint8* buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 min = 255; if (buf[0][x+1] < min) min = buf[0][x+1]; if (buf[1][x] < min) min = buf[1][x]; if (buf[1][x+1] < min) min = buf[1][x+1]; if (buf[1][x+2] < min) min = buf[1][x+2]; if (buf[2][x+1] < min) min = buf[2][x+1]; out[x] = min; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KUndo2MagicString KisDilateSelectionFilter::name() { return kundo2_i18n("Dilate Selection"); } QRect KisDilateSelectionFilter::changeRect(const QRect& rect) { const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisDilateSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // dilate (radius 1 pixel) a mask (1bpp) quint8* buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 max = 0; if (buf[0][x+1] > max) max = buf[0][x+1]; if (buf[1][x] > max) max = buf[1][x]; if (buf[1][x+1] > max) max = buf[1][x+1]; if (buf[1][x+2] > max) max = buf[1][x+2]; if (buf[2][x+1] > max) max = buf[2][x+1]; out[x] = max; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KisBorderSelectionFilter::KisBorderSelectionFilter(qint32 xRadius, qint32 yRadius) : m_xRadius(xRadius), m_yRadius(yRadius) { } KUndo2MagicString KisBorderSelectionFilter::name() { return kundo2_i18n("Border Selection"); } QRect KisBorderSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisBorderSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; quint8 *buf[3]; quint8 **density; quint8 **transition; if (m_xRadius == 1 && m_yRadius == 1) { // optimize this case specifically quint8* source[3]; for (qint32 i = 0; i < 3; i++) source[i] = new quint8[rect.width()]; quint8* transition = new quint8[rect.width()]; pixelSelection->readBytes(source[0], rect.x(), rect.y(), rect.width(), 1); memcpy(source[1], source[0], rect.width()); if (rect.height() > 1) pixelSelection->readBytes(source[2], rect.x(), rect.y() + 1, rect.width(), 1); else memcpy(source[2], source[1], rect.width()); computeTransition(transition, source, rect.width()); pixelSelection->writeBytes(transition, rect.x(), rect.y(), rect.width(), 1); for (qint32 y = 1; y < rect.height(); y++) { rotatePointers(source, 3); if (y + 1 < rect.height()) pixelSelection->readBytes(source[2], rect.x(), rect.y() + y + 1, rect.width(), 1); else memcpy(source[2], source[1], rect.width()); computeTransition(transition, source, rect.width()); pixelSelection->writeBytes(transition, rect.x(), rect.y() + y, rect.width(), 1); } for (qint32 i = 0; i < 3; i++) delete[] source[i]; delete[] transition; return; } qint32* max = new qint32[rect.width() + 2 * m_xRadius]; for (qint32 i = 0; i < (rect.width() + 2 * m_xRadius); i++) max[i] = m_yRadius + 2; max += m_xRadius; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[rect.width()]; transition = new quint8*[m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { transition[i] = new quint8[rect.width() + 2 * m_xRadius]; memset(transition[i], 0, rect.width() + 2 * m_xRadius); transition[i] += m_xRadius; } quint8* out = new quint8[rect.width()]; density = new quint8*[2 * m_xRadius + 1]; density += m_xRadius; for (qint32 x = 0; x < (m_xRadius + 1); x++) { // allocate density[][] density[ x] = new quint8[2 * m_yRadius + 1]; density[ x] += m_yRadius; density[-x] = density[x]; } for (qint32 x = 0; x < (m_xRadius + 1); x++) { // compute density[][] double tmpx, tmpy, dist; quint8 a; if (x > 0) tmpx = x - 0.5; else if (x < 0) tmpx = x + 0.5; else tmpx = 0.0; for (qint32 y = 0; y < (m_yRadius + 1); y++) { if (y > 0) tmpy = y - 0.5; else if (y < 0) tmpy = y + 0.5; else tmpy = 0.0; dist = ((tmpy * tmpy) / (m_yRadius * m_yRadius) + (tmpx * tmpx) / (m_xRadius * m_xRadius)); if (dist < 1.0) a = (quint8)(255 * (1.0 - sqrt(dist))); else a = 0; density[ x][ y] = a; density[ x][-y] = a; density[-x][ y] = a; density[-x][-y] = a; } } pixelSelection->readBytes(buf[0], rect.x(), rect.y(), rect.width(), 1); memcpy(buf[1], buf[0], rect.width()); if (rect.height() > 1) pixelSelection->readBytes(buf[2], rect.x(), rect.y() + 1, rect.width(), 1); else memcpy(buf[2], buf[1], rect.width()); computeTransition(transition[1], buf, rect.width()); for (qint32 y = 1; y < m_yRadius && y + 1 < rect.height(); y++) { // set up top of image rotatePointers(buf, 3); pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + 1, rect.width(), 1); computeTransition(transition[y + 1], buf, rect.width()); } for (qint32 x = 0; x < rect.width(); x++) { // set up max[] for top of image max[x] = -(m_yRadius + 7); for (qint32 j = 1; j < m_yRadius + 1; j++) if (transition[j][x]) { max[x] = j; break; } } for (qint32 y = 0; y < rect.height(); y++) { // main calculation loop rotatePointers(buf, 3); rotatePointers(transition, m_yRadius + 1); if (y < rect.height() - (m_yRadius + 1)) { pixelSelection->readBytes(buf[2], rect.x(), rect.y() + y + m_yRadius + 1, rect.width(), 1); computeTransition(transition[m_yRadius], buf, rect.width()); } else memcpy(transition[m_yRadius], transition[m_yRadius - 1], rect.width()); for (qint32 x = 0; x < rect.width(); x++) { // update max array if (max[x] < 1) { if (max[x] <= -m_yRadius) { if (transition[m_yRadius][x]) max[x] = m_yRadius; else max[x]--; } else if (transition[-max[x]][x]) max[x] = -max[x]; else if (transition[-max[x] + 1][x]) max[x] = -max[x] + 1; else max[x]--; } else max[x]--; if (max[x] < -m_yRadius - 1) max[x] = -m_yRadius - 1; } quint8 last_max = max[0][density[-1]]; qint32 last_index = 1; for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line last_index--; if (last_index >= 0) { last_max = 0; for (qint32 i = m_xRadius; i >= 0; i--) if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x+i]] > last_max) { last_max = density[i][max[x + i]]; last_index = i; } out[x] = last_max; } else { last_max = 0; for (qint32 i = m_xRadius; i >= -m_xRadius; i--) if (max[x + i] <= m_yRadius && max[x + i] >= -m_yRadius && density[i][max[x + i]] > last_max) { last_max = density[i][max[x + i]]; last_index = i; } out[x] = last_max; } if (last_max == 0) { qint32 i; for (i = x + 1; i < rect.width(); i++) { if (max[i] >= -m_yRadius) break; } if (i - x > m_xRadius) { for (; x < i - m_xRadius; x++) out[x] = 0; x--; } last_index = m_xRadius; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } delete [] out; for (qint32 i = 0; i < 3; i++) delete[] buf[i]; max -= m_xRadius; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) { transition[i] -= m_xRadius; delete transition[i]; } delete[] transition; for (qint32 i = 0; i < m_xRadius + 1 ; i++) { density[i] -= m_yRadius; delete density[i]; } density -= m_xRadius; delete[] density; } KisFeatherSelectionFilter::KisFeatherSelectionFilter(qint32 radius) : m_radius(radius) { } KUndo2MagicString KisFeatherSelectionFilter::name() { return kundo2_i18n("Feather Selection"); } QRect KisFeatherSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_radius, -m_radius, m_radius, m_radius); } void KisFeatherSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // compute horizontal kernel const uint kernelSize = m_radius * 2 + 1; Matrix gaussianMatrix(1, kernelSize); const qreal multiplicand = 1 / (2 * M_PI * m_radius * m_radius); const qreal exponentMultiplicand = 1 / (2 * m_radius * m_radius); for (uint x = 0; x < kernelSize; x++) { uint xDistance = qAbs((int)m_radius - (int)x); gaussianMatrix(0, x) = multiplicand * exp( -(qreal)((xDistance * xDistance) + (m_radius * m_radius)) * exponentMultiplicand ); } KisConvolutionKernelSP kernelHoriz = KisConvolutionKernel::fromMatrix(gaussianMatrix, 0, gaussianMatrix.sum()); KisConvolutionKernelSP kernelVertical = KisConvolutionKernel::fromMatrix(gaussianMatrix.transpose(), 0, gaussianMatrix.sum()); KisPaintDeviceSP interm = new KisPaintDevice(pixelSelection->colorSpace()); KisConvolutionPainter horizPainter(interm); horizPainter.setChannelFlags(interm->colorSpace()->channelFlags(false, true)); horizPainter.applyMatrix(kernelHoriz, pixelSelection, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT); horizPainter.end(); KisConvolutionPainter verticalPainter(pixelSelection); verticalPainter.setChannelFlags(pixelSelection->colorSpace()->channelFlags(false, true)); verticalPainter.applyMatrix(kernelVertical, interm, rect.topLeft(), rect.topLeft(), rect.size(), BORDER_REPEAT); verticalPainter.end(); } KisGrowSelectionFilter::KisGrowSelectionFilter(qint32 xRadius, qint32 yRadius) : m_xRadius(xRadius), m_yRadius(yRadius) { } KUndo2MagicString KisGrowSelectionFilter::name() { return kundo2_i18n("Grow Selection"); } QRect KisGrowSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisGrowSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; /** * Much code resembles Shrink filter, so please fix bugs * in both filters */ quint8 **buf; // caches the region's pixel data quint8 **max; // caches the largest values for each column max = new quint8* [rect.width() + 2 * m_xRadius]; buf = new quint8* [m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { buf[i] = new quint8[rect.width()]; } quint8* buffer = new quint8[(rect.width() + 2 * m_xRadius) *(m_yRadius + 1)]; for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) { if (i < m_xRadius) max[i] = buffer; else if (i < rect.width() + m_xRadius) max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)]; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)]; for (qint32 j = 0; j < m_xRadius + 1; j++) max[i][j] = 0; } /* offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius] */ max += m_xRadius; quint8* out = new quint8[ rect.width()]; // holds the new scan line we are computing qint32* circ = new qint32[ 2 * m_xRadius + 1 ]; // holds the y coords of the filter's mask computeBorder(circ, m_xRadius, m_yRadius); /* offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius] */ circ += m_xRadius; memset(buf[0], 0, rect.width()); for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) { // load top of image pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1); } for (qint32 x = 0; x < rect.width() ; x++) { // set up max for top of image max[x][0] = 0; // buf[0][x] is always 0 max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x] for (qint32 j = 2; j < m_yRadius + 1; j++) { max[x][j] = MAX(buf[j][x], max[x][j-1]); } } for (qint32 y = 0; y < rect.height(); y++) { rotatePointers(buf, m_yRadius + 1); if (y < rect.height() - (m_yRadius)) pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1); else memset(buf[m_yRadius], 0, rect.width()); for (qint32 x = 0; x < rect.width(); x++) { /* update max array */ for (qint32 i = m_yRadius; i > 0; i--) { max[x][i] = MAX(MAX(max[x][i - 1], buf[i - 1][x]), buf[i][x]); } max[x][0] = buf[0][x]; } qint32 last_max = max[0][circ[-1]]; qint32 last_index = 1; for (qint32 x = 0; x < rect.width(); x++) { /* render scan line */ last_index--; if (last_index >= 0) { if (last_max == 255) out[x] = 255; else { last_max = 0; for (qint32 i = m_xRadius; i >= 0; i--) if (last_max < max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } else { last_index = m_xRadius; last_max = max[x + m_xRadius][circ[m_xRadius]]; for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--) if (last_max < max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } /* undo the offsets to the pointers so we can free the malloced memmory */ circ -= m_xRadius; max -= m_xRadius; delete[] circ; delete[] buffer; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) delete[] buf[i]; delete[] buf; delete[] out; } KisShrinkSelectionFilter::KisShrinkSelectionFilter(qint32 xRadius, qint32 yRadius, bool edgeLock) : m_xRadius(xRadius), m_yRadius(yRadius), m_edgeLock(edgeLock) { } KUndo2MagicString KisShrinkSelectionFilter::name() { return kundo2_i18n("Shrink Selection"); } QRect KisShrinkSelectionFilter::changeRect(const QRect& rect) { return rect.adjusted(-m_xRadius, -m_yRadius, m_xRadius, m_yRadius); } void KisShrinkSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { if (m_xRadius <= 0 || m_yRadius <= 0) return; /* pretty much the same as fatten_region only different blame all bugs in this function on jaycox@gimp.org */ /* If edge_lock is true we assume that pixels outside the region we are passed are identical to the edge pixels. If edge_lock is false, we assume that pixels outside the region are 0 */ quint8 **buf; // caches the region's pixels quint8 **max; // caches the smallest values for each column qint32 last_max, last_index; max = new quint8* [rect.width() + 2 * m_xRadius]; buf = new quint8* [m_yRadius + 1]; for (qint32 i = 0; i < m_yRadius + 1; i++) { buf[i] = new quint8[rect.width()]; } qint32 buffer_size = (rect.width() + 2 * m_xRadius + 1) * (m_yRadius + 1); quint8* buffer = new quint8[buffer_size]; if (m_edgeLock) memset(buffer, 255, buffer_size); else memset(buffer, 0, buffer_size); for (qint32 i = 0; i < rect.width() + 2 * m_xRadius; i++) { if (i < m_xRadius) if (m_edgeLock) max[i] = buffer; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)]; else if (i < rect.width() + m_xRadius) max[i] = &buffer[(m_yRadius + 1) * (i - m_xRadius)]; else if (m_edgeLock) max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius - 1)]; else max[i] = &buffer[(m_yRadius + 1) * (rect.width() + m_xRadius)]; } if (!m_edgeLock) for (qint32 j = 0 ; j < m_xRadius + 1; j++) max[0][j] = 0; // offset the max pointer by m_xRadius so the range of the array is [-m_xRadius] to [region->w + m_xRadius] max += m_xRadius; quint8* out = new quint8[rect.width()]; // holds the new scan line we are computing qint32* circ = new qint32[2 * m_xRadius + 1]; // holds the y coords of the filter's mask computeBorder(circ, m_xRadius, m_yRadius); // offset the circ pointer by m_xRadius so the range of the array is [-m_xRadius] to [m_xRadius] circ += m_xRadius; for (qint32 i = 0; i < m_yRadius && i < rect.height(); i++) // load top of image pixelSelection->readBytes(buf[i + 1], rect.x(), rect.y() + i, rect.width(), 1); if (m_edgeLock) memcpy(buf[0], buf[1], rect.width()); else memset(buf[0], 0, rect.width()); for (qint32 x = 0; x < rect.width(); x++) { // set up max for top of image max[x][0] = buf[0][x]; for (qint32 j = 1; j < m_yRadius + 1; j++) max[x][j] = MIN(buf[j][x], max[x][j-1]); } for (qint32 y = 0; y < rect.height(); y++) { rotatePointers(buf, m_yRadius + 1); if (y < rect.height() - m_yRadius) pixelSelection->readBytes(buf[m_yRadius], rect.x(), rect.y() + y + m_yRadius, rect.width(), 1); else if (m_edgeLock) memcpy(buf[m_yRadius], buf[m_yRadius - 1], rect.width()); else memset(buf[m_yRadius], 0, rect.width()); for (qint32 x = 0 ; x < rect.width(); x++) { // update max array for (qint32 i = m_yRadius; i > 0; i--) { max[x][i] = MIN(MIN(max[x][i - 1], buf[i - 1][x]), buf[i][x]); } max[x][0] = buf[0][x]; } last_max = max[0][circ[-1]]; last_index = 0; for (qint32 x = 0 ; x < rect.width(); x++) { // render scan line last_index--; if (last_index >= 0) { if (last_max == 0) out[x] = 0; else { last_max = 255; for (qint32 i = m_xRadius; i >= 0; i--) if (last_max > max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } else { last_index = m_xRadius; last_max = max[x + m_xRadius][circ[m_xRadius]]; for (qint32 i = m_xRadius - 1; i >= -m_xRadius; i--) if (last_max > max[x + i][circ[i]]) { last_max = max[x + i][circ[i]]; last_index = i; } out[x] = last_max; } } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, rect.width(), 1); } // undo the offsets to the pointers so we can free the malloced memmory circ -= m_xRadius; max -= m_xRadius; delete[] circ; delete[] buffer; delete[] max; for (qint32 i = 0; i < m_yRadius + 1; i++) - delete buf[i]; + delete[] buf[i]; delete[] buf; delete[] out; } KUndo2MagicString KisSmoothSelectionFilter::name() { return kundo2_i18n("Smooth Selection"); } QRect KisSmoothSelectionFilter::changeRect(const QRect& rect) { const qint32 radius = 1; return rect.adjusted(-radius, -radius, radius, radius); } void KisSmoothSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { // Simple convolution filter to smooth a mask (1bpp) quint8 *buf[3]; qint32 width = rect.width(); qint32 height = rect.height(); quint8* out = new quint8[width]; for (qint32 i = 0; i < 3; i++) buf[i] = new quint8[width + 2]; // load top of image pixelSelection->readBytes(buf[0] + 1, rect.x(), rect.y(), width, 1); buf[0][0] = buf[0][1]; buf[0][width + 1] = buf[0][width]; memcpy(buf[1], buf[0], width + 2); for (qint32 y = 0; y < height; y++) { if (y + 1 < height) { pixelSelection->readBytes(buf[2] + 1, rect.x(), rect.y() + y + 1, width, 1); buf[2][0] = buf[2][1]; buf[2][width + 1] = buf[2][width]; } else { memcpy(buf[2], buf[1], width + 2); } for (qint32 x = 0 ; x < width; x++) { qint32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] + buf[1][x] + buf[2][x+1] + buf[1][x+2] + buf[2][x] + buf[1][x+1] + buf[2][x+2]); out[x] = value / 9; } pixelSelection->writeBytes(out, rect.x(), rect.y() + y, width, 1); rotatePointers(buf, 3); } for (qint32 i = 0; i < 3; i++) delete[] buf[i]; delete[] out; } KUndo2MagicString KisInvertSelectionFilter::name() { return kundo2_i18n("Invert Selection"); } QRect KisInvertSelectionFilter::changeRect(const QRect& rect) { Q_UNUSED(rect); return QRect(); } void KisInvertSelectionFilter::process(KisPixelSelectionSP pixelSelection, const QRect& rect) { Q_UNUSED(rect); pixelSelection->invert(); } diff --git a/libs/image/tests/kis_convolution_painter_test.cpp b/libs/image/tests/kis_convolution_painter_test.cpp index 0da4cb4192..d8898fd789 100644 --- a/libs/image/tests/kis_convolution_painter_test.cpp +++ b/libs/image/tests/kis_convolution_painter_test.cpp @@ -1,440 +1,440 @@ /* * Copyright (c) 2007 Boudewijn Rempt boud@valdyas.org * * 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 //MSVC requires that Vc come first #include "kis_convolution_painter_test.h" #include #include #include #include #include #include #include "kis_paint_device.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include #include #include "testutil.h" KisPaintDeviceSP initAsymTestDevice(QRect &imageRect, int &pixelSize, QByteArray &initialData) { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); pixelSize = dev->pixelSize(); imageRect = QRect(0,0,5,5); initialData.resize(25 * pixelSize); quint8 *ptr = (quint8*) initialData.data(); for(int i = 0; i < 25; i++) { KoColor pixel(QColor(i,i,i,255), dev->colorSpace()); memcpy(ptr, pixel.data(), pixelSize); ptr += pixelSize; } dev->writeBytes((const quint8*)initialData.constData(), imageRect); return dev; } Matrix initSymmFilter(qreal &offset, qreal &factor) { Matrix filter; filter(0,0) = 1.0 / 21; filter(0,1) = 3.0 / 21; filter(0,2) = 1.0 / 21; filter(1,0) = 3.0 / 21; filter(1,1) = 5.0 / 21; filter(1,2) = 3.0 / 21; filter(2,0) = 1.0 / 21; filter(2,1) = 3.0 / 21; filter(2,2) = 1.0 / 21; offset = 0.0; factor = 1.0; return filter; } Matrix initAsymmFilter(qreal &offset, qreal &factor) { Matrix filter; filter(0,0) = 1.0; filter(1,0) = 2.0; filter(2,0) = 1.0; filter(0,1) = 0.0; filter(1,1) = 1.0; filter(2,1) = 0.0; filter(0,2) =-1.0; filter(1,2) =-2.0; filter(2,2) =-1.0; offset = 0.0; factor = 1.0; return filter; } void printPixel(QString prefix, int pixelSize, quint8 *data) { QString str = prefix; for(int i = 0; i < pixelSize; i++) { str += ' '; str += QString::number(data[i]); } dbgKrita << str; } void KisConvolutionPainterTest::testIdentityConvolution() { QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(qimage, 0, 0, 0); KisConvolutionKernelSP kernel = new KisConvolutionKernel(3, 3, 0, 0); kernel->data()(0) = 0; kernel->data()(1) = 0; kernel->data()(2) = 0; kernel->data()(3) = 0; kernel->data()(4) = 1; kernel->data()(5) = 0; kernel->data()(6) = 0; kernel->data()(7) = 0; kernel->data()(8) = 0; KisConvolutionPainter gc(dev); gc.beginTransaction(); gc.applyMatrix(kernel, dev, QPoint(0, 0), QPoint(0, 0), QSize(qimage.width(), qimage.height())); gc.deleteTransaction(); QImage resultImage = dev->convertToQImage(0, 0, 0, qimage.width(), qimage.height()); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, qimage, resultImage)) { resultImage.save("identity_convolution.png"); QFAIL(QString("Identity kernel did change image, first different pixel: %1,%2 ").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisConvolutionPainterTest::testSymmConvolution() { qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initSymmFilter(offset, factor); QRect imageRect; int pixelSize = 0; QByteArray initialData; KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(filter, offset, factor); KisConvolutionPainter gc(dev); gc.beginTransaction(); QRect filterRect = imageRect.adjusted(1,1,-1,-1); gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(), filterRect.size()); gc.deleteTransaction(); QByteArray resultData(initialData.size(), 0); dev->readBytes((quint8*)resultData.data(), imageRect); QCOMPARE(resultData, initialData); } void KisConvolutionPainterTest::testAsymmConvolutionImp(QBitArray channelFlags) { qreal offset = 0.0; qreal factor = 1.0; Matrix filter = initAsymmFilter(offset, factor); QRect imageRect; int pixelSize = -1; QByteArray initialData; KisPaintDeviceSP dev = initAsymTestDevice(imageRect, pixelSize, initialData); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMatrix(filter, offset, factor); KisConvolutionPainter gc(dev); gc.beginTransaction(); gc.setChannelFlags(channelFlags); QRect filterRect = imageRect.adjusted(1,1,-1,-1); gc.applyMatrix(kernel, dev, filterRect.topLeft(), filterRect.topLeft(), filterRect.size()); gc.deleteTransaction(); QByteArray resultData(initialData.size(), 0); dev->readBytes((quint8*)resultData.data(), imageRect); QRect filteredRect = imageRect.adjusted(1, 1, -1, -1); quint8 *srcPtr = (quint8*) initialData.data(); quint8 *resPtr = (quint8*) resultData.data(); for(int row = 0; row < imageRect.height(); row++) { for(int col = 0; col < imageRect.width(); col++) { bool isFiltered = filteredRect.contains(col, row); int pixelValue = 8 + row * imageRect.width() + col; KoColor filteredPixel(QColor(pixelValue, pixelValue, pixelValue, 255), dev->colorSpace()); KoColor resultPixel(dev->colorSpace()); for(int j = 0; j < pixelSize; j++) { resultPixel.data()[j] = isFiltered && channelFlags[j] ? filteredPixel.data()[j] : srcPtr[j]; } if(memcmp(resPtr, resultPixel.data(), pixelSize)) { printPixel("Actual: ", pixelSize, resPtr); printPixel("Expected:", pixelSize, resultPixel.data()); QFAIL("Failed to filter area"); } srcPtr += pixelSize; resPtr += pixelSize; } } } void KisConvolutionPainterTest::testAsymmAllChannels() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipRed() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[2] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipGreen() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[1] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipBlue() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[0] = false; testAsymmConvolutionImp(channelFlags); } void KisConvolutionPainterTest::testAsymmSkipAlpha() { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); channelFlags[3] = false; testAsymmConvolutionImp(channelFlags); } // #include void KisConvolutionPainterTest::benchmarkConvolution() { QImage referenceImage(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); QRect imageRect(QPoint(), referenceImage.size()); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); int diameter = 1; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 4; i++) { KisCircleMaskGenerator* kas = new KisCircleMaskGenerator(diameter, 1.0, 5, 5, 2, false); KisConvolutionKernelSP kernel = KisConvolutionKernel::fromMaskGenerator(kas); KisConvolutionPainter gc(dev); QTime timer; timer.start(); // CALLGRIND_START_INSTRUMENTATION; gc.beginTransaction(); gc.applyMatrix(kernel, dev, imageRect.topLeft(), imageRect.topLeft(), imageRect.size()); gc.deleteTransaction(); // CALLGRIND_STOP_INSTRUMENTATION; dbgKrita << "Diameter:" << diameter << "time:" << timer.elapsed(); - if(diameter < 10) { + if(diameter < 4) { diameter += 2; } else { diameter += 8; } } } void KisConvolutionPainterTest::testGaussianBase(KisPaintDeviceSP dev, bool useFftw, const QString &prefix) { QBitArray channelFlags = KoColorSpaceRegistry::instance()->rgb8()->channelFlags(true, true); KisPainter gc(dev); qreal horizontalRadius = 5, verticalRadius = 5; for(int i = 0; i < 3 ; i++, horizontalRadius+=5, verticalRadius+=5) { QTime timer; timer.start(); gc.beginTransaction(); if (( horizontalRadius > 0 ) && ( verticalRadius > 0 )) { KisPaintDeviceSP interm = new KisPaintDevice(dev->colorSpace()); KisConvolutionKernelSP kernelHoriz = KisGaussianKernel::createHorizontalKernel(horizontalRadius); KisConvolutionKernelSP kernelVertical = KisGaussianKernel::createVerticalKernel(verticalRadius); const QRect applyRect = dev->exactBounds(); KisConvolutionPainter::TestingEnginePreference enginePreference = useFftw ? KisConvolutionPainter::FFTW : KisConvolutionPainter::SPATIAL; KisConvolutionPainter horizPainter(interm, enginePreference); horizPainter.setChannelFlags(channelFlags); horizPainter.applyMatrix(kernelHoriz, dev, applyRect.topLeft() - QPoint(0, verticalRadius), applyRect.topLeft() - QPoint(0, verticalRadius), applyRect.size() + QSize(0, 2 * verticalRadius), BORDER_REPEAT); KisConvolutionPainter verticalPainter(dev, enginePreference); verticalPainter.setChannelFlags(channelFlags); verticalPainter.applyMatrix(kernelVertical, interm, applyRect.topLeft(), applyRect.topLeft(), applyRect.size(), BORDER_REPEAT); QImage result = dev->convertToQImage(0, applyRect.x(), applyRect.y(), applyRect.width(), applyRect.height()); QString engine = useFftw ? "fftw" : "spatial"; QString testCaseName = QString("test_gaussian_%1_%2_%3.png").arg(horizontalRadius).arg(verticalRadius).arg(engine); TestUtil::checkQImage(result, "convolution_painter_test", QString("gaussian_") + prefix, testCaseName); gc.revertTransaction(); } dbgKrita << "Elapsed time:" << timer.elapsed() << "ms"; } } void KisConvolutionPainterTest::testGaussian(bool useFftw) { QImage referenceImage(TestUtil::fetchDataFileLazy("kritaTransparent.png")); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); testGaussianBase(dev, useFftw, ""); } void KisConvolutionPainterTest::testGaussianSpatial() { testGaussian(false); } void KisConvolutionPainterTest::testGaussianFFTW() { testGaussian(true); } void KisConvolutionPainterTest::testGaussianSmall(bool useFftw) { KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); KoColor c(Qt::yellow, dev->colorSpace()); for (int i = 0; i < 50; i++) { quint8 baseOpacity = 75; KoColor c(Qt::magenta, dev->colorSpace()); for (int j = 0; j <= 6; j++) { c.setOpacity(static_cast(baseOpacity + 30 * j)); dev->setPixel(i + j, i, c); } } testGaussianBase(dev, useFftw, "reduced"); } void KisConvolutionPainterTest::testGaussianSmallSpatial() { testGaussianSmall(false); } void KisConvolutionPainterTest::testGaussianSmallFFTW() { testGaussianSmall(true); } void KisConvolutionPainterTest::testGaussianDetails(bool useFftw) { QImage referenceImage(TestUtil::fetchDataFileLazy("resolution_test.png")); KisPaintDeviceSP dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); dev->convertFromQImage(referenceImage, 0, 0, 0); testGaussianBase(dev, useFftw, "details"); } void KisConvolutionPainterTest::testGaussianDetailsSpatial() { testGaussianDetails(false); } void KisConvolutionPainterTest::testGaussianDetailsFFTW() { testGaussianDetails(true); } QTEST_MAIN(KisConvolutionPainterTest) diff --git a/libs/image/tests/kis_fixed_paint_device_test.cpp b/libs/image/tests/kis_fixed_paint_device_test.cpp index ed95ae122e..caad6a0f73 100644 --- a/libs/image/tests/kis_fixed_paint_device_test.cpp +++ b/libs/image/tests/kis_fixed_paint_device_test.cpp @@ -1,370 +1,370 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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_fixed_paint_device_test.h" #include #include #include #include #include #include "kis_painter.h" #include "kis_types.h" #include "kis_paint_device.h" #include "kis_fixed_paint_device.h" #include "kis_layer.h" #include "kis_paint_layer.h" #include "kis_selection.h" #include "kis_datamanager.h" #include "kis_global.h" #include "testutil.h" #include "kis_transaction.h" #include "kis_image.h" void KisFixedPaintDeviceTest::testCreation() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev = new KisFixedPaintDevice(cs); QVERIFY(dev->bounds() == QRect()); QVERIFY(*dev->colorSpace() == *cs); QVERIFY(dev->pixelSize() == cs->pixelSize()); dev->setRect(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); dev->initialize(); QVERIFY(dev->data() != 0); quint8* data = dev->data(); for (uint i = 0; i < 100 * 100 * cs->pixelSize(); ++i) { QVERIFY(data[i] == 0); } } void logFailure(const QString & reason, const KoColorSpace * srcCs, const KoColorSpace * dstCs) { QString profile1("no profile"); QString profile2("no profile"); if (srcCs->profile()) profile1 = srcCs->profile()->name(); if (dstCs->profile()) profile2 = dstCs->profile()->name(); QWARN(QString("Failed %1 %2 -> %3 %4 %5") .arg(srcCs->name()) .arg(profile1) .arg(dstCs->name()) .arg(profile2) .arg(reason) .toLatin1()); } void KisFixedPaintDeviceTest::testColorSpaceConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8(); const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(srcCs); dev->convertFromQImage(image, 0); dev->convertTo(dstCs); QVERIFY(dev->bounds() == QRect(0, 0, image.width(), image.height())); QVERIFY(dev->pixelSize() == dstCs->pixelSize()); QVERIFY(*dev->colorSpace() == *dstCs); } void KisFixedPaintDeviceTest::testRoundtripQImageConversion() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->convertFromQImage(image, 0); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_fixed_paint_device_test_test_roundtrip_qimage.png"); result.save("kis_fixed_paint_device_test_test_roundtrip_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltFixed() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); // Without opacity KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result, 1)) { fdev->convertToQImage(0).save("kis_fixed_paint_device_test_test_blt_fixed_expected.png"); result.save("kis_fixed_paint_device_test_test_blt_fixed_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltFixedOpacity() { // blt a semi-transparent image on a white paint device QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 640, 441); QImage checkResult(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent_result.png"); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, checkResult, result, 1)) { checkResult.save("kis_fixed_paint_device_test_test_blt_fixed_opactiy_expected.png"); result.save("kis_fixed_paint_device_test_test_blt_fixed_opacity_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testSilly() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->initialize(); dev->initialize(); } void KisFixedPaintDeviceTest::testClear() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->clear(QRect(0, 0, 100, 100)); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data() + (50 * 50 * cs->pixelSize())) == OPACITY_TRANSPARENT_U8); } void KisFixedPaintDeviceTest::testFill() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); quint8* red = new quint8[cs->pixelSize()]; memcpy(red, KoColor(Qt::red, cs).data(), cs->pixelSize()); cs->setOpacity(red, quint8(128), 1); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->fill(0, 0, 100, 100, red); QVERIFY(dev->bounds() == QRect(0, 0, 100, 100)); QVERIFY(cs->opacityU8(dev->data()) == 128); QVERIFY(memcmp(dev->data(), red, cs->pixelSize()) == 0); //Compare fill will normal paint device dev = new KisFixedPaintDevice(cs); dev->setRect(QRect(0, 0, 150, 150)); dev->initialize(); dev->fill(50, 50, 50, 50, red); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->fill(50, 50, 50, 50, red); QImage image = dev->convertToQImage(0); QImage checkImage = dev2->convertToQImage(0, 0, 0, 150, 150); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, checkImage)) { image.save("kis_fixed_paint_device_filled_result.png"); checkImage.save("kis_fixed_paint_device_filled_result_expected.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } delete[] red; } void KisFixedPaintDeviceTest::testBltFixedSmall() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "fixed_blit_small.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); // Without opacity KisPaintDeviceSP dev = new KisPaintDevice(cs); KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); QImage result = dev->convertToQImage(0, 0, 0, 51, 51); QPoint errpoint; if (!TestUtil::compareQImages(errpoint, image, result)) { image.save("kis_fixed_paint_device_test_blt_small_image.png"); result.save("kis_fixed_paint_device_test_blt_small_result.png"); QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1()); } } void KisFixedPaintDeviceTest::testBltPerformance() { QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa_transparent.png"); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP fdev = new KisFixedPaintDevice(cs); fdev->convertFromQImage(image, 0); KisPaintDeviceSP dev = new KisPaintDevice(cs); dev->fill(0, 0, 640, 441, KoColor(Qt::white, cs).data()); QTime t; t.start(); int x; - for (x = 0; x < 1000; ++x) { + for (x = 0; x < 100; ++x) { KisPainter gc(dev); gc.bltFixed(QPoint(0, 0), fdev, image.rect()); } dbgKrita << x << "blits" << " done in " << t.elapsed() << "ms"; } inline void setPixel(KisFixedPaintDeviceSP dev, int x, int y, quint8 alpha) { KoColor c(Qt::black, dev->colorSpace()); c.setOpacity(alpha); dev->fill(x, y, 1, 1, c.data()); } inline quint8 pixel(KisFixedPaintDeviceSP dev, int x, int y) { KoColor c(Qt::black, dev->colorSpace()); dev->readBytes(c.data(), x, y, 1, 1); return c.opacityU8(); } void KisFixedPaintDeviceTest::testMirroring_data() { QTest::addColumn("rc"); QTest::addColumn("mirrorHorizontally"); QTest::addColumn("mirrorVertically"); QTest::newRow("4, false, false") << (QRect(99,99,4,4)) << false << false; QTest::newRow("4, false, true") << (QRect(99,99,4,4)) << false << true; QTest::newRow("4, true, false") << (QRect(99,99,4,4)) << true << false; QTest::newRow("4, true, true") << (QRect(99,99,4,4)) << true << true; QTest::newRow("5, false, false") << (QRect(99,99,5,5)) << false << false; QTest::newRow("5, false, true") << (QRect(99,99,5,5)) << false << true; QTest::newRow("5, true, false") << (QRect(99,99,5,5)) << true << false; QTest::newRow("5, true, true") << (QRect(99,99,5,5)) << true << true; } void KisFixedPaintDeviceTest::testMirroring() { QFETCH(QRect, rc); QFETCH(bool, mirrorHorizontally); QFETCH(bool, mirrorVertically); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisFixedPaintDeviceSP dev = new KisFixedPaintDevice(cs); dev->setRect(rc); dev->initialize(); KoColor c(Qt::black, cs); qsrand(1); int value = 0; for (int i = rc.x(); i < rc.x() + rc.width(); i++) { for (int j = rc.y(); j < rc.y() + rc.height(); j++) { setPixel(dev, i, j, value); value = qrand() % 255; } value = qrand() % 255; } //dev->convertToQImage(0).save("0_a.png"); dev->mirror(mirrorHorizontally, mirrorVertically); //dev->convertToQImage(0).save("0_b.png"); int startX; int endX; int incX; int startY; int endY; int incY; if (mirrorHorizontally) { startX = rc.x() + rc.width() - 1; endX = rc.x() - 1; incX = -1; } else { startX = rc.x(); endX = rc.x() + rc.width(); incX = 1; } if (mirrorVertically) { startY = rc.y() + rc.height() - 1; endY = rc.y() - 1; incY = -1; } else { startY = rc.y(); endY = rc.y() + rc.height(); incY = 1; } qsrand(1); value = 0; for (int i = startX; i != endX ; i += incX) { for (int j = startY; j != endY; j += incY) { QCOMPARE(pixel(dev, i, j), (quint8)value); value = qrand() % 255; } value = qrand() % 255; } } QTEST_MAIN(KisFixedPaintDeviceTest) diff --git a/libs/image/tests/kis_warp_transform_worker_test.cpp b/libs/image/tests/kis_warp_transform_worker_test.cpp index 764f526876..d29085a4fe 100644 --- a/libs/image/tests/kis_warp_transform_worker_test.cpp +++ b/libs/image/tests/kis_warp_transform_worker_test.cpp @@ -1,353 +1,353 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * 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_warp_transform_worker_test.h" #include #include "testutil.h" #include "kis_warptransform_worker.h" #include struct WarpTransforWorkerData { WarpTransforWorkerData() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); updater = pu.startSubtask(); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); // QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png")); QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png")); dev = new KisPaintDevice(cs); dev->convertFromQImage(image, 0); alpha = 1.0; bounds = dev->exactBounds(); origPoints << bounds.topLeft(); origPoints << bounds.topRight(); origPoints << bounds.bottomRight(); origPoints << bounds.bottomLeft(); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0); transfPoints << bounds.topLeft(); transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft()); transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft()); transfPoints << bounds.bottomLeft() + QPointF(200, 0); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40,20); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40,20); } KisPaintDeviceSP dev; QVector origPoints; QVector transfPoints; qreal alpha; KoUpdaterPtr updater; QRectF bounds; }; void KisWarpTransformWorkerTest::test() { WarpTransforWorkerData d; KisWarpTransformWorker worker(KisWarpTransformWorker::RIGID_TRANSFORM, d.dev, d.origPoints, d.transfPoints, d.alpha, d.updater); QBENCHMARK_ONCE { worker.run(); } QImage result = d.dev->convertToQImage(0); TestUtil::checkQImage(result, "warp_transform_test", "simple", "tr"); } void KisWarpTransformWorkerTest::testQImage() { TestUtil::TestProgressBar bar; KoProgressUpdater pu(&bar); KoUpdaterPtr updater = pu.startSubtask(); // QImage image(TestUtil::fetchDataFileLazy("test_transform_quality.png")); QImage image(TestUtil::fetchDataFileLazy("test_transform_quality_second.png")); image = image.convertToFormat(QImage::Format_ARGB32); dbgKrita << ppVar(image.format()); QVector origPoints; QVector transfPoints; qreal alpha = 1.0; QRectF bounds(image.rect()); origPoints << bounds.topLeft(); origPoints << bounds.topRight(); origPoints << bounds.bottomRight(); origPoints << bounds.bottomLeft(); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()); origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0); transfPoints << bounds.topLeft(); transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft()); transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft()); transfPoints << bounds.bottomLeft() + QPointF(200, 0); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40,20); transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40,20); QImage result; QPointF newOffset; QBENCHMARK_ONCE { result = KisWarpTransformWorker::transformQImage( KisWarpTransformWorker::RIGID_TRANSFORM, origPoints, transfPoints, alpha, image, QPointF(), &newOffset); } dbgKrita << ppVar(newOffset); TestUtil::checkQImage(result, "warp_transform_test", "qimage", "tr"); } #include "kis_four_point_interpolator_forward.h" void KisWarpTransformWorkerTest::testForwardInterpolator() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 10); dst << QPointF(100, 120); dst << QPointF(0, 100); KisFourPointInterpolatorForward interp(src, dst); QCOMPARE(interp.map(QPointF(0,50)), QPointF(0,50)); QCOMPARE(interp.map(QPointF(50,0)), QPointF(50,5)); QCOMPARE(interp.map(QPointF(100,0)), QPointF(100,10)); QCOMPARE(interp.map(QPointF(100,50)), QPointF(100,65)); QCOMPARE(interp.map(QPointF(100,100)), QPointF(100,120)); QCOMPARE(interp.map(QPointF(50,100)), QPointF(50,110)); QCOMPARE(interp.map(QPointF(50,50)), QPointF(50,57.5)); } #include "kis_four_point_interpolator_backward.h" void KisWarpTransformWorkerTest::testBackwardInterpolatorXShear() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 0); dst << QPointF(120, 100); dst << QPointF(10, 100); KisFourPointInterpolatorBackward interp(src, dst); QCOMPARE(interp.map(QPointF(10,100)), QPointF(0,100)); QCOMPARE(interp.map(QPointF(5,50)), QPointF(0,50)); QCOMPARE(interp.map(QPointF(110,50)), QPointF(100,50)); QCOMPARE(interp.map(QPointF(57.5,50)), QPointF(50,50)); } void KisWarpTransformWorkerTest::testBackwardInterpolatorYShear() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 10); dst << QPointF(100, 120); dst << QPointF(0, 100); KisFourPointInterpolatorBackward interp(src, dst); QCOMPARE(interp.map(QPointF(100,10)), QPointF(100,0)); QCOMPARE(interp.map(QPointF(50,5)), QPointF(50,0)); QCOMPARE(interp.map(QPointF(50,110)), QPointF(50,100)); QCOMPARE(interp.map(QPointF(50,57.5)), QPointF(50,50)); } void KisWarpTransformWorkerTest::testBackwardInterpolatorXYShear() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(0, 0); dst << QPointF(100, 10); dst << QPointF(140, 120); dst << QPointF(20, 100); KisFourPointInterpolatorBackward interp(src, dst); QCOMPARE(interp.map(QPointF(100,10)), QPointF(100,0)); QCOMPARE(interp.map(QPointF(50,5)), QPointF(50,0)); QCOMPARE(interp.map(QPointF(80,110)), QPointF(50,100)); QCOMPARE(interp.map(QPointF(120,65)), QPointF(100,50)); QCOMPARE(interp.map(QPointF(10,50)), QPointF(0,50)); } void KisWarpTransformWorkerTest::testBackwardInterpolatorRoundTrip() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst; dst << QPointF(100, 100); dst << QPointF(20, 140); dst << QPointF(10, 80); dst << QPointF(15, 5); KisFourPointInterpolatorForward f(src, dst); KisFourPointInterpolatorBackward b(src, dst); for (qreal y = 0; y <= 100; y += 1.0) { for (qreal x = 0; x <= 100; x += 1.0) { QPointF pt(x, y); QPointF fwdPt = f.map(pt); QPointF bwdPt = b.map(fwdPt); //dbgKrita << "R:" << ppVar(pt) << ppVar(fwdPt) << ppVar(bwdPt) << (bwdPt - pt); QVERIFY((bwdPt - pt).manhattanLength() < 1e-3); } } } #include "kis_grid_interpolation_tools.h" void KisWarpTransformWorkerTest::testGridSize() { QCOMPARE(GridIterationTools::calcGridDimension(1, 7, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(1, 8, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(1, 9, 4), 4); QCOMPARE(GridIterationTools::calcGridDimension(0, 7, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(1, 8, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(4, 9, 4), 3); QCOMPARE(GridIterationTools::calcGridDimension(0, 9, 4), 4); QCOMPARE(GridIterationTools::calcGridDimension(-1, 9, 4), 5); QCOMPARE(GridIterationTools::calcGridDimension(0, 300, 8), 39); } void KisWarpTransformWorkerTest::testBackwardInterpolatorExtrapolation() { QPolygonF src; src << QPointF(0, 0); src << QPointF(100, 0); src << QPointF(100, 100); src << QPointF(0, 100); QPolygonF dst(src); std::rotate(dst.begin(), dst.begin() + 1, dst.end()); KisFourPointInterpolatorBackward interp(src, dst); // standard checks QCOMPARE(interp.map(QPointF(0,0)), QPointF(0,100)); QCOMPARE(interp.map(QPointF(100,0)), QPointF(0,0)); QCOMPARE(interp.map(QPointF(100,100)), QPointF(100,0)); QCOMPARE(interp.map(QPointF(0,100)), QPointF(100,100)); // extrapolate! QCOMPARE(interp.map(QPointF(-10,0)), QPointF(0,110)); QCOMPARE(interp.map(QPointF(0,-10)), QPointF(-10,100)); QCOMPARE(interp.map(QPointF(-10,-10)), QPointF(-10,110)); QCOMPARE(interp.map(QPointF(110,0)), QPointF(0,-10)); QCOMPARE(interp.map(QPointF(100,-10)), QPointF(-10,0)); QCOMPARE(interp.map(QPointF(110,-10)), QPointF(-10,-10)); QCOMPARE(interp.map(QPointF(110,100)), QPointF(100, -10)); QCOMPARE(interp.map(QPointF(100,110)), QPointF(110, 0)); QCOMPARE(interp.map(QPointF(110,110)), QPointF(110,-10)); QCOMPARE(interp.map(QPointF(-10,100)), QPointF(100, 110)); QCOMPARE(interp.map(QPointF(0,110)), QPointF(110, 100)); QCOMPARE(interp.map(QPointF(-10,110)), QPointF(110,110)); } #include "krita_utils.h" void KisWarpTransformWorkerTest::testNeedChangeRects() { WarpTransforWorkerData d; KisWarpTransformWorker worker(KisWarpTransformWorker::RIGID_TRANSFORM, d.dev, d.origPoints, d.transfPoints, d.alpha, d.updater); QCOMPARE(KritaUtils::sampleRectWithPoints(d.bounds.toAlignedRect()).size(), 9); - QCOMPARE(worker.approxChangeRect(d.bounds.toAlignedRect()), QRect(-89,-89, 1072,1076)); + QCOMPARE(worker.approxChangeRect(d.bounds.toAlignedRect()), QRect(-44,-44, 982,986)); } QTEST_MAIN(KisWarpTransformWorkerTest) diff --git a/libs/ui/tests/kis_derived_resources_test.cpp b/libs/ui/tests/kis_derived_resources_test.cpp index a19198426e..759b57b7c5 100644 --- a/libs/ui/tests/kis_derived_resources_test.cpp +++ b/libs/ui/tests/kis_derived_resources_test.cpp @@ -1,106 +1,143 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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_derived_resources_test.h" #include #include #include #include #include "kis_canvas_resource_provider.h" #include #include #include #include #include #include #include - +#include #include "testutil.h" +void addResourceTypes() +{ + // All Krita's resource types + KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); + KoResourcePaths::addResourceType("kis_images", "data", "/images/"); + KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); + KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); + KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); + KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); + KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); + KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); + KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); + KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); + KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); + KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); + KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); + KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); + KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); + KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); + KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); + KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); + KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); + KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); + KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); + KoResourcePaths::addResourceType("tags", "data", "/tags/"); + +} + void KisDerivedResourcesTest::test() { KisDocument* doc = createEmptyDocument(); + + addResourceTypes(); + KisMainWindow* mainWindow = KisPart::instance()->createMainWindow(); QPointer view = new KisView(doc, mainWindow->resourceManager(), mainWindow->actionCollection(), mainWindow); KisViewManager *viewManager = new KisViewManager(mainWindow, mainWindow->actionCollection()); KoCanvasResourceManager *manager = viewManager->resourceProvider()->resourceManager(); QApplication::processEvents(); QString presetFileName = "autobrush_300px.kpp"; QVariant i; KisPaintOpPresetSP preset; if (!presetFileName.isEmpty()) { QString fullFileName = TestUtil::fetchDataFileLazy(presetFileName); preset = new KisPaintOpPreset(fullFileName); bool presetValid = preset->load(); Q_ASSERT(presetValid); Q_UNUSED(presetValid); i.setValue(preset); } QVERIFY(i.isValid()); QSignalSpy spy(manager, SIGNAL(canvasResourceChanged(int, const QVariant &))); manager->setResource(KisCanvasResourceProvider::CurrentPaintOpPreset, i); QCOMPARE(spy[0][0].toInt(), (int)KisCanvasResourceProvider::CurrentPaintOpPreset); QCOMPARE(spy[0][1].value(), preset); QCOMPARE(spy[1][0].toInt(), (int)KisCanvasResourceProvider::EraserMode); QCOMPARE(spy[1][1].toBool(), false); QCOMPARE(spy[2][0].toInt(), (int)KisCanvasResourceProvider::LodAvailability); QCOMPARE(spy[2][1].toBool(), true); - QCOMPARE(spy[3][0].toInt(), (int)KisCanvasResourceProvider::Opacity); + QCOMPARE(spy[3][0].toInt(), (int)KisCanvasResourceProvider::Size); QCOMPARE(spy[3][1].toDouble(), 1.0); - QCOMPARE(spy[4][0].toInt(), (int)KisCanvasResourceProvider::CurrentEffectiveCompositeOp); - QCOMPARE(spy[4][1].toString(), COMPOSITE_OVER); + QCOMPARE(spy[4][0].toInt(), (int)KisCanvasResourceProvider::Flow); + QCOMPARE(spy[4][1].toDouble(), 1.0); + + QCOMPARE(spy[5][0].toInt(), (int)KisCanvasResourceProvider::Opacity); + QCOMPARE(spy[5][1].toDouble(), 1.0); + + QCOMPARE(spy[6][0].toInt(), (int)KisCanvasResourceProvider::CurrentEffectiveCompositeOp); + QCOMPARE(spy[6][1].toString(), COMPOSITE_OVER); spy.clear(); preset->settings()->setPaintOpOpacity(0.8); QCOMPARE(spy.size(), 1); QCOMPARE(spy[0][0].toInt(), (int)KisCanvasResourceProvider::Opacity); QCOMPARE(spy[0][1].toDouble(), 0.8); spy.clear(); mainWindow->hide(); QApplication::processEvents(); delete view; delete doc; delete mainWindow; } QTEST_MAIN(KisDerivedResourcesTest) diff --git a/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp b/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp index 6b1617861b..192bde1e24 100644 --- a/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp +++ b/plugins/color/lcms2engine/tests/TestKoLcmsColorProfile.cpp @@ -1,214 +1,214 @@ #include "TestKoLcmsColorProfile.h" #include #include #include #include #include #include #include qreal testRounding(qreal value) { qreal factor; int temp; const int numPlaces = 3; factor = pow(10.0, numPlaces); temp = (int)(value * factor + 0.5); return temp / factor; } void TestKoLcmsColorProfile::testChromaticitiesFromProfile() { #if 0 cmsHPROFILE profile = cmsCreate_sRGBProfile(); KoLcmsRGBColorProfile::Chromaticities chromaticities = KoLcmsRGBColorProfile::chromaticitiesFromProfile(profile); const cmsCIExyY profileRed = {0.6400f, 0.3300f, 0.212656f}; const cmsCIExyY profileGreen = {0.3000f, 0.6000f, 0.715158f}; const cmsCIExyY profileBlue = {0.1500f, 0.0600f, 0.072186f}; const cmsCIExyY profileWhite = {0.3127f, 0.3290f, 1.000000f}; QCOMPARE(testRounding(chromaticities.primaries.Red.x), testRounding(profileRed.x)); QCOMPARE(testRounding(chromaticities.primaries.Red.y), testRounding(profileRed.y)); QCOMPARE(testRounding(chromaticities.primaries.Red.Y), testRounding(profileRed.Y)); QCOMPARE(testRounding(chromaticities.primaries.Green.x), testRounding(profileGreen.x)); QCOMPARE(testRounding(chromaticities.primaries.Green.y), testRounding(profileGreen.y)); QCOMPARE(testRounding(chromaticities.primaries.Green.Y), testRounding(profileGreen.Y)); QCOMPARE(testRounding(chromaticities.primaries.Blue.x), testRounding(profileBlue.x)); QCOMPARE(testRounding(chromaticities.primaries.Blue.y), testRounding(profileBlue.y)); QCOMPARE(testRounding(chromaticities.primaries.Blue.Y), testRounding(profileBlue.Y)); QCOMPARE(testRounding(chromaticities.whitePoint.x), testRounding(profileWhite.x)); QCOMPARE(testRounding(chromaticities.whitePoint.y), testRounding(profileWhite.y)); cmsCloseProfile(profile); #endif } void TestKoLcmsColorProfile::testProfileCreationFromChromaticities() { #if 0 KoLcmsRGBColorProfile::Chromaticities chromaticities; chromaticities.primaries.Red.x = 0.7347f; chromaticities.primaries.Red.y = 0.2653f; chromaticities.primaries.Red.Y = 1.0f; chromaticities.primaries.Green.x = 0.1596f; chromaticities.primaries.Green.y = 0.8404f; chromaticities.primaries.Green.Y = 1.0f; chromaticities.primaries.Blue.x = 0.0366f; chromaticities.primaries.Blue.y = 0.0001f; chromaticities.primaries.Blue.Y = 1.0f; chromaticities.whitePoint.x = 0.34567f; chromaticities.whitePoint.y = 0.35850f; chromaticities.whitePoint.Y = 1.0f; qreal gamma = 1.75f; KoLcmsRGBColorProfile *profile = new KoLcmsRGBColorProfile(chromaticities, gamma); QVERIFY(profile != 0); QCOMPARE(profile->colorSpaceSignature(), icSigRgbData); cmsHPROFILE lcmsProfile = profile->lcmsProfile(); KoLcmsRGBColorProfile::Chromaticities profileChromaticities = KoLcmsRGBColorProfile::chromaticitiesFromProfile(lcmsProfile); QCOMPARE(testRounding(profileChromaticities.primaries.Red.x), testRounding(chromaticities.primaries.Red.x)); QCOMPARE(testRounding(profileChromaticities.primaries.Red.y), testRounding(chromaticities.primaries.Red.y)); QCOMPARE(testRounding(profileChromaticities.primaries.Green.x), testRounding(chromaticities.primaries.Green.x)); QCOMPARE(testRounding(profileChromaticities.primaries.Green.y), testRounding(chromaticities.primaries.Green.y)); QCOMPARE(testRounding(profileChromaticities.primaries.Blue.x), testRounding(chromaticities.primaries.Blue.x)); QCOMPARE(testRounding(profileChromaticities.primaries.Blue.y), testRounding(chromaticities.primaries.Blue.y)); QCOMPARE(testRounding(profileChromaticities.whitePoint.x), testRounding(chromaticities.whitePoint.x)); QCOMPARE(testRounding(profileChromaticities.whitePoint.y), testRounding(chromaticities.whitePoint.y)); LPGAMMATABLE redGamma = cmsReadICCGamma(lcmsProfile, icSigRedTRCTag); LPGAMMATABLE greenGamma = cmsReadICCGamma(lcmsProfile, icSigGreenTRCTag); LPGAMMATABLE blueGamma = cmsReadICCGamma(lcmsProfile, icSigBlueTRCTag); QCOMPARE(testRounding(cmsEstimateGamma(redGamma)), gamma); QCOMPARE(testRounding(cmsEstimateGamma(greenGamma)), gamma); QCOMPARE(testRounding(cmsEstimateGamma(blueGamma)), gamma); QString expectedProfileName = QString("lcms virtual RGB profile - R(%1, %2) G(%3, %4) B(%5, %6) W(%7, %8) gamma %9") .arg(chromaticities.primaries.Red.x) .arg(chromaticities.primaries.Red.y) .arg(chromaticities.primaries.Green.x) .arg(chromaticities.primaries.Green.y) .arg(chromaticities.primaries.Blue.x) .arg(chromaticities.primaries.Blue.y) .arg(chromaticities.whitePoint.x) .arg(chromaticities.whitePoint.y) .arg(gamma); QCOMPARE(profile->name(), expectedProfileName); QCOMPARE(QString(cmsTakeProductDesc(lcmsProfile)), expectedProfileName); profileChromaticities = profile->chromaticities(); QCOMPARE(profileChromaticities.primaries.Red.x, chromaticities.primaries.Red.x); QCOMPARE(profileChromaticities.primaries.Red.y, chromaticities.primaries.Red.y); QCOMPARE(profileChromaticities.primaries.Green.x, chromaticities.primaries.Green.x); QCOMPARE(profileChromaticities.primaries.Green.y, chromaticities.primaries.Green.y); QCOMPARE(profileChromaticities.primaries.Blue.x, chromaticities.primaries.Blue.x); QCOMPARE(profileChromaticities.primaries.Blue.y, chromaticities.primaries.Blue.y); QCOMPARE(profileChromaticities.whitePoint.x, chromaticities.whitePoint.x); QCOMPARE(profileChromaticities.whitePoint.y, chromaticities.whitePoint.y); const QString testProfileName = "Test Profile Name"; profile = new KoLcmsRGBColorProfile(chromaticities, gamma, testProfileName); lcmsProfile = profile->lcmsProfile(); QCOMPARE(profile->name(), testProfileName); QCOMPARE(QString(cmsTakeProductDesc(lcmsProfile)), testProfileName); #endif } void TestKoLcmsColorProfile::testConversion() { const KoColorSpace *sRgb = KoColorSpaceRegistry::instance()->rgb16("sRGB built-in"); Q_ASSERT(sRgb); const KoColorSpace *linearRgb = KoColorSpaceRegistry::instance()->rgb16("scRGB (linear)"); Q_ASSERT(linearRgb); quint16 src[4]; src[0] = 257; src[1] = 257; src[2] = 257; src[3] = 65535; quint16 dst[4]; memset(&dst, 0, 8); linearRgb->convertPixelsTo((quint8 *)&src, (quint8 *)&dst, sRgb, 1, KoColorConversionTransformation::IntentRelativeColorimetric, KoColorConversionTransformation::BlackpointCompensation); quint16 dst2[4]; memset(&dst2, 0, 8); cmsHPROFILE sRgbProfile = cmsCreate_sRGBProfile(); QByteArray rawData = linearRgb->profile()->rawData(); cmsHPROFILE linearRgbProfile = cmsOpenProfileFromMem((void *)rawData.constData(), rawData.size()); cmsHTRANSFORM tf = cmsCreateTransform(linearRgbProfile, TYPE_BGRA_16, sRgbProfile, TYPE_BGRA_16, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE); cmsDoTransform(tf, (quint8 *)&src, (quint8 *)&dst2, 1); Q_ASSERT(dst[0] == dst2[0]); } void TestKoLcmsColorProfile::testProofingConversion() { const KoColorSpace *sRgb = KoColorSpaceRegistry::instance()->rgb16("sRGB built-in"); Q_ASSERT(sRgb); const KoColorSpace *lab = KoColorSpaceRegistry::instance()->lab16();//there's only one lab profile, replace with it's name. Q_ASSERT(lab); quint16 src[4];//the following ought to give us a purple only possible in lab. I can't seem to proof this away, somehow... src[0] = 32896; src[1] = 65535; src[2] = 0; src[3] = 65535; quint16 dst[4]; memset(&dst, 0, 8); cmsHPROFILE sRgbProfile = cmsCreate_sRGBProfile(); cmsHPROFILE LabProfile = cmsCreateLab4Profile(NULL); - quint16 alarm[4];//cyan! + quint16 alarm[cmsMAXCHANNELS]={0}; alarm[0] = 65535; alarm[1] = 0; alarm[2] = 0; alarm[3] = 65535; cmsSetAlarmCodes(alarm); cmsHTRANSFORM tf = cmsCreateProofingTransform(LabProfile, TYPE_Lab_16, LabProfile, TYPE_Lab_16, sRgbProfile, INTENT_ABSOLUTE_COLORIMETRIC, INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK); cmsDoTransform(tf, (quint8 *)&src, (quint8 *)&dst, 1); qDebug()<