File indexing completed on 2024-06-09 04:22:15

0001 /*
0002  *  SPDX-FileCopyrightText: 2007 Sven Langkamp <sven.langkamp@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_selection_test.h"
0008 #include <simpletest.h>
0009 
0010 
0011 #include <kis_debug.h>
0012 #include <QRect>
0013 
0014 #include <KoColorSpace.h>
0015 #include <KoColorSpaceRegistry.h>
0016 #include <KoCompositeOpRegistry.h>
0017 
0018 #include "kis_datamanager.h"
0019 #include "kis_pixel_selection.h"
0020 #include "kis_selection.h"
0021 #include "kis_default_bounds.h"
0022 #include "KisImageResolutionProxy.h"
0023 #include "kis_fill_painter.h"
0024 #include "kis_mask.h"
0025 #include "kis_image.h"
0026 #include "kis_transparency_mask.h"
0027 #include <testutil.h>
0028 #include "testimage.h"
0029 #include <KoColorModelStandardIds.h>
0030 
0031 void KisSelectionTest::testGrayColorspaceConversion()
0032 {
0033     const KoColorSpace *csA =
0034         KoColorSpaceRegistry::instance()->
0035         colorSpace(GrayAColorModelID.id(),
0036                    Integer8BitsColorDepthID.id(),
0037                    QString());
0038 
0039     const KoColorSpace *csNoA =
0040         KoColorSpaceRegistry::instance()->alpha8();
0041 
0042     QVERIFY(csA);
0043     QVERIFY(csNoA);
0044 
0045     QCOMPARE(csA->pixelSize(), 2U);
0046     QCOMPARE(csNoA->pixelSize(), 1U);
0047 
0048     quint8 color1[1] = {128};
0049     quint8 color2[2] = {64,32};
0050 
0051     csA->convertPixelsTo(color2, color1, csNoA, 1,
0052                          KoColorConversionTransformation::internalRenderingIntent(),
0053                          KoColorConversionTransformation::internalConversionFlags());
0054 
0055     QCOMPARE((int)color1[0], 8);
0056 
0057     csNoA->convertPixelsTo(color1, color2, csA, 1,
0058                            KoColorConversionTransformation::internalRenderingIntent(),
0059                            KoColorConversionTransformation::internalConversionFlags());
0060 
0061     QCOMPARE((int)color2[0], 8);
0062     QCOMPARE((int)color2[1], 255);
0063 }
0064 
0065 void KisSelectionTest::testGrayColorspaceOverComposition()
0066 {
0067     const KoColorSpace *csA =
0068         KoColorSpaceRegistry::instance()->
0069         colorSpace(GrayAColorModelID.id(),
0070                    Integer8BitsColorDepthID.id(),
0071                    QString());
0072     const KoColorSpace *csNoA =
0073         KoColorSpaceRegistry::instance()->alpha8();
0074 
0075     QVERIFY(csA);
0076     QVERIFY(csNoA);
0077 
0078     QCOMPARE(csA->pixelSize(), 2U);
0079     QCOMPARE(csNoA->pixelSize(), 1U);
0080 
0081     quint8 color0[2] = {32,255};
0082     quint8 color1[2] = {128,64};
0083     quint8 color3[1] = {32};
0084 
0085     KoCompositeOp::ParameterInfo params;
0086     params.dstRowStart = color0;
0087     params.dstRowStride = 0;
0088     params.srcRowStart = color1;
0089     params.srcRowStride = 0;
0090     params.maskRowStart = 0;
0091     params.maskRowStride = 0;
0092     params.rows = 1;
0093     params.cols = 1;
0094     params.opacity = 1.0;
0095     params.flow = 1.0;
0096 
0097     csA->bitBlt(csA, params, csA->compositeOp(COMPOSITE_OVER),
0098                 KoColorConversionTransformation::internalRenderingIntent(),
0099                 KoColorConversionTransformation::internalConversionFlags());
0100 
0101     QCOMPARE((int)color0[0], 56);
0102     QCOMPARE((int)color0[1], 255);
0103 
0104     params.dstRowStart = color3;
0105 
0106     csNoA->bitBlt(csA, params, csNoA->compositeOp(COMPOSITE_OVER),
0107                   KoColorConversionTransformation::internalRenderingIntent(),
0108                   KoColorConversionTransformation::internalConversionFlags());
0109 
0110     QCOMPARE((int)color3[0], 56);
0111 }
0112 
0113 void KisSelectionTest::testSelectionComponents()
0114 {
0115     KisSelectionSP selection = new KisSelection();
0116 
0117     QCOMPARE(selection->hasNonEmptyPixelSelection(), false);
0118     QCOMPARE(selection->hasNonEmptyShapeSelection(), false);
0119     QCOMPARE(selection->shapeSelection(), (void*)0);
0120 
0121     selection->pixelSelection()->select(QRect(10,10,10,10));
0122     QCOMPARE(selection->hasNonEmptyPixelSelection(), true);
0123     QCOMPARE(selection->selectedExactRect(), QRect(10,10,10,10));
0124 }
0125 
0126 void KisSelectionTest::testSelectionActions()
0127 {
0128     KisSelectionSP selection = new KisSelection();
0129     QVERIFY(selection->hasNonEmptyPixelSelection() == false);
0130     QVERIFY(selection->hasNonEmptyShapeSelection() == false);
0131 
0132     KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0133     pixelSelection->select(QRect(0, 0, 20, 20));
0134 
0135     KisPixelSelectionSP tmpSel = new KisPixelSelection();
0136     tmpSel->select(QRect(10, 0, 20, 20));
0137 
0138     pixelSelection->applySelection(tmpSel, SELECTION_ADD);
0139     QCOMPARE(pixelSelection->selectedExactRect(), QRect(0, 0, 30, 20));
0140     QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 30, 20));
0141 
0142     pixelSelection->clear();
0143     pixelSelection->select(QRect(0, 0, 20, 20));
0144 
0145     pixelSelection->applySelection(tmpSel, SELECTION_SUBTRACT);
0146     QCOMPARE(pixelSelection->selectedExactRect(), QRect(0, 0, 10, 20));
0147     QCOMPARE(selection->selectedExactRect(), QRect(0, 0, 10, 20));
0148 
0149     pixelSelection->clear();
0150     pixelSelection->select(QRect(0, 0, 20, 20));
0151 
0152     pixelSelection->applySelection(tmpSel, SELECTION_INTERSECT);
0153     QCOMPARE(pixelSelection->selectedExactRect(), QRect(10, 0, 10, 20));
0154     QCOMPARE(selection->selectedExactRect(), QRect(10, 0, 10, 20));
0155 }
0156 
0157 void KisSelectionTest::testInvertSelection()
0158 {
0159     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0160     KisImageSP image = new KisImage(0, 1024, 1024, cs, "stest");
0161 
0162     KisSelectionSP selection = new KisSelection(new KisDefaultBounds(image), toQShared(new KisImageResolutionProxy(image)));
0163     KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0164     pixelSelection->select(QRect(20, 20, 20, 20));
0165 
0166     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 30, 30), MAX_SELECTED);
0167     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MIN_SELECTED);
0168     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 512, 512), MIN_SELECTED);
0169     QCOMPARE(pixelSelection->selectedExactRect(), QRect(20, 20, 20, 20));
0170 
0171     pixelSelection->invert();
0172 
0173     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 100, 100), MAX_SELECTED);
0174     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 22, 22), MIN_SELECTED);
0175     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MAX_SELECTED);
0176     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 512, 512), MAX_SELECTED);
0177     QCOMPARE(pixelSelection->selectedExactRect(), QRect(0,0,1024,1024));
0178     QCOMPARE(pixelSelection->selectedRect(), QRect(0,0,1024,1024));
0179 
0180     selection->updateProjection();
0181 
0182     QCOMPARE(selection->selectedExactRect(), QRect(0,0,1024,1024));
0183     QCOMPARE(selection->selectedRect(), QRect(0,0,1024,1024));
0184 
0185     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 100, 100), MAX_SELECTED);
0186     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 22, 22), MIN_SELECTED);
0187     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 10, 10), MAX_SELECTED);
0188     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 0, 0), MAX_SELECTED);
0189     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 512, 512), MAX_SELECTED);
0190 }
0191 
0192 void KisSelectionTest::testInvertSelectionSemi()
0193 {
0194     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0195     KisImageSP image = new KisImage(0, 1024, 1024, cs, "stest");
0196 
0197     KisSelectionSP selection = new KisSelection(new KisDefaultBounds(image), toQShared(new KisImageResolutionProxy(image)));
0198     KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0199     quint8 selectedness = 42;
0200     pixelSelection->select(QRect(20, 20, 20, 20), selectedness);
0201 
0202     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 30, 30), selectedness);
0203     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MIN_SELECTED);
0204     QCOMPARE(pixelSelection->selectedExactRect(), QRect(20, 20, 20, 20));
0205 
0206     pixelSelection->invert();
0207 
0208     quint8 invertedSelectedness = MAX_SELECTED - selectedness;
0209     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 30, 30), invertedSelectedness);
0210     QCOMPARE(TestUtil::alphaDevicePixel(pixelSelection, 0, 0), MAX_SELECTED);
0211     QCOMPARE(pixelSelection->selectedExactRect(), QRect(0,0,1024,1024));
0212     QCOMPARE(pixelSelection->selectedRect(), QRect(0,0,1024,1024));
0213 
0214     selection->updateProjection();
0215 
0216     QCOMPARE(selection->selectedExactRect(), QRect(0,0,1024,1024));
0217     QCOMPARE(selection->selectedRect(), QRect(0,0,1024,1024));
0218 
0219     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 30, 30), invertedSelectedness);
0220     QCOMPARE(TestUtil::alphaDevicePixel(selection->projection(), 0, 0), MAX_SELECTED);
0221 }
0222 
0223 void KisSelectionTest::testCopy()
0224 {
0225     KisSelectionSP sel = new KisSelection();
0226     sel->pixelSelection()->select(QRect(10, 10, 200, 200), 128);
0227 
0228     sel->updateProjection();
0229 
0230     KisSelectionSP sel2 = new KisSelection(*sel.data());
0231     QCOMPARE(sel2->selectedExactRect(), sel->selectedExactRect());
0232 
0233     QPoint errpoint;
0234     if (!TestUtil::comparePaintDevices(errpoint, sel->projection(), sel2->projection())) {
0235         sel2->projection()->convertToQImage(0, 0, 0, 200, 200).save("merge_visitor6.png");
0236         QFAIL(QString("Failed to copy selection, first different pixel: %1,%2 ")
0237               .arg(errpoint.x())
0238               .arg(errpoint.y())
0239               .toLatin1());
0240     }
0241 }
0242 
0243 void KisSelectionTest::testSelectionExactBounds()
0244 {
0245     QRect referenceImageRect(0,0,1000,1000);
0246     QRect referenceDeviceRect(100,100,1040,1040);
0247 
0248     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0249 
0250     KisImageSP image = new KisImage(0, referenceImageRect.width(),
0251                                     referenceImageRect.height(),
0252                                     cs, "stest");
0253 
0254     KisPaintDeviceSP device = new KisPaintDevice(cs);
0255     device->setDefaultBounds(new KisDefaultBounds(image));
0256     device->fill(referenceDeviceRect, KoColor(Qt::white, cs));
0257 
0258     QCOMPARE(device->exactBounds(), referenceDeviceRect);
0259 
0260     KisSelectionSP selection = new KisSelection(new KisSelectionDefaultBounds(device), toQShared(new KisImageResolutionProxy(image)));
0261 
0262     quint8 defaultPixel = MAX_SELECTED;
0263     selection->pixelSelection()->setDefaultPixel(KoColor(&defaultPixel, selection->pixelSelection()->colorSpace()));
0264 
0265     // the selection uses device's extent only for performance reasons
0266     // \see bug 320213
0267     QCOMPARE(selection->selectedExactRect(), device->extent() | referenceImageRect);
0268 }
0269 
0270 void KisSelectionTest::testSetParentNodeAfterCreation()
0271 {
0272     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0273     KisImageSP image = new KisImage(0, 100, 100, cs, "stest");
0274     KisSelectionSP selection = new KisSelection();
0275     KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0276 
0277     QCOMPARE(selection->parentNode(), KisNodeWSP(0));
0278     QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(0));
0279 
0280     selection->setParentNode(image->root());
0281 
0282     QCOMPARE(selection->parentNode(), KisNodeWSP(image->root()));
0283     QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(image->root()));
0284 }
0285 
0286 void KisSelectionTest::testSetParentNodeBeforeCreation()
0287 {
0288     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
0289     KisImageSP image = new KisImage(0, 100, 100, cs, "stest");
0290     KisSelectionSP selection = new KisSelection();
0291 
0292     selection->setParentNode(image->root());
0293 
0294     KisPixelSelectionSP pixelSelection = selection->pixelSelection();
0295 
0296     QCOMPARE(selection->parentNode(), KisNodeWSP(image->root()));
0297     QCOMPARE(selection->pixelSelection()->parentNode(), KisNodeWSP(image->root()));
0298 }
0299 
0300 void KisSelectionTest::testOutlineGeneration()
0301 {
0302     KisSelectionSP sel = new KisSelection();
0303     sel->pixelSelection()->select(QRect(428,436, 430,211), 128);
0304 
0305     QVERIFY(sel->outlineCacheValid());
0306 
0307     QPainterPath originalOutline = sel->outlineCache();
0308 
0309     sel->pixelSelection()->invalidateOutlineCache();
0310     sel->recalculateOutlineCache();
0311 
0312     QPainterPath calculatedOutline = sel->outlineCache();
0313 
0314     QPainterPath closedSubPath = calculatedOutline;
0315     closedSubPath.closeSubpath();
0316 
0317     /**
0318      * Our outline generation code has a small problem: it can
0319      * generate a polygon, which isn't closed (it'll repeat the first
0320      * point instead). There is a special workaround for it in
0321      * KisPixelSelection::recalculateOutlineCache(), which explicitly
0322      * closes the path, so here we just check it.
0323      */
0324 
0325     bool isClosed = closedSubPath == calculatedOutline;
0326     QVERIFY(isClosed);
0327 }
0328 
0329 KISTEST_MAIN(KisSelectionTest)
0330 
0331