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()); }
// 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; }
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; }
SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) { return device->createRandomConstAccessorNG(0, 0); }
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); }
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; } }
KisRandomSubAccessor::KisRandomSubAccessor(KisPaintDeviceSP device) : m_device(device) , m_currentPoint(0, 0) , m_randomAccessor(device->createRandomConstAccessorNG(0, 0)) { }