void KisConvolutionPainterTest::testSymmConvolution() { qreal offset = 0.0; qreal factor = 1.0; Eigen::Matrix<qreal, 3, 3> 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; Eigen::Matrix<qreal, 3, 3> 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 checkReadWriteRoundTrip(KisPaintDeviceSP dev, const QRect &rc) { KisPaintDeviceSP deviceCopy = new KisPaintDevice(*dev.data()); QRect readRect(10, 10, 20, 20); int bufSize = rc.width() * rc.height() * dev->pixelSize(); QScopedPointer<quint8> buf1(new quint8[bufSize]); deviceCopy->readBytes(buf1.data(), rc); deviceCopy->clear(); QVERIFY(deviceCopy->extent().isEmpty()); QScopedPointer<quint8> buf2(new quint8[bufSize]); deviceCopy->writeBytes(buf1.data(), rc); deviceCopy->readBytes(buf2.data(), rc); QVERIFY(!memcmp(buf1.data(), buf2.data(), bufSize)); }
void KisSobelFilter::prepareRow(KisPaintDeviceSP src, quint8* data, quint32 x, quint32 y, quint32 w, quint32 h) const { if (y > h - 1) y = h - 1; quint32 pixelSize = src->pixelSize(); src->readBytes(data, x, y, w, 1); for (quint32 b = 0; b < pixelSize; b++) { int offset = pixelSize - b; data[-offset] = data[b]; data[w * pixelSize + b] = data[(w - 1) * pixelSize + b]; } }
//#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 KisBidirectionalMixingOption::applyFixed(KisFixedPaintDeviceSP dab, KisPaintDeviceSP device, KisPainter* painter, qint32 sx, qint32 sy, qint32 sw, qint32 sh, quint8 pressure, const QRect& dstRect) { if (!isChecked()) return; KisFixedPaintDevice canvas(device->colorSpace()); canvas.setRect(QRect(dstRect.x(), dstRect.y(), sw, sh)); canvas.initialize(); device->readBytes(canvas.data(), canvas.bounds()); const KoColorSpace* cs = dab->colorSpace(); int channelCount = cs->channelCount(); quint8* dabPointer = dab->data(); quint8* canvasPointer = canvas.data(); QVector<float> cc(channelCount ), dc(channelCount ); for (int y = 0; y < sh; y++) { for (int x = 0; x < sw; x++) { if (cs->alpha(dabPointer) > 10 && cs->alpha(canvasPointer) > 10) { cs->normalisedChannelsValue(canvasPointer, cc); cs->normalisedChannelsValue(dabPointer, dc); for (int i = 0; i < channelCount ; i++) { dc[i] = (1.0 - 0.4 * pressure) * cc[i] + 0.4 * pressure * dc[i]; } cs->fromNormalisedChannelsValue(dabPointer, dc); if (x == (int)(sw / 2) && y == (int)(sh / 2)) painter->setPaintColor(KoColor(dabPointer, cs)); } } dabPointer += dab->pixelSize(); canvasPointer += canvas.pixelSize(); } }
void KisPaintDeviceTest::testRoundtripReadWrite() { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = new KisPaintDevice(cs); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png"); dev->convertFromQImage(image, 0); quint8* bytes = new quint8[cs->pixelSize() * image.width() * image.height()]; memset(bytes, 0, image.width() * image.height() * dev->pixelSize()); dev->readBytes(bytes, image.rect()); KisPaintDeviceSP dev2 = new KisPaintDevice(cs); dev2->writeBytes(bytes, image.rect()); QVERIFY(dev2->exactBounds() == image.rect()); dev2->convertToQImage(0, 0, 0, image.width(), image.height()).save("readwrite.png"); QPoint pt; if (!TestUtil::comparePaintDevices(pt, dev, dev2)) { QFAIL(QString("Failed round trip using readBytes and writeBytes, first different pixel: %1,%2 ").arg(pt.x()).arg(pt.y()).toLatin1()); } }
void KisImagePyramid::retrieveImageData(const QRect &rect) { // XXX: use QThreadStorage to cache the two patches (512x512) of pixels. Note // that when we do that, we need to reset that cache when the projection's // colorspace changes. const KoColorSpace *projectionCs = m_originalImage->projection()->colorSpace(); KisPaintDeviceSP originalProjection = m_originalImage->projection(); quint32 numPixels = rect.width() * rect.height(); QScopedArrayPointer<quint8> originalBytes( new quint8[originalProjection->colorSpace()->pixelSize() * numPixels]); originalProjection->readBytes(originalBytes.data(), rect); if (m_displayFilter && m_useOcio && projectionCs->colorModelId() == RGBAColorModelID) { #ifdef HAVE_OCIO const KoColorProfile *destinationProfile = m_displayFilter->useInternalColorManagement() ? m_monitorProfile : projectionCs->profile(); const KoColorSpace *floatCs = KoColorSpaceRegistry::instance()->colorSpace( RGBAColorModelID.id(), Float32BitsColorDepthID.id(), destinationProfile); const KoColorSpace *modifiedMonitorCs = KoColorSpaceRegistry::instance()->colorSpace( RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), destinationProfile); if (projectionCs->colorDepthId() == Float32BitsColorDepthID) { m_displayFilter->filter(originalBytes.data(), numPixels); } else { QScopedArrayPointer<quint8> dst(new quint8[floatCs->pixelSize() * numPixels]); projectionCs->convertPixelsTo(originalBytes.data(), dst.data(), floatCs, numPixels, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags); m_displayFilter->filter(dst.data(), numPixels); originalBytes.swap(dst); } { QScopedArrayPointer<quint8> dst(new quint8[modifiedMonitorCs->pixelSize() * numPixels]); floatCs->convertPixelsTo(originalBytes.data(), dst.data(), modifiedMonitorCs, numPixels, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags); originalBytes.swap(dst); } #endif } else { QList<KoChannelInfo*> channelInfo = projectionCs->channels(); if (!m_channelFlags.size() == channelInfo.size()) { setChannelFlags(QBitArray()); } if (!m_channelFlags.isEmpty() && !m_allChannelsSelected) { QScopedArrayPointer<quint8> dst(new quint8[projectionCs->pixelSize() * numPixels]); int channelSize = channelInfo[m_selectedChannelIndex]->size(); int pixelSize = projectionCs->pixelSize(); KisConfig cfg; if (m_onlyOneChannelSelected && !cfg.showSingleChannelAsColor()) { int selectedChannelPos = channelInfo[m_selectedChannelIndex]->pos(); for (uint pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) { for (uint channelIndex = 0; channelIndex < projectionCs->channelCount(); ++channelIndex) { if (channelInfo[channelIndex]->channelType() == KoChannelInfo::COLOR) { memcpy(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), originalBytes.data() + (pixelIndex * pixelSize) + selectedChannelPos, channelSize); } else if (channelInfo[channelIndex]->channelType() == KoChannelInfo::ALPHA) { memcpy(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), originalBytes.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), channelSize); } } } } else { for (uint pixelIndex = 0; pixelIndex < numPixels; ++pixelIndex) { for (uint channelIndex = 0; channelIndex < projectionCs->channelCount(); ++channelIndex) { if (m_channelFlags.testBit(channelIndex)) { memcpy(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), originalBytes.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), channelSize); } else { memset(dst.data() + (pixelIndex * pixelSize) + (channelIndex * channelSize), 0, channelSize); } } } } originalBytes.swap(dst); } QScopedArrayPointer<quint8> dst(new quint8[m_monitorColorSpace->pixelSize() * numPixels]); projectionCs->convertPixelsTo(originalBytes.data(), dst.data(), m_monitorColorSpace, numPixels, m_renderingIntent, m_conversionFlags); originalBytes.swap(dst); } m_pyramid[ORIGINAL_INDEX]->writeBytes(originalBytes.data(), rect); }
void KisPaintDeviceTest::testReadBytesWrapAround() { const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KisPaintDeviceSP dev = createWrapAroundPaintDevice(cs); KoColor c1(Qt::red, cs); KoColor c2(Qt::green, cs); dev->setPixel(3, 3, c1); dev->setPixel(18, 18, c2); const int pixelSize = dev->pixelSize(); { QRect readRect(10, 10, 20, 20); QScopedPointer<quint8> buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final1.png"); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check weird case when the read rect is larger than wrap rect QRect readRect(10, 10, 30, 30); QScopedPointer<quint8> buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final2.png"); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // even more large QRect readRect(10, 10, 40, 40); QScopedPointer<quint8> buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final3.png"); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 7) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 8) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (7 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (8 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (27 + readRect.width() * 27) * pixelSize, c2.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (28 + readRect.width() * 28) * pixelSize, c2.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 12) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 13) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (12 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (13 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (32 + readRect.width() * 32) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (33 + readRect.width() * 33) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check if the wrap rect contains the read rect entirely QRect readRect(1, 1, 10, 10); QScopedPointer<quint8> buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final4.png"); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check if the wrap happens only on vertical side of the rect QRect readRect(1, 1, 29, 10); QScopedPointer<quint8> buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final5.png"); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (21 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (22 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } { // check if the wrap happens only on horizontal side of the rect QRect readRect(1, 1, 10, 29); QScopedPointer<quint8> buf(new quint8[readRect.width() * readRect.height() * pixelSize]); dev->readBytes(buf.data(), readRect); //dev->convertToQImage(0, readRect.x(), readRect.y(), readRect.width(), readRect.height()).save("final6.png"); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 1) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 2) * pixelSize, c1.data(), pixelSize)); QVERIFY(memcmp(buf.data() + (1 + readRect.width() * 21) * pixelSize, c1.data(), pixelSize)); QVERIFY(!memcmp(buf.data() + (2 + readRect.width() * 22) * pixelSize, c1.data(), pixelSize)); checkReadWriteRoundTrip(dev, readRect); } }