Example #1
0
void KisFilterRandomPick::processImpl(KisPaintDeviceSP device,
                                      const QRect& applyRect,
                                      const KisFilterConfigurationSP config,
                                      KoUpdater* progressUpdater
                                      ) const
{
    Q_UNUSED(config);
    Q_ASSERT(!device.isNull());

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

    const KoColorSpace * cs = device->colorSpace();

    QVariant value;
    int level = (config && config->getProperty("level", value)) ? value.toInt() : 50;
    int opacity = (config && config->getProperty("opacity", value)) ? value.toInt() : 100;
    double windowsize = (config && config->getProperty("windowsize", value)) ? value.toDouble() : 2.5;

    int seedThreshold = rand();
    int seedH = rand();
    int seedV = rand();

    if (config) {
        seedThreshold = config->getInt("seedThreshold", seedThreshold);
        seedH = config->getInt("seedH", seedH);
        seedV = config->getInt("seedV", seedV);
    }
    KisRandomGenerator randT(seedThreshold);
    KisRandomGenerator randH(seedH);
    KisRandomGenerator randV(seedV);

    KisSequentialIterator dstIt(device, applyRect);
    KisRandomConstAccessorSP srcRA = device->createRandomConstAccessorNG(0, 0);

    double threshold = (100 - level) / 100.0;

    qint16 weights[2];
    weights[0] = (255 * opacity) / 100; weights[1] = 255 - weights[0];
    const quint8* pixels[2];
    KoMixColorsOp * mixOp = cs->mixColorsOp();
    do{
        if (randT.doubleRandomAt(dstIt.x(), dstIt.y()) > threshold) {
            int x = static_cast<int>(dstIt.x() + windowsize * (randH.doubleRandomAt(dstIt.x(), dstIt.y()) - 0.5));
            int y = static_cast<int>(dstIt.y() +  windowsize * (randV.doubleRandomAt(dstIt.x(), dstIt.y()) -0.5));
            srcRA->moveTo(x, y);
            pixels[0] = srcRA->oldRawData();
            pixels[1] = dstIt.oldRawData();
            mixOp->mixColors(pixels, weights, 2, dstIt.rawData());
        }
        if (progressUpdater) progressUpdater->setValue(++count);
    } while(dstIt.nextPixel());

}
Example #2
0
// gmic assumes float rgba in 0.0 - 255.0, thus default value
void KisGmicSimpleConvertor::convertToGmicImage(KisPaintDeviceSP dev, gmic_image<float>& gmicImage, QRect rc)
{
    Q_ASSERT(!dev.isNull());
    Q_ASSERT(gmicImage._spectrum == 4); // rgba

    if (rc.isEmpty())
    {
        rc = QRect(0,0,gmicImage._width, gmicImage._height);
    }

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

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

    KoColorConversionTransformation::Intent renderingIntent = KoColorConversionTransformation::internalRenderingIntent();
    KoColorConversionTransformation::ConversionFlags conversionFlags = KoColorConversionTransformation::internalConversionFlags();

    const KoColorSpace * colorSpace = dev->colorSpace();
    KisRandomConstAccessorSP it = dev->createRandomConstAccessorNG(0,0);

    int optimalBufferSize = 64; // most common numContiguousColumns, tile size?
    quint8 * floatRGBApixel = new quint8[rgbaFloat32bitcolorSpace->pixelSize() * optimalBufferSize];
    quint32 pixelSize = rgbaFloat32bitcolorSpace->pixelSize();
    int pos = 0;
    for (int y = 0; y < rc.height(); y++)
    {
        int x = 0;
        while (x < rc.width())
        {
            it->moveTo(x, y);
            qint32 numContiguousColumns = qMin(it->numContiguousColumns(x), optimalBufferSize);
            numContiguousColumns = qMin(numContiguousColumns, rc.width() - x);

            colorSpace->convertPixelsTo(it->rawDataConst(), floatRGBApixel, rgbaFloat32bitcolorSpace, numContiguousColumns, renderingIntent, conversionFlags);

            pos = y * gmicImage._width + x;
            for (qint32 bx = 0; bx < numContiguousColumns; bx++)
            {
                memcpy(gmicImage._data + pos                  ,floatRGBApixel + bx * pixelSize   , 4);
                memcpy(gmicImage._data + pos + greenOffset    ,floatRGBApixel + bx * pixelSize + 4, 4);
                memcpy(gmicImage._data + pos + blueOffset     ,floatRGBApixel + bx * pixelSize + 8, 4);
                memcpy(gmicImage._data + pos + alphaOffset    ,floatRGBApixel + bx * pixelSize + 12, 4);
                pos++;
            }

            x += numContiguousColumns;
        }
    }
    delete [] floatRGBApixel;
}
Example #3
0
/* Function to apply the Emboss effect
 *
 * data             => The image data in RGBA mode.
 * Width            => Width of image.
 * Height           => Height of image.
 * d                => Emboss value
 *
 * Theory           => This is an amazing effect. And the theory is very simple to
 *                     understand. You get the diference between the colors and
 *                     increase it. After this, get the gray tone
 */
void KisEmbossFilter::processImpl(KisPaintDeviceSP device,
                                  const QRect& applyRect,
                                  const KisFilterConfigurationSP config,
                                  KoUpdater* progressUpdater
                                  ) const
{
    QPoint srcTopLeft = applyRect.topLeft();
    Q_ASSERT(device);

    //read the filter configuration values from the KisFilterConfiguration object
    quint32 embossdepth = config ?  config->getInt("depth", 30) : 30;

    //the actual filter function from digikam. It needs a pointer to a quint8 array
    //with the actual pixel data.

    float Depth = embossdepth / 10.0;
    int    R = 0, G = 0, B = 0;
    uchar  Gray = 0;
    int Width = applyRect.width();
    int Height = applyRect.height();

    if (progressUpdater) {
        progressUpdater->setRange(0, Height);
    }

    KisSequentialIterator it(device, applyRect);
    QColor color1;
    QColor color2;
    KisRandomConstAccessorSP acc = device->createRandomAccessorNG(srcTopLeft.x(), srcTopLeft.y());
    do {
    
        // XXX: COLORSPACE_INDEPENDENCE or at least work IN RGB16A
        device->colorSpace()->toQColor(it.oldRawData(), &color1);
        acc->moveTo(srcTopLeft.x() + it.x() + Lim_Max(it.x(), 1, Width), srcTopLeft.y() + it.y() + Lim_Max(it.y(), 1, Height));

        device->colorSpace()->toQColor(acc->oldRawData(), &color2);

        R = abs((int)((color1.red() - color2.red()) * Depth + (quint8_MAX / 2)));
        G = abs((int)((color1.green() - color2.green()) * Depth + (quint8_MAX / 2)));
        B = abs((int)((color1.blue() - color2.blue()) * Depth + (quint8_MAX / 2)));

        Gray = CLAMP((R + G + B) / 3, 0, quint8_MAX);

        device->colorSpace()->fromQColor(QColor(Gray, Gray, Gray, color1.alpha()), it.rawData());
        if (progressUpdater) { progressUpdater->setValue(it.y()); if(progressUpdater->interrupted()) return; }
    } while(it.nextPixel());
}
Example #4
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;

}
void KisRainDropsFilter::process(KisConstProcessingInformation srcInfo,
                                 KisProcessingInformation dstInfo,
                                 const QSize& size,
                                 const KisFilterConfiguration* config,
                                 KoUpdater* progressUpdater
                                ) const
{

    const KisPaintDeviceSP src = srcInfo.paintDevice();
    KisPaintDeviceSP dst = dstInfo.paintDevice();
    QPoint dstTopLeft = dstInfo.topLeft();
    QPoint srcTopLeft = srcInfo.topLeft();
    Q_UNUSED(config);
    Q_ASSERT(!src.isNull());
    Q_ASSERT(!dst.isNull());

    //read the filter configuration values from the KisFilterConfiguration object
    quint32 DropSize = config->getInt("dropSize", 80);
    quint32 number = config->getInt("number", 80);
    quint32 fishEyes = config->getInt("fishEyes", 30);

    if (progressUpdater) {
        progressUpdater->setRange(0, size.width() * size.height());
    }
    int count = 0;

    if (fishEyes <= 0) fishEyes = 1;

    if (fishEyes > 100) fishEyes = 100;

    int Width = size.width();
    int Height = size.height();

    bool** BoolMatrix = CreateBoolArray(Width, Height);

    int       i, j, k, l, m, n;                 // loop variables
    int       Bright;                           // Bright value for shadows and highlights
    int       x, y;                             // center coordinates
    int       Counter = 0;                      // Counter (duh !)
    int       NewSize;                          // Size of current raindrop
    int       halfSize;                         // Half of the current raindrop
    int       Radius;                           // Maximum radius for raindrop
    int       BlurRadius;                       // Blur Radius
    int       BlurPixels;

    double    r, a;                             // polar coordinates
    double    OldRadius;                        // Radius before processing
    double    NewfishEyes = (double)fishEyes * 0.01;  // FishEye fishEyesicients
    double    s;
    double    R, G, B;

    bool      FindAnother = false;              // To search for good coordinates

    const KoColorSpace * cs = src->colorSpace();


    // XXX: move the seed to the config, so the filter can be used as
    // and adjustment filter (boud).
    // And use a thread-safe random number generator
    QDateTime dt = QDateTime::currentDateTime();
    QDateTime Y2000(QDate(2000, 1, 1), QTime(0, 0, 0));

    srand((uint) dt.secsTo(Y2000));

    // Init booleen Matrix.

    for (i = 0 ; (i < Width) && !(progressUpdater && progressUpdater->interrupted()) ; ++i) {
        for (j = 0 ; (j < Height) && !(progressUpdater && progressUpdater->interrupted()); ++j) {
            BoolMatrix[i][j] = false;
        }
    }

    KisRandomAccessorSP dstAccessor = dst->createRandomAccessorNG(dstTopLeft.x(), dstTopLeft.y());
    KisRandomConstAccessorSP srcAccessor = src->createRandomConstAccessorNG(dstTopLeft.x(), dstTopLeft.y());
    
    for (uint NumBlurs = 0; (NumBlurs <= number) && !(progressUpdater && progressUpdater->interrupted()); ++NumBlurs) {
        NewSize = (int)(rand() * ((double)(DropSize - 5) / RAND_MAX) + 5);
        halfSize = NewSize / 2;
        Radius = halfSize;
        s = Radius / log(NewfishEyes * Radius + 1);

        Counter = 0;

        do {
            FindAnother = false;
            y = (int)(rand() * ((double)(Width - 1) / RAND_MAX));
            x = (int)(rand() * ((double)(Height - 1) / RAND_MAX));

            if (BoolMatrix[y][x])
                FindAnother = true;
            else
                for (i = x - halfSize ; (i <= x + halfSize) && !(progressUpdater && progressUpdater->interrupted()); i++)
                    for (j = y - halfSize ; (j <= y + halfSize) && !(progressUpdater && progressUpdater->interrupted()); j++)
                        if ((i >= 0) && (i < Height) && (j >= 0) && (j < Width))
                            if (BoolMatrix[j][i])
                                FindAnother = true;

            Counter++;
        } while ((FindAnother && (Counter < 10000) && !(progressUpdater && progressUpdater->interrupted())));

        if (Counter >= 10000) {
            NumBlurs = number;
            break;
        }

        for (i = -1 * halfSize ; (i < NewSize - halfSize) && !(progressUpdater && progressUpdater->interrupted()); i++) {
            for (j = -1 * halfSize ; (j < NewSize - halfSize) && !(progressUpdater && progressUpdater->interrupted()); j++) {
                r = sqrt(i * i + j * j);
                a = atan2(static_cast<double>(i), static_cast<double>(j));

                if (r <= Radius) {
                    OldRadius = r;
                    r = (exp(r / s) - 1) / NewfishEyes;

                    k = x + (int)(r * sin(a));
                    l = y + (int)(r * cos(a));

                    m = x + i;
                    n = y + j;

                    if ((k >= 0) && (k < Height) && (l >= 0) && (l < Width)) {
                        if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width)) {
                            Bright = 0;

                            if (OldRadius >= 0.9 * Radius) {
                                if ((a <= 0) && (a > -2.25))
                                    Bright = -80;
                                else if ((a <= -2.25) && (a > -2.5))
                                    Bright = -40;
                                else if ((a <= 0.25) && (a > 0))
                                    Bright = -40;
                            }

                            else if (OldRadius >= 0.8 * Radius) {
                                if ((a <= -0.75) && (a > -1.50))
                                    Bright = -40;
                                else if ((a <= 0.10) && (a > -0.75))
                                    Bright = -30;
                                else if ((a <= -1.50) && (a > -2.35))
                                    Bright = -30;
                            }

                            else if (OldRadius >= 0.7 * Radius) {
                                if ((a <= -0.10) && (a > -2.0))
                                    Bright = -20;
                                else if ((a <= 2.50) && (a > 1.90))
                                    Bright = 60;
                            }

                            else if (OldRadius >= 0.6 * Radius) {
                                if ((a <= -0.50) && (a > -1.75))
                                    Bright = -20;
                                else if ((a <= 0) && (a > -0.25))
                                    Bright = 20;
                                else if ((a <= -2.0) && (a > -2.25))
                                    Bright = 20;
                            }

                            else if (OldRadius >= 0.5 * Radius) {
                                if ((a <= -0.25) && (a > -0.50))
                                    Bright = 30;
                                else if ((a <= -1.75) && (a > -2.0))
                                    Bright = 30;
                            }

                            else if (OldRadius >= 0.4 * Radius) {
                                if ((a <= -0.5) && (a > -1.75))
                                    Bright = 40;
                            }

                            else if (OldRadius >= 0.3 * Radius) {
                                if ((a <= 0) && (a > -2.25))
                                    Bright = 30;
                            }

                            else if (OldRadius >= 0.2 * Radius) {
                                if ((a <= -0.5) && (a > -1.75))
                                    Bright = 20;
                            }

                            BoolMatrix[n][m] = true;

                            QColor originalColor;

                            srcAccessor->moveTo(srcTopLeft.x() + l, srcTopLeft.y() + k);
                            cs->toQColor(srcAccessor->oldRawData(), &originalColor);

                            int newRed = CLAMP(originalColor.red() + Bright, 0, quint8_MAX);
                            int newGreen = CLAMP(originalColor.green() + Bright, 0, quint8_MAX);
                            int newBlue = CLAMP(originalColor.blue() + Bright, 0, quint8_MAX);

                            QColor newColor;
                            newColor.setRgb(newRed, newGreen, newBlue);

                            dstAccessor->moveTo(dstTopLeft.x() + n, dstTopLeft.y() + m);
                            cs->fromQColor(newColor, dstAccessor->rawData());
                        }
                    }
                }
            }
        }

        BlurRadius = NewSize / 25 + 1;

        for (i = -1 * halfSize - BlurRadius ; (i < NewSize - halfSize + BlurRadius) && !(progressUpdater && progressUpdater->interrupted()) ; i++) {
            for (j = -1 * halfSize - BlurRadius;
                    ((j < NewSize - halfSize + BlurRadius) && !(progressUpdater && progressUpdater->interrupted()));
                    ++j) {
                r = sqrt(i * i + j * j);

                if (r <= Radius * 1.1) {
                    R = G = B = 0;
                    BlurPixels = 0;

                    for (k = -1 * BlurRadius; k < BlurRadius + 1; k++)
                        for (l = -1 * BlurRadius; l < BlurRadius + 1; l++) {
                            m = x + i + k;
                            n = y + j + l;

                            if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width)) {
                                QColor color;
                                dstAccessor->moveTo(dstTopLeft.x() + n, dstTopLeft.y() + m);
                                cs->toQColor(dstAccessor->rawData(), &color);

                                R += color.red();
                                G += color.green();
                                B += color.blue();
                                BlurPixels++;
                            }
                        }

                    m = x + i;
                    n = y + j;

                    if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width)) {
                        QColor color;

                        color.setRgb((int)(R / BlurPixels), (int)(G / BlurPixels), (int)(B / BlurPixels));
                        dstAccessor->moveTo(dstTopLeft.x() + n, dstTopLeft.y() + m);
                        cs->fromQColor(color, dstAccessor->rawData());
                    }
                }
            }
        }

        if (progressUpdater) progressUpdater->setValue(++count);
    }

    FreeBoolArray(BoolMatrix, Width);
}
Example #6
0
KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(const QByteArray& from, const QByteArray& to)
{
    dbgFile << "HeightMap export! From:" << from << ", To:" << to;

    if (from != "application/x-krita")
        return KisImportExportFilter::NotImplemented;

    KisDocument *inputDoc = inputDocument();
    QString filename = outputFile();

    if (!inputDoc)
        return KisImportExportFilter::NoDocumentCreated;

    if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;

    KisImageWSP image = inputDoc->image();
    Q_CHECK_PTR(image);

    if (inputDoc->image()->width() != inputDoc->image()->height()) {
        inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not square"));
        return KisImportExportFilter::WrongFormat;
    }

    if (inputDoc->image()->colorSpace()->colorModelId() != GrayAColorModelID) {
        inputDoc->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not grayscale"));
        return KisImportExportFilter::WrongFormat;
    }

    KoDialog* kdb = new KoDialog(0);
    kdb->setWindowTitle(i18n("HeightMap Export Options"));
    kdb->setButtons(KoDialog::Ok | KoDialog::Cancel);

    Ui::WdgOptionsHeightMap optionsHeightMap;

    QWidget* wdg = new QWidget(kdb);
    optionsHeightMap.setupUi(wdg);

    kdb->setMainWidget(wdg);
    QApplication::restoreOverrideCursor();

    QString filterConfig = KisConfig().exportConfiguration("HeightMap");
    KisPropertiesConfiguration cfg;
    cfg.fromXML(filterConfig);

    optionsHeightMap.intSize->setValue(image->width());

    int endianness = cfg.getInt("endianness", 0);
    QDataStream::ByteOrder bo = QDataStream::LittleEndian;
    optionsHeightMap.radioPC->setChecked(true);

    if (endianness == 0) {
        bo = QDataStream::BigEndian;
        optionsHeightMap.radioMac->setChecked(true);
    }

    if (!getBatchMode()) {
        if (kdb->exec() == QDialog::Rejected) {
            return KisImportExportFilter::UserCancelled;
        }
    }

    if (optionsHeightMap.radioMac->isChecked()) {
        cfg.setProperty("endianness", 0);
        bo = QDataStream::BigEndian;
    }
    else {
        cfg.setProperty("endianness", 1);
        bo = QDataStream::LittleEndian;
    }
    KisConfig().setExportConfiguration("HeightMap", cfg);

    bool downscale = false;
    if (to == "image/x-r8" && image->colorSpace()->colorDepthId() == Integer16BitsColorDepthID) {

        downscale = (QMessageBox::question(0,
                                           i18nc("@title:window", "Downscale Image"),
                                           i18n("You specified the .r8 extension for a 16 bit/channel image. Do you want to save as 8 bit? Your image data will not be changed."),
                                           QMessageBox::Yes | QMessageBox::No)
                          == QMessageBox::Yes);
    }


    // the image must be locked at the higher levels
    KIS_SAFE_ASSERT_RECOVER_NOOP(image->locked());
    KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());

    QFile f(filename);
    f.open(QIODevice::WriteOnly);
    QDataStream s(&f);
    s.setByteOrder(bo);

    KisRandomConstAccessorSP it = pd->createRandomConstAccessorNG(0, 0);
    bool r16 = ((image->colorSpace()->colorDepthId() == Integer16BitsColorDepthID) && !downscale);
    for (int i = 0; i < image->height(); ++i) {
        for (int j = 0; j < image->width(); ++j) {
            it->moveTo(i, j);
            if (r16) {
                s << KoGrayU16Traits::gray(const_cast<quint8*>(it->rawDataConst()));
            }
            else {
                s << KoGrayU8Traits::gray(const_cast<quint8*>(it->rawDataConst()));
            }
        }
    }

    f.close();
    return KisImportExportFilter::OK;
}
void KisSmudgeRadiusOption::apply(KisPainter& painter, const KisPaintInformation& info, qreal scale, qreal posx, qreal posy,KisPaintDeviceSP dev) const
{
    double sliderValue = computeValue(info);
    int smudgeRadius = ((sliderValue * scale)*0.5)/100.0;//scale is diameter?


    KoColor color = painter.paintColor();
    if (smudgeRadius == 1) {
        dev->pixel(posx, posy, &color);
        painter.setPaintColor(color);
    } else {

        const KoColorSpace* cs = dev->colorSpace();
        int pixelSize = cs->pixelSize();

        quint8* data = new quint8[pixelSize];
        static quint8** pixels = new quint8*[2];
        qint16* weights = new qint16[2];

        pixels[1] = new quint8[pixelSize];
        pixels[0] = new quint8[pixelSize];

        int loop_increment = 1;
        if(smudgeRadius >= 8)
        {
            loop_increment = (2*smudgeRadius)/16;
        }
        int i = 0;
        int k = 0;
        int j = 0;
        KisRandomConstAccessorSP accessor = dev->createRandomConstAccessorNG(0, 0);
        KisCrossDeviceColorPickerInt colorPicker(painter.device(), color);
        colorPicker.pickColor(posx, posy, color.data());

        for (int y = 0; y <= smudgeRadius; y = y + loop_increment) {
            for (int x = 0; x <= smudgeRadius; x = x + loop_increment) {

                for(j = 0;j < 2;j++)
                {
                    if(j == 1)
                    {
                        y = y*(-1);
                    }
                    for(k = 0;k < 2;k++)
                    {
                        if(k == 1)
                        {
                            x = x*(-1);
                        }
                        accessor->moveTo(posx + x, posy + y);
                        memcpy(pixels[1], accessor->rawDataConst(), pixelSize);
                        if(i == 0)
                        {
                            memcpy(pixels[0],accessor->rawDataConst(),pixelSize);
                        }
                        if (x == 0 && y == 0) {
                            // Because the sum of the weights must be 255,
                            // we cheat a bit, and weigh the center pixel differently in order
                            // to sum to 255 in total
                            // It's -(counts -1), because we'll add the center one implicitly
                            // through that calculation
                            weights[1] = (255 - ((i + 1) * (255 /(i+2) )) );
                        } else {
                            weights[1] = 255 /(i+2);
                        }


                        i++;
                        if (i>smudgeRadius){i=0;}
                        weights[0] = 255 - weights[1];
                        const quint8** cpixels = const_cast<const quint8**>(pixels);
                        cs->mixColorsOp()->mixColors(cpixels, weights,2, data);
                        memcpy(pixels[0],data,pixelSize);


                    }
                    x = x*(-1);
                }
                y = y*(-1);
            }

        }

        KoColor color = KoColor(pixels[0],cs);
        painter.setPaintColor(color);

        for (int l = 0; l < 2; l++){
            delete[] pixels[l];
        }
        // delete[] pixels;
        delete[] data;
    }


}