Example #1
0
void KisPaintDeviceTest::testMakeClone()
{
    QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png");

    const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
    KisPaintDeviceSP srcDev = new KisPaintDevice(cs);
    srcDev->convertFromQImage(image, 0);
    srcDev->move(10,10);

    const KoColorSpace * weirdCS = KoColorSpaceRegistry::instance()->lab16();
    KisPaintDeviceSP dstDev = new KisPaintDevice(weirdCS);
    dstDev->move(1000,1000);

    QVERIFY(!dstDev->fastBitBltPossible(srcDev));

    QRect cloneRect(100,100,200,200);
    QPoint errpoint;

    dstDev->makeCloneFrom(srcDev, cloneRect);

    QVERIFY(*dstDev->colorSpace() == *srcDev->colorSpace());
    QCOMPARE(dstDev->pixelSize(), srcDev->pixelSize());
    QCOMPARE(dstDev->x(), srcDev->x());
    QCOMPARE(dstDev->y(), srcDev->y());
    QCOMPARE(dstDev->exactBounds(), cloneRect);

    QImage srcImage = srcDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
                                              cloneRect.width(), cloneRect.height());
    QImage dstImage = dstDev->convertToQImage(0, cloneRect.x(), cloneRect.y(),
                                              cloneRect.width(), cloneRect.height());
    if (!TestUtil::compareQImages(errpoint, dstImage, srcImage)) {
        QFAIL(QString("Failed to create identical image, first different pixel: %1,%2 \n").arg(errpoint.x()).arg(errpoint.y()).toLatin1());
    }
}
Example #2
0
void KisPaintDeviceTest::testCreation()
{
    const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
    KisPaintDeviceSP dev = new KisPaintDevice(cs);
    QVERIFY(dev->objectName().isEmpty());

    dev = new KisPaintDevice(cs);
    QVERIFY(*dev->colorSpace() == *cs);
    QVERIFY(dev->x() == 0);
    QVERIFY(dev->y() == 0);
    QVERIFY(dev->pixelSize() == cs->pixelSize());
    QVERIFY(dev->channelCount() == cs->channelCount());
    QVERIFY(dev->dataManager() != 0);

    KisImageSP image = new KisImage(0, 1000, 1000, cs, "merge test");
    KisPaintLayerSP layer = new KisPaintLayer(image, "bla", 125);

    dev = new KisPaintDevice(layer.data(), cs);
    QVERIFY(*dev->colorSpace() == *cs);
    QVERIFY(dev->x() == 0);
    QVERIFY(dev->y() == 0);
    QVERIFY(dev->pixelSize() == cs->pixelSize());
    QVERIFY(dev->channelCount() == cs->channelCount());
    QVERIFY(dev->dataManager() != 0);

    // Let the layer go out of scope and see what happens
    {
        KisPaintLayerSP l2 = new KisPaintLayer(image, "blabla", 250);
        dev = new KisPaintDevice(l2.data(), cs);
    }

}
Example #3
0
void KisMultiwayCut::Private::maskOutKeyStroke(KisPaintDeviceSP keyStrokeDevice, KisPaintDeviceSP mask, const QRect &boundingRect)
{
    KIS_ASSERT_RECOVER_RETURN(keyStrokeDevice->pixelSize() == 1);
    KIS_ASSERT_RECOVER_RETURN(mask->pixelSize() == 1);

    QRegion region =
        keyStrokeDevice->region() &
        mask->exactBounds() & boundingRect;

    Q_FOREACH (const QRect &rc, region.rects()) {
        KisSequentialIterator dstIt(keyStrokeDevice, rc);
        KisSequentialConstIterator mskIt(mask, rc);

        do {
            if (*mskIt.rawDataConst() > 0) {
                *dstIt.rawData() = 0;
            }
        } while (dstIt.nextPixel() && mskIt.nextPixel());
    }
}
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];
    }
}
Example #5
0
void KisPaintDeviceTest::testColorSpaceConversion()
{
    QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "tile.png");
    const KoColorSpace* srcCs = KoColorSpaceRegistry::instance()->rgb8();
    const KoColorSpace* dstCs = KoColorSpaceRegistry::instance()->lab16();
    KisPaintDeviceSP dev = new KisPaintDevice(srcCs);
    dev->convertFromQImage(image, 0);
    dev->move(10, 10);   // Unalign with tile boundaries
    KUndo2Command* cmd = dev->convertTo(dstCs);

    QCOMPARE(dev->exactBounds(), QRect(10, 10, image.width(), image.height()));
    QCOMPARE(dev->pixelSize(), dstCs->pixelSize());
    QVERIFY(*dev->colorSpace() == *dstCs);

    delete cmd;
}
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;
}
Example #7
0
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));
}
Example #8
0
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 KisPixelizeFilter::process(KisPaintDeviceSP device,
                         const QRect& applyRect,
                         const KisFilterConfiguration* configuration,
                         KoUpdater* progressUpdater
                               ) const
{
    QPoint srcTopLeft = applyRect.topLeft();
    Q_ASSERT(device);
    Q_ASSERT(configuration);

    qint32 width = applyRect.width();
    qint32 height = applyRect.height();

    //read the filter configuration values from the KisFilterConfiguration object
    quint32 pixelWidth = configuration->getInt("pixelWidth", 10);
    quint32 pixelHeight = configuration->getInt("pixelHeight", 10);
    if (pixelWidth == 0) pixelWidth = 1;
    if (pixelHeight == 0) pixelHeight = 1;

    qint32 pixelSize = device->pixelSize();
    QVector<qint32> average(pixelSize);

    qint32 count;

    if (progressUpdater) {
        progressUpdater->setRange(0, applyRect.width() * applyRect.height());
    }

    qint32 numberOfPixelsProcessed = 0;

    for (qint32 y = 0; y < height; y += pixelHeight - (y % pixelHeight)) {
        qint32 h = pixelHeight;
        h = qMin(h, height - y);

        for (qint32 x = 0; x < width; x += pixelWidth - (x % pixelWidth)) {
            qint32 w = pixelWidth;
            w = qMin(w, width - x);

            for (qint32 i = 0; i < pixelSize; i++) {
                average[i] = 0;
            }
            count = 0;

            //read
            KisRectConstIteratorSP srcIt = device->createRectConstIteratorNG(srcTopLeft.x() + x, srcTopLeft.y() + y, w, h);
            do {
                for (qint32 i = 0; i < pixelSize; i++) {
                    average[i] += srcIt->oldRawData()[i];
                }
                count++;
            } while (srcIt->nextPixel());

            //average
            if (count > 0) {
                for (qint32 i = 0; i < pixelSize; i++)
                    average[i] /= count;
            }
            //write
            KisRectIteratorSP dstIt = device->createRectIteratorNG(srcTopLeft.x() + x, srcTopLeft.y() + y, w, h);
            do {
                for (int i = 0; i < pixelSize; i++) {
                    dstIt->rawData()[i] = average[i];
                }
            } while (dstIt->nextPixel());
            if (progressUpdater) progressUpdater->setValue(++numberOfPixelsProcessed);
        }
    }
}
Example #10
0
void KisPaintDeviceTest::testWrappedRandomAccessor()
{
    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();

    int x;
    int y;

    x = 3;
    y = 3;
    KisRandomAccessorSP dstIt = dev->createRandomAccessorNG(x, y);

    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);

    x = 23;
    y = 23;
    dstIt->moveTo(x, y);
    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);

    x = 3;
    y = 23;
    dstIt->moveTo(x, y);
    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);

    x = 23;
    y = 3;
    dstIt->moveTo(x, y);
    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);

    x = -17;
    y = 3;
    dstIt->moveTo(x, y);
    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);

    x = 3;
    y = -17;
    dstIt->moveTo(x, y);
    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);

    x = -17;
    y = -17;
    dstIt->moveTo(x, y);
    QVERIFY(!memcmp(dstIt->rawData(), c1.data(), pixelSize));
    QCOMPARE(dstIt->numContiguousColumns(x), 17);
    QCOMPARE(dstIt->numContiguousRows(y), 17);
}
Example #11
0
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);
    }
}
Example #12
0
void cutOneWay(const KoColor &color,
               KisPaintDeviceSP src,
               KisPaintDeviceSP colorScribble,
               KisPaintDeviceSP backgroundScribble,
               KisPaintDeviceSP resultDevice,
               KisPaintDeviceSP maskDevice,
               const QRect &boundingRect)
{
    using namespace boost;

    KIS_ASSERT_RECOVER_RETURN(src->pixelSize() == 1);
    KIS_ASSERT_RECOVER_RETURN(colorScribble->pixelSize() == 1);
    KIS_ASSERT_RECOVER_RETURN(backgroundScribble->pixelSize() == 1);
    KIS_ASSERT_RECOVER_RETURN(maskDevice->pixelSize() == 1);
    KIS_ASSERT_RECOVER_RETURN(*resultDevice->colorSpace() == *color.colorSpace());

    KisLazyFillCapacityMap capacityMap(src, colorScribble, backgroundScribble, maskDevice, boundingRect);
    KisLazyFillGraph &graph = capacityMap.graph();

    std::vector<default_color_type> groups(num_vertices(graph));
    std::vector<int> residual_capacity(num_edges(graph), 0);

    std::vector<typename graph_traits<KisLazyFillGraph>::vertices_size_type> distance_vec(num_vertices(graph), 0);
    std::vector<typename graph_traits<KisLazyFillGraph>::edge_descriptor> predecessor_vec(num_vertices(graph));

    auto vertexIndexMap = get(boost::vertex_index, graph);

    typedef KisLazyFillGraph::vertex_descriptor Vertex;

    Vertex s(Vertex::LABEL_A);
    Vertex t(Vertex::LABEL_B);

    float maxFlow =
        boykov_kolmogorov_max_flow(graph,
                                   capacityMap,
                                   make_iterator_property_map(&residual_capacity[0], get(boost::edge_index, graph)),
                                   get(boost::edge_reverse, graph),
                                   make_iterator_property_map(&predecessor_vec[0], vertexIndexMap),
                                   make_iterator_property_map(&groups[0], vertexIndexMap),
                                   make_iterator_property_map(&distance_vec[0], vertexIndexMap),
                                   vertexIndexMap,
                                   s,
                                   t);
    Q_UNUSED(maxFlow);

    KisSequentialIterator dstIt(resultDevice, graph.rect());
    KisSequentialIterator mskIt(maskDevice, graph.rect());

    const int pixelSize = resultDevice->pixelSize();

    do {
        KisLazyFillGraph::vertex_descriptor v(dstIt.x(), dstIt.y());
        long vertex_idx = get(boost::vertex_index, graph, v);
        default_color_type label = groups[vertex_idx];

        if (label == black_color) {
            memcpy(dstIt.rawData(), color.data(), pixelSize);
            *mskIt.rawData() = 10 + (int(label) << 4);
        }
    } while (dstIt.nextPixel() && mskIt.nextPixel());
}
Example #13
0
bool KisTIFFWriterVisitor::saveLayerProjection(KisLayer *layer)
{
    dbgFile << "visiting on layer" << layer->name() << "";
    KisPaintDeviceSP pd = layer->projection();
    // Save depth
    int depth = 8 * pd->pixelSize() / pd->channelCount();
    TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth);
    // Save number of samples
    if (m_options->alpha) {
        TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount());
        uint16 sampleinfo[1] = { EXTRASAMPLE_UNASSALPHA };
        TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo);
    } else {
        TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount() - 1);
        TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0);
    }
    // Save colorspace information
    uint16 color_type;
    uint16 sample_format = SAMPLEFORMAT_UINT;
    if (!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format)) { // unsupported colorspace
        return false;
    }
    TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type);
    TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format);
    TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width());
    TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height());

    // Set the compression options
    TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType);
    TIFFSetField(image(), TIFFTAG_FAXMODE, m_options->faxMode);
    TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality);
    TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress);
    TIFFSetField(image(), TIFFTAG_PIXARLOGQUALITY, m_options->pixarLogCompress);

    // Set the predictor
    TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor);

    // Use contiguous configuration
    TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    // Use 8 rows per strip
    TIFFSetField(image(), TIFFTAG_ROWSPERSTRIP, 8);

    // Save profile
    if (m_options->saveProfile) {
        const KoColorProfile* profile = pd->colorSpace()->profile();
        if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) {
            QByteArray ba = profile->rawData();
            TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(), ba.constData());
        }
    }
    tsize_t stripsize = TIFFStripSize(image());
    tdata_t buff = _TIFFmalloc(stripsize);
    qint32 height = layer->image()->height();
    qint32 width = layer->image()->width();
    bool r = true;
    for (int y = 0; y < height; y++) {
        KisHLineConstIteratorSP it = pd->createHLineConstIteratorNG(0, y, width);
        switch (color_type) {
        case PHOTOMETRIC_MINISBLACK: {
            quint8 poses[] = { 0, 1 };
            r = copyDataToStrips(it, buff, depth, sample_format, 1, poses);
        }
        break;
        case PHOTOMETRIC_RGB: {
            quint8 poses[4];
            if (sample_format == SAMPLEFORMAT_IEEEFP) {
                poses[2] = 2;
                poses[1] = 1;
                poses[0] = 0;
                poses[3] = 3;
            } else {
                poses[0] = 2;
                poses[1] = 1;
                poses[2] = 0;
                poses[3] = 3;
            }
            r = copyDataToStrips(it, buff, depth, sample_format, 3, poses);
        }
        break;
        case PHOTOMETRIC_SEPARATED: {
            quint8 poses[] = { 0, 1, 2, 3, 4 };
            r = copyDataToStrips(it, buff, depth, sample_format, 4, poses);
        }
        break;
        case PHOTOMETRIC_ICCLAB: {
            quint8 poses[] = { 0, 1, 2, 3 };
            r = copyDataToStrips(it, buff, depth, sample_format, 3, poses);
        }
        break;
        return false;
        }
        if (!r) return false;
        TIFFWriteScanline(image(), buff, y, (tsample_t) - 1);
    }
    _TIFFfree(buff);
    TIFFWriteDirectory(image());
    return true;
}
void KisCubismFilter::cubism(KisPaintDeviceSP src,
                             const QPoint& srcTopLeft,
                             KisPaintDeviceSP dst,
                             const QPoint& dstTopLeft,
                             const QSize& size,
                             quint32 tileSize,
                             quint32 tileSaturation) const
{
    Q_ASSERT(src);
    Q_ASSERT(dst);

    //fill the destination image with the background color (black for now)
    KisRectIteratorPixel dstIt = dst->createRectIterator(dstTopLeft.x(), dstTopLeft.y(), size.width(), size.height());
    qint32 depth = src->colorSpace()->colorChannelCount();
    while (! dstIt.isDone()) {
        for (qint32 i = 0; i < depth; i++) {
            dstIt.rawData()[i] = 0;
        }
        ++dstIt;
    }

    //compute number of rows and columns
    qint32 cols = (size.width() + tileSize - 1) / tileSize;
    qint32 rows = (size.height() + tileSize - 1) / tileSize;
    qint32 numTiles = (rows + 1) * (cols + 1);

//         setProgressTotalSteps(numTiles);
//         setProgressStage(i18n("Applying cubism filter..."),0);

    qint32* randomIndices = new qint32[numTiles];
    for (qint32 i = 0; i < numTiles; i++) {
        randomIndices[i] = i;
    }
    randomizeIndices(numTiles, randomIndices);

    qint32 count = 0;
    qint32 i, j, ix, iy;
    double x, y, width, height, theta;
    KisPolygon *poly = new KisPolygon();
    qint32 pixelSize = src->pixelSize();
    const quint8 *srcPixel /*= new quint8[ pixelSize ]*/;
    quint8 *dstPixel = 0;
    KisRandomAccessor srcAccessor = src->createRandomAccessor(0, 0);
    while (count < numTiles) {
        i = randomIndices[count] / (cols + 1);
        j = randomIndices[count] % (cols + 1);
        x = j * tileSize + (tileSize / 4.0) - randomDoubleNumber(0, tileSize / 2.0) + dstTopLeft.x();
        y = i * tileSize + (tileSize / 4.0) - randomDoubleNumber(0, tileSize / 2.0) + dstTopLeft.y();
        width = (tileSize + randomDoubleNumber(0, tileSize / 4.0) - tileSize / 8.0) * tileSaturation;
        height = (tileSize + randomDoubleNumber(0, tileSize / 4.0) - tileSize / 8.0) * tileSaturation;
        theta = randomDoubleNumber(0, 2 * M_PI);
        poly->clear();
        poly->addPoint(-width / 2.0, -height / 2.0);
        poly->addPoint(width / 2.0, -height / 2.0);
        poly->addPoint(width / 2.0, height / 2.0);
        poly->addPoint(-width / 2.0, height / 2.0);
        poly->rotate(theta);
        poly->translate(x, y);
        //  bounds check on x, y
        ix = (qint32) CLAMP(x, dstTopLeft.x(), dstTopLeft.x() + size.width() - 1);
        iy = (qint32) CLAMP(y, dstTopLeft.y(), dstTopLeft.y() + size.height() - 1);

        //read the pixel at ix, iy
        srcAccessor.moveTo(ix, iy);
        srcPixel = srcAccessor.rawData();
        if (srcPixel[pixelSize - 1]) {
            fillPolyColor(src, srcTopLeft, dst, dstTopLeft, size, poly, srcPixel, dstPixel);
        }
        count++;
//                 if ((count % 5) == 0) setProgress(count);
    }

}
void KisCubismFilter::fillPolyColor(KisPaintDeviceSP src,
                                    const QPoint& srcTopLeft,
                                    KisPaintDeviceSP dst,
                                    const QPoint & dstTopLeft,
                                    const QSize& size,
                                    KisPolygon* poly,
                                    const quint8* col,
                                    quint8* dest) const
// void KisCubismFilter::fillPolyColor (KisPaintDeviceSP src, KisPaintDeviceSP dst, KisPolygon* poly, const quint8* col, quint8* /*s*/, QRect rect) const
{
    Q_UNUSED(srcTopLeft);
    Q_UNUSED(dest);

    qint32         val;
    double         alpha;
    qint32         x, y;
    double          xx, yy;
    double          vec[2];
    QRect rect(dstTopLeft, size);
    qint32         x1 = rect.left(), y1 = rect.top(), x2 = rect.right(), y2 = rect.bottom();
//         qint32         selWidth, selHeight;
    qint32         *vals, *valsIter, *valsEnd;
    qint32         xs, ys, xe, ye;


    qint32 sx = (qint32)(*poly)[0].x();
    qint32 sy = (qint32)(*poly)[0].y();
    qint32 ex = (qint32)(*poly)[1].x();
    qint32 ey = (qint32)(*poly)[1].y();

    double dist = sqrt((double)(SQR(ex - sx) + SQR(ey - sy)));
    double oneOverDist = 0.0;
    if (dist > 0.0) {
        double oneOverDist = 1 / dist;
        vec[0] = (ex - sx) * oneOverDist;
        vec[1] = (ey - sy) * oneOverDist;
    }

    qint32 pixelSize = src->pixelSize();
    //get the extents of the polygon
    double dMinX, dMinY, dMaxX, dMaxY;
    poly->extents(dMinX, dMinY, dMaxX, dMaxY);
    qint32 minX = static_cast<qint32>(dMinX);
    qint32 minY = static_cast<qint32>(dMinY);
    qint32 maxX = static_cast<qint32>(dMaxX);
    qint32 maxY = static_cast<qint32>(dMaxY);
    qint32 sizeX = (maxX - minX) * SUPERSAMPLE;
    qint32 sizeY = (maxY - minY) * SUPERSAMPLE;

    qint32 *minScanlines = new qint32[sizeY];
    qint32 *minScanlinesIter = minScanlines;
    qint32 *maxScanlines = new qint32[sizeY];
    qint32 *maxScanlinesIter = maxScanlines;

    for (qint32 i = 0; i < sizeY; i++) {
        minScanlines[i] = maxX * SUPERSAMPLE;
        maxScanlines[i] = minX * SUPERSAMPLE;
    }

    if (poly->numberOfPoints()) {
        qint32 polyNpts = poly->numberOfPoints();

        xs = static_cast<qint32>((*poly)[polyNpts-1].x());
        ys = static_cast<qint32>((*poly)[polyNpts-1].y());
        xe = static_cast<qint32>((*poly)[0].x());
        ye = static_cast<qint32>((*poly)[0].y());

        xs *= SUPERSAMPLE;
        ys *= SUPERSAMPLE;
        xe *= SUPERSAMPLE;
        ye *= SUPERSAMPLE;

        convertSegment(xs, ys, xe, ye, minY * SUPERSAMPLE, minScanlines, maxScanlines, minX* SUPERSAMPLE, maxX* SUPERSAMPLE);

        KisPolygon::iterator it;

        for (it = poly->begin(); it != poly->end();) {
            xs = static_cast<qint32>((*it).x());
            ys = static_cast<qint32>((*it).y());
            ++it;

            if (it != poly->end()) {
                xe = static_cast<qint32>((*it).x());
                ye = static_cast<qint32>((*it).y());

                xs *= SUPERSAMPLE;
                ys *= SUPERSAMPLE;
                xe *= SUPERSAMPLE;
                ye *= SUPERSAMPLE;

                convertSegment(xs, ys, xe, ye, minY * SUPERSAMPLE, minScanlines, maxScanlines, minX* SUPERSAMPLE, maxX* SUPERSAMPLE);
            }
        }
    }

    KisRandomAccessor dstAccessor = dst->createRandomAccessor(0, 0);

    KoMixColorsOp * mixOp = src->colorSpace()->mixColorsOp();
    quint8* buf = new quint8[pixelSize];

    vals = new qint32[sizeX];
//         x1 = minX; x2 = maxX; y1 = minY; y2 = maxY;
    for (qint32 i = 0; i < sizeY; i++, minScanlinesIter++, maxScanlinesIter++) {
        if (!(i % SUPERSAMPLE)) {
            memset(vals, 0, sizeof(qint32) * sizeX);
        }

        yy = static_cast<double>(i) / static_cast<double>(SUPERSAMPLE) + minY;

        for (qint32 j = *minScanlinesIter; j < *maxScanlinesIter; j++) {
            x = j - minX * SUPERSAMPLE;
            vals[x] += 255;
        }

        if (!((i + 1) % SUPERSAMPLE)) {
            y = (i / SUPERSAMPLE) + minY;
            if (y >= y1 && y <= y2) {
                for (qint32 j = 0; j < sizeX; j += SUPERSAMPLE) {
                    x = (j / SUPERSAMPLE) + minX;

                    if (x >= x1 && x <= x2) {
                        for (val = 0, valsIter = &vals[j], valsEnd = &valsIter[SUPERSAMPLE]; valsIter < valsEnd; valsIter++) {
                            val += *valsIter;
                        }
                        val /= SQR(SUPERSAMPLE);

                        if (val > 0) {
                            xx = static_cast<double>(j) / static_cast<double>(SUPERSAMPLE) + minX;
                            alpha = calcAlphaBlend(vec, oneOverDist, xx - sx, yy - sy);

                            qint16 weights[2];
                            weights[0] = static_cast<quint8>(alpha * 255);
                            weights[1] = 255 - weights[0];

                            dstAccessor.moveTo(x, y);
                            memcpy(buf, dstAccessor.rawData(), sizeof(quint8) * pixelSize);

                            const quint8* colors[2];
                            colors[0] = col;
                            colors[1] = buf;

                            mixOp->mixColors(colors, weights, 2, dstAccessor.rawData());
                        }
                    }
                }
            }
        }
    }
    delete[] buf;
    delete[] vals;
    delete[] minScanlines;
    delete[] maxScanlines;
}
Example #16
0
void KisGmicSimpleConvertor::convertToGmicImageFast(KisPaintDeviceSP dev, CImg< float >& gmicImage, QRect rc)
{
    KoColorTransformation * pixelToGmicPixelFormat = createTransformation(dev->colorSpace());
    if (pixelToGmicPixelFormat == 0)
    {
        dbgPlugins << "Fall-back to slow color conversion method";
        convertToGmicImage(dev, gmicImage, rc);
        return;
    }

    if (rc.isEmpty())
    {
        dbgPlugins << "Image rectangle is empty! Using supplied gmic layer dimension";
        rc = QRect(0,0,gmicImage._width, gmicImage._height);
    }

    qint32 x = rc.x();
    qint32 y = rc.y();
    qint32 width = rc.width();
    qint32 height = rc.height();

    width  = width < 0  ? 0 : width;
    height = height < 0 ? 0 : height;

    const qint32 numChannels = 4;

    int greenOffset = gmicImage._width * gmicImage._height;
    int blueOffset = greenOffset * 2;
    int alphaOffset = greenOffset * 3;

    QVector<float *> planes;
    planes.append(gmicImage._data);
    planes.append(gmicImage._data + greenOffset);
    planes.append(gmicImage._data + blueOffset);
    planes.append(gmicImage._data + alphaOffset);

    KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG(dev->x(), dev->y());
    int tileWidth = it->numContiguousColumns(dev->x());
    int tileHeight = it->numContiguousRows(dev->y());

    Q_ASSERT(tileWidth == 64);
    Q_ASSERT(tileHeight == 64);

    const KoColorSpace *rgbaFloat32bitcolorSpace = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(),
                                                                                                Float32BitsColorDepthID.id(),
                                                                                                KoColorSpaceRegistry::instance()->rgb8()->profile());
    Q_CHECK_PTR(rgbaFloat32bitcolorSpace);
    const qint32 dstPixelSize = rgbaFloat32bitcolorSpace->pixelSize();
    const qint32 srcPixelSize = dev->pixelSize();

    quint8 * dstTile = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * tileWidth * tileHeight];

    qint32 dataY = 0;
    qint32 imageX = x;
    qint32 imageY = y;
    it->moveTo(imageX, imageY);
    qint32 rowsRemaining = height;

    while (rowsRemaining > 0) {

        qint32 dataX = 0;
        imageX = x;
        qint32 columnsRemaining = width;
        qint32 numContiguousImageRows = it->numContiguousRows(imageY);

        qint32 rowsToWork = qMin(numContiguousImageRows, rowsRemaining);
        qint32 convertedTileY = tileHeight - rowsToWork;
        Q_ASSERT(convertedTileY >= 0);

        while (columnsRemaining > 0) {

            qint32 numContiguousImageColumns = it->numContiguousColumns(imageX);
            qint32 columnsToWork = qMin(numContiguousImageColumns, columnsRemaining);
            qint32 convertedTileX = tileWidth - columnsToWork;
            Q_ASSERT(convertedTileX >= 0);

            const qint32 dataIdx = dataX + dataY * width;
            const qint32 dstTileIndex = convertedTileX + convertedTileY * tileWidth;
            const qint32 tileRowStride = (tileWidth - columnsToWork) * dstPixelSize;
            const qint32 srcTileRowStride = (tileWidth - columnsToWork) * srcPixelSize;

            it->moveTo(imageX, imageY);
            quint8 *tileItStart = dstTile + dstTileIndex * dstPixelSize;

            // transform tile row by row
            quint8 *dstTileIt = tileItStart;
            quint8 *srcTileIt = const_cast<quint8*>(it->rawDataConst());

            qint32 row = rowsToWork;
            while (row > 0)
            {
                pixelToGmicPixelFormat->transform(srcTileIt, dstTileIt , columnsToWork);
                srcTileIt += columnsToWork * srcPixelSize;
                srcTileIt += srcTileRowStride;

                dstTileIt += columnsToWork * dstPixelSize;
                dstTileIt += tileRowStride;

                row--;
            }

            // here we want to copy floats to dstTile, so tileItStart has to point to float buffer
            qint32 channelSize = sizeof(float);
            for(qint32 i=0; i<numChannels;i++)
            {
                float * planeIt = planes[i] + dataIdx;
                qint32 dataStride = (width - columnsToWork);
                quint8* tileIt = tileItStart;

                for (qint32 row = 0; row < rowsToWork; row++) {
                    for (int col = 0; col < columnsToWork; col++) {
                        memcpy(planeIt, tileIt, channelSize);
                        tileIt += dstPixelSize;
                        planeIt += 1;
                    }

                    tileIt += tileRowStride;
                    planeIt += dataStride;
                }
                // skip channel in tile: red, green, blue, alpha
                tileItStart += channelSize;
            }

            imageX += columnsToWork;
            dataX += columnsToWork;
            columnsRemaining -= columnsToWork;
        }

        imageY += rowsToWork;
        dataY += rowsToWork;
        rowsRemaining -= rowsToWork;
    }

    delete [] dstTile;
    delete pixelToGmicPixelFormat;

}
Example #17
0
void SprayBrush::paint(KisPaintDeviceSP dab, KisPaintDeviceSP source,
                       const KisPaintInformation& info, qreal rotation, qreal scale,
                       const KoColor &color, const KoColor &bgColor)
{
    // initializing painter
    if (!m_painter) {
        m_painter = new KisPainter(dab);
        m_painter->setFillStyle(KisPainter::FillStyleForegroundColor);
        m_painter->setMaskImageSize(m_shapeProperties->width, m_shapeProperties->height);
        m_dabPixelSize = dab->colorSpace()->pixelSize();
        if (m_colorProperties->useRandomHSV) {
            m_transfo = dab->colorSpace()->createColorTransformation("hsv_adjustment", QHash<QString, QVariant>());
        }

        m_brushQImage = m_shapeProperties->image;
        if (!m_brushQImage.isNull()) {
            m_brushQImage = m_brushQImage.scaled(m_shapeProperties->width, m_shapeProperties->height);
        }
        m_imageDevice = new KisPaintDevice(dab->colorSpace());
    }


    qreal x = info.pos().x();
    qreal y = info.pos().y();
    KisRandomAccessorSP accessor = dab->createRandomAccessorNG(qRound(x), qRound(y));

    Q_ASSERT(color.colorSpace()->pixelSize() == dab->pixelSize());
    m_inkColor = color;
    KisCrossDeviceColorPicker colorPicker(source, m_inkColor);

    // apply size sensor
    m_radius = m_properties->radius * scale;

    // jitter movement
    if (m_properties->jitterMovement) {
        x = x + ((2 * m_radius * drand48()) - m_radius) * m_properties->amount;
        y = y + ((2 * m_radius * drand48()) - m_radius) * m_properties->amount;
    }

    // this is wrong for every shape except pixel and anti-aliased pixel


    if (m_properties->useDensity) {
        m_particlesCount = (m_properties->coverage * (M_PI * m_radius * m_radius));
    }
    else {
        m_particlesCount = m_properties->particleCount;
    }

    QHash<QString, QVariant> params;
    qreal nx, ny;
    int ix, iy;

    qreal angle;
    qreal length;
    qreal rotationZ = 0.0;
    qreal particleScale = 1.0;

    bool shouldColor = true;
    if (m_colorProperties->fillBackground) {
        m_painter->setPaintColor(bgColor);
        paintCircle(m_painter, x, y, m_radius);
    }

    QTransform m;
    m.reset();
    m.rotateRadians(-rotation + deg2rad(m_properties->brushRotation));
    m.scale(m_properties->scale, m_properties->scale);

    for (quint32 i = 0; i < m_particlesCount; i++) {
        // generate random angle
        angle = drand48() * M_PI * 2;

        // generate random length
        if (m_properties->gaussian) {
            length = qBound<qreal>(0.0, m_rand->nextGaussian(0.0, 0.50) , 1.0);
        }
        else {
            length = drand48();
        }

        if (m_shapeDynamicsProperties->enabled) {
            // rotation
            rotationZ = rotationAngle();

            if (m_shapeDynamicsProperties->followCursor) {

                rotationZ = linearInterpolation(rotationZ, angle, m_shapeDynamicsProperties->followCursorWeigth);
            }


            if (m_shapeDynamicsProperties->followDrawingAngle) {

                rotationZ = linearInterpolation(rotationZ, info.drawingAngle(), m_shapeDynamicsProperties->followDrawingAngleWeight);
            }

            // random size - scale
            if (m_shapeDynamicsProperties->randomSize) {
                particleScale = drand48();
            }
        }
        // generate polar coordinate
        nx = (m_radius * cos(angle)  * length);
        ny = (m_radius * sin(angle)  * length);

        // compute the height of the ellipse
        ny *= m_properties->aspect;

        // transform
        m.map(nx, ny, &nx, &ny);

        // color transformation

        if (shouldColor) {
            if (m_colorProperties->sampleInputColor) {
                colorPicker.pickOldColor(nx + x, ny + y, m_inkColor.data());
            }

            // mix the color with background color
            if (m_colorProperties->mixBgColor) {
                KoMixColorsOp * mixOp = dab->colorSpace()->mixColorsOp();

                const quint8 *colors[2];
                colors[0] = m_inkColor.data();
                colors[1] = bgColor.data();

                qint16 colorWeights[2];
                int MAX_16BIT = 255;
                qreal blend = info.pressure();

                colorWeights[0] = static_cast<quint16>(blend * MAX_16BIT);
                colorWeights[1] = static_cast<quint16>((1.0 - blend) * MAX_16BIT);
                mixOp->mixColors(colors, colorWeights, 2, m_inkColor.data());
            }

            if (m_colorProperties->useRandomHSV && m_transfo) {
                params["h"] = (m_colorProperties->hue / 180.0) * drand48();
                params["s"] = (m_colorProperties->saturation / 100.0) * drand48();
                params["v"] = (m_colorProperties->value / 100.0) * drand48();
                m_transfo->setParameters(params);
                m_transfo->transform(m_inkColor.data(), m_inkColor.data() , 1);
            }

            if (m_colorProperties->useRandomOpacity) {
                quint8 alpha = qRound(drand48() * OPACITY_OPAQUE_U8);
                m_inkColor.setOpacity(alpha);
                m_painter->setOpacity(alpha);
            }

            if (!m_colorProperties->colorPerParticle) {
                shouldColor = false;
            }
            m_painter->setPaintColor(m_inkColor);
        }

        qreal jitteredWidth = qMax(qreal(1.0), m_shapeProperties->width * particleScale);
        qreal jitteredHeight = qMax(qreal(1.0), m_shapeProperties->height * particleScale);

        if (m_shapeProperties->enabled){
        switch (m_shapeProperties->shape){
            // ellipse
            case 0:
            {
                if (m_shapeProperties->width == m_shapeProperties->height){
                    paintCircle(m_painter, nx + x, ny + y, qRound(jitteredWidth * 0.5));
                }
                else {
                    paintEllipse(m_painter, nx + x, ny + y, qRound(jitteredWidth * 0.5) , qRound(jitteredHeight * 0.5), rotationZ);
                }
                break;
            }
            // rectangle
            case 1:
            {
                paintRectangle(m_painter, nx + x, ny + y, qRound(jitteredWidth) , qRound(jitteredHeight), rotationZ);
                break;
            }
            // wu-particle
            case 2: {
                paintParticle(accessor, m_inkColor, nx + x, ny + y);
                break;
            }
            // pixel
            case 3: {
                ix = qRound(nx + x);
                iy = qRound(ny + y);
                accessor->moveTo(ix, iy);
                memcpy(accessor->rawData(), m_inkColor.data(), m_dabPixelSize);
                break;
            }
            case 4: {
                if (!m_brushQImage.isNull()) {

                    QTransform m;
                    m.rotate(rad2deg(rotationZ));

                    if (m_shapeDynamicsProperties->randomSize) {
                        m.scale(particleScale, particleScale);
                    }
                    m_transformed = m_brushQImage.transformed(m, Qt::SmoothTransformation);
                    m_imageDevice->convertFromQImage(m_transformed, 0);
                    KisRandomAccessorSP ac = m_imageDevice->createRandomAccessorNG(0, 0);
                    QRect rc = m_transformed.rect();

                    if (m_colorProperties->useRandomHSV && m_transfo) {

                        for (int y = rc.y(); y < rc.y() + rc.height(); y++) {
                            for (int x = rc.x(); x < rc.x() + rc.width(); x++) {
                                ac->moveTo(x, y);
                                m_transfo->transform(ac->rawData(), ac->rawData() , 1);
                            }
                        }
                    }

                    ix = qRound(nx + x - rc.width() * 0.5);
                    iy = qRound(ny + y - rc.height() * 0.5);
                    m_painter->bitBlt(QPoint(ix, iy), m_imageDevice, rc);
                    m_imageDevice->clear();
                    break;
                }
            }
            }
            // Auto-brush
        }
        else {
            QPointF hotSpot = m_brush->hotSpot(particleScale, particleScale, -rotationZ, info);
            QPointF pos(nx + x, ny + y);
            QPointF pt = pos - hotSpot;

            qint32 ix;
            qreal xFraction;
            qint32 iy;
            qreal yFraction;

            KisPaintOp::splitCoordinate(pt.x(), &ix, &xFraction);
            KisPaintOp::splitCoordinate(pt.y(), &iy, &yFraction);

            //KisFixedPaintDeviceSP dab;
            if (m_brush->brushType() == IMAGE ||
                    m_brush->brushType() == PIPE_IMAGE) {
                m_fixedDab = m_brush->paintDevice(m_fixedDab->colorSpace(), particleScale, -rotationZ, info, xFraction, yFraction);

                if (m_colorProperties->useRandomHSV && m_transfo) {
                    quint8 * dabPointer = m_fixedDab->data();
                    int pixelCount = m_fixedDab->bounds().width() * m_fixedDab->bounds().height();
                    m_transfo->transform(dabPointer, dabPointer, pixelCount);
                }

            }
            else {
                m_brush->mask(m_fixedDab, m_inkColor, particleScale, particleScale, -rotationZ, info, xFraction, yFraction);
            }
            m_painter->bltFixed(QPoint(ix, iy), m_fixedDab, m_fixedDab->bounds());
        }
    }
    // recover from jittering of color,
    // m_inkColor.opacity is recovered with every paint
}
void KisSobelFilter::process(KisPaintDeviceSP device,
                            const QRect& applyRect,
                            const KisFilterConfiguration* configuration,
                            KoUpdater* progressUpdater
                            ) const
{
    QPoint srcTopLeft = applyRect.topLeft();
    Q_ASSERT(!device.isNull());

    //read the filter configuration values from the KisFilterConfiguration object
    bool doHorizontal = configuration->getBool("doHorizontally", true);
    bool doVertical = configuration->getBool("doVertically", true);
    bool keepSign = configuration->getBool("keepSign", true);
    bool makeOpaque = configuration->getBool("makeOpaque", true);

    quint32 width = applyRect.width();
    quint32 height = applyRect.height();
    quint32 pixelSize = device->pixelSize();

    int cost = applyRect.height();

    /*  allocate row buffers  */
    quint8* prevRow = new quint8[(width + 2) * pixelSize];
    Q_CHECK_PTR(prevRow);
    quint8* curRow = new quint8[(width + 2) * pixelSize];
    Q_CHECK_PTR(curRow);
    quint8* nextRow = new quint8[(width + 2) * pixelSize];
    Q_CHECK_PTR(nextRow);
    quint8* dest = new quint8[ width  * pixelSize];
    Q_CHECK_PTR(dest);

    quint8* pr = prevRow + pixelSize;
    quint8* cr = curRow + pixelSize;
    quint8* nr = nextRow + pixelSize;

    prepareRow(device, pr, srcTopLeft.x(), srcTopLeft.y() - 1, width, height);
    prepareRow(device, cr, srcTopLeft.x(), srcTopLeft.y(), width, height);

    quint32 counter = 0;
    quint8* d;
    quint8* tmp;
    qint32 gradient, horGradient, verGradient;
    // loop through the rows, applying the sobel convolution

    KisHLineIteratorSP dstIt = device->createHLineIteratorNG(srcTopLeft.x(), srcTopLeft.y(), width);

    for (quint32 row = 0; row < height; row++) {

        // prepare the next row
        prepareRow(device, nr, srcTopLeft.x(), srcTopLeft.y() + row + 1, width, height);
        d = dest;

        for (quint32 col = 0; col < width * pixelSize; col++) {
            int positive = col + pixelSize;
            int negative = col - pixelSize;
            horGradient = (doHorizontal ?
                           ((pr[negative] +  2 * pr[col] + pr[positive]) -
                            (nr[negative] + 2 * nr[col] + nr[positive]))
                           : 0);

            verGradient = (doVertical ?
                           ((pr[negative] + 2 * cr[negative] + nr[negative]) -
                            (pr[positive] + 2 * cr[positive] + nr[positive]))
                           : 0);
            gradient = (qint32)((doVertical && doHorizontal) ?
                                (ROUND(RMS(horGradient, verGradient)) / 5.66)   // always >0
                                : (keepSign ? (127 + (ROUND((horGradient + verGradient) / 8.0)))
                                   : (ROUND(qAbs(horGradient + verGradient) / 4.0))));

            *d++ = gradient;
            if (gradient > 10) counter ++;
        }

        //  shuffle the row pointers
        tmp = pr;
        pr = cr;
        cr = nr;
        nr = tmp;

        //store the dest
        device->writeBytes(dest, srcTopLeft.x(), row, width, 1);

        if (makeOpaque) {
            do {
                device->colorSpace()->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
            } while(dstIt->nextPixel());
            dstIt->nextRow();
        }
        if (progressUpdater) progressUpdater->setProgress(row / cost);
    }

    delete[] prevRow;
    delete[] curRow;
    delete[] nextRow;
    delete[] dest;
}